daicy
发布于 2025-09-28 / 6 阅读
0
0

DDD应用层和领域层的区别

在DDD(领域驱动设计)的分层架构中,应用层(Application Layer)领域层(Domain Layer) 是核心层级,二者的定位、职责和关注点有本质区别,但又协同工作以实现业务需求。以下从核心定位、职责、包含组件、设计原则等方面详细对比两者的区别:

一、核心定位:“做什么” vs “怎么做”

  • 领域层:聚焦**“业务本质”,回答“业务是什么”“业务规则是什么”。它是系统的业务内核**,封装了领域的核心概念、业务规则和逻辑,是业务知识的软件化体现,不依赖任何技术实现。
  • 应用层:聚焦**“业务流程”,回答“如何用领域层的能力完成用户需求”。它是领域层的“使用者和协调者”**,不包含核心业务逻辑,仅负责组织和调度领域层的对象,完成跨领域的业务流程。

二、核心职责:业务逻辑 vs 流程编排

1. 领域层的核心职责

领域层是业务逻辑的“栖息地”,负责实现所有核心业务规则,解决“业务本身的问题”。具体包括:

  • 封装领域概念(如“订单”“商品”“支付”),通过实体(Entity)值对象(Value Object) 表达业务对象;
  • 实现领域内的业务规则(如“订单金额=商品总价+运费-优惠券”“订单状态只能从‘待支付’流转到‘已支付’或‘已取消’”);
  • 处理领域内的复杂计算(如“折扣计算”“税费计算”);
  • 维护领域对象之间的关系(如“订单包含多个订单项”“用户拥有多个地址”);
  • 通过领域服务(Domain Service) 处理跨实体/跨值对象的领域逻辑(如“订单支付时需同时扣减库存和增加积分”)。

一句话总结:领域层“知道业务该如何运作”,是系统的“业务大脑”。

2. 应用层的核心职责

应用层不包含核心业务逻辑,而是协调领域层的能力完成用户的具体需求,解决“如何组织业务步骤”的问题。具体包括:

  • 接收接口层传递的用户请求(如“创建订单”“取消支付”);
  • 按业务流程调度领域层的对象(实体、领域服务)和基础设施层的技术服务(如仓储、缓存);
  • 管理业务流程的事务边界(如“创建订单时,需保证‘扣减库存’和‘保存订单’要么同时成功,要么同时失败”);
  • 处理跨限界上下文的通信(如“订单支付成功后,通知物流上下文创建物流单”);
  • 将领域层的处理结果转换为应用层DTO(数据传输对象),返回给接口层。

一句话总结:应用层“知道如何调用业务能力完成任务”,是系统的“业务流程指挥官”。

三、包含的核心组件:业务对象 vs 流程服务

1. 领域层的核心组件

领域层的组件直接映射业务概念,是“业务知识的载体”:

  • 实体(Entity):有唯一标识、状态可变的业务对象(如OrderProduct),包含自身的业务行为(如Order.pay()处理支付逻辑);
  • 值对象(Value Object):无唯一标识、不可变的业务对象(如MoneyAddress),用于描述实体的属性;
  • 聚合(Aggregate):由实体和值对象组成的业务单元(如OrderAggregate包含Order实体和OrderItem值对象),确保数据一致性;
  • 聚合根(Aggregate Root):聚合的入口点(如Order是订单聚合的根),控制外部对聚合内对象的访问;
  • 领域服务(Domain Service):无状态的服务,封装跨实体/跨聚合的领域逻辑(如OrderPaymentService协调订单和库存的交互);
  • 领域事件(Domain Event):记录领域内发生的重要事件(如OrderPaidEvent),用于领域内或跨上下文的通知;
  • 仓储接口(Repository Interface):定义领域对象的持久化抽象(如OrderRepository),不包含具体实现。

2. 应用层的核心组件

应用层的组件聚焦流程编排,是“业务能力的组织者”:

  • 应用服务(Application Service):核心组件,每个服务对应一个业务用例(如OrderApplicationService包含createOrder()cancelOrder()方法),负责调用领域层和基础设施层完成流程;
  • 命令/查询对象(Command/Query):封装应用层的输入参数(如CreateOrderCommand包含创建订单所需的用户ID、商品列表等),与接口层的Request对象对应;
  • DTO(Data Transfer Object):封装应用层的输出结果(如OrderDTO包含订单ID、状态、金额等),用于向接口层传递数据,与领域对象解耦;
  • 应用事件(Application Event):记录应用层的流程事件(如OrderCreatedEvent),用于应用层内部或与外部系统的通信(区别于领域事件,不包含核心业务含义)。

四、代码示例:同一业务场景的分层实现

以“创建订单”业务为例,对比应用层和领域层的代码实现差异:

1. 领域层代码(核心业务逻辑)

// 订单实体(领域层):包含订单状态流转和金额计算的核心逻辑
public class Order extends Entity {
    private OrderId id;
    private UserId userId;
    private List<OrderItem> items; // 订单项(值对象)
    private Money totalAmount; // 总金额(值对象)
    private OrderStatus status; // 订单状态(枚举,领域概念)

    // 领域行为:创建订单(封装“订单初始化”的业务规则)
    public Order(UserId userId, List<OrderItem> items) {
        this.id = new OrderId(UUID.randomUUID().toString());
        this.userId = userId;
        this.items = items;
        this.totalAmount = calculateTotalAmount(items); // 计算总金额(核心业务逻辑)
        this.status = OrderStatus.PENDING_PAYMENT; // 初始状态为“待支付”(业务规则)
    }

    // 领域行为:计算订单总金额(核心业务逻辑)
    private Money calculateTotalAmount(List<OrderItem> items) {
        BigDecimal sum = BigDecimal.ZERO;
        for (OrderItem item : items) {
            sum = sum.add(item.getPrice().multiply(new BigDecimal(item.getQuantity())));
        }
        return new Money(sum, Currency.CNY); // 人民币结算(业务规则)
    }

    // 其他领域行为:支付、取消等(均包含业务规则)
    public void pay(PaymentInfo paymentInfo) {
        if (this.status != OrderStatus.PENDING_PAYMENT) {
            throw new BusinessException("只有待支付订单可以支付"); // 状态校验(业务规则)
        }
        this.status = OrderStatus.PAID;
        // 发布领域事件(订单支付成功)
        DomainEventPublisher.publish(new OrderPaidEvent(this.id, this.userId));
    }
}

// 库存领域服务(领域层):跨实体的领域逻辑
public class InventoryDomainService {
    // 扣减库存(核心业务逻辑:检查库存是否充足)
    public void deductStock(ProductId productId, int quantity) {
        Inventory inventory = inventoryRepository.getByProductId(productId);
        if (inventory.getRemainingQuantity() < quantity) {
            throw new BusinessException("商品库存不足"); // 库存校验(业务规则)
        }
        inventory.reduce(quantity);
        inventoryRepository.save(inventory);
    }
}

2. 应用层代码(流程编排,无核心业务逻辑)

// 订单应用服务(应用层):组织创建订单的流程
@Service
public class OrderApplicationService {
    // 依赖领域层(仓储接口、领域服务)和基础设施层(事务管理)
    private final OrderRepository orderRepository;
    private final InventoryDomainService inventoryDomainService;
    private final ProductRepository productRepository;

    // 创建订单的业务流程(应用层核心逻辑:流程编排)
    @Transactional // 事务管理(技术关注点,由应用层负责)
    public OrderDTO createOrder(CreateOrderCommand command) {
        // 1. 校验参数(基础校验,非核心业务)
        validateCommand(command);

        // 2. 调用领域层查询商品信息(获取领域对象)
        List<OrderItem> orderItems = new ArrayList<>();
        for (OrderItemCommand itemCmd : command.getItems()) {
            Product product = productRepository.getById(new ProductId(itemCmd.getProductId()));
            // 构建订单项(值对象)
            orderItems.add(new OrderItem(
                product.getId(),
                product.getName(),
                product.getPrice(),
                itemCmd.getQuantity()
            ));
        }

        // 3. 调用领域层扣减库存(跨领域逻辑,由领域服务实现)
        for (OrderItem item : orderItems) {
            inventoryDomainService.deductStock(item.getProductId(), item.getQuantity());
        }

        // 4. 调用领域层创建订单(核心业务逻辑在Order实体的构造方法中)
        Order order = new Order(new UserId(command.getUserId()), orderItems);

        // 5. 调用仓储保存订单(技术操作,由基础设施层实现)
        Order savedOrder = orderRepository.save(order);

        // 6. 转换为DTO返回(数据传输,与接口层对接)
        return OrderAssembler.toDTO(savedOrder);
    }

    // 参数校验(非核心业务逻辑)
    private void validateCommand(CreateOrderCommand command) {
        if (command.getUserId() == null || command.getItems().isEmpty()) {
            throw new IllegalArgumentException("用户ID和商品列表不能为空");
        }
    }
}

五、关键区别总结

对比维度领域层(Domain Layer)应用层(Application Layer)
核心关注点业务规则、领域概念、核心逻辑(“业务是什么”)业务流程、用例实现、跨领域协调(“如何完成业务”)
是否包含业务逻辑包含所有核心业务逻辑(如状态流转、金额计算、规则校验)不包含核心业务逻辑,仅包含流程编排逻辑(如调用顺序、事务控制)
依赖关系仅依赖自身(领域对象),不依赖其他层依赖领域层(调用领域对象)和基础设施层(使用仓储、服务)
稳定性最稳定,仅随业务本质变更而修改(如商业模式变更)较易变,随业务流程变更而修改(如新增步骤、调整顺序)
对外暴露不直接暴露给接口层,仅通过应用层间接访问直接暴露给接口层,是接口层的直接依赖
组件示例实体、值对象、领域服务、聚合根应用服务、Command/Query、DTO

六、通俗理解:以“餐馆”为例

  • 领域层:相当于“厨师团队”和“核心食材”。厨师(领域服务)掌握做菜的核心技艺(业务规则),食材(实体/值对象)有自身的属性和特性(如“牛排需要煎至七分熟”),他们决定了“菜的本质味道”。
  • 应用层:相当于“服务员”。服务员不直接做菜(无核心业务逻辑),但负责协调厨师(调用领域层)、传递顾客需求(处理请求)、安排上菜顺序(流程编排),确保“顾客的用餐流程顺畅”。

两者缺一不可:没有领域层,应用层就没有可调用的“业务能力”;没有应用层,领域层的“业务能力”无法组织成用户需要的“完整服务”。


评论