在DDD架构中,如何设计Domain层与Entity层的关系?

在DDD(领域驱动设计)架构中,Domain层Entity层的关系是核心业务逻辑与数据模型的结合点,二者的设计直接影响系统的可维护性和扩展性。以下是具体的设计原则和代码示例:


一、核心设计原则

  1. Domain层主导业务逻辑
  2. Domain层包含聚合根(Aggregate Root)实体(Entity)值对象(Value Object)领域服务(Domain Service),是业务逻辑的集中地。
  3. Entity层(或基础设施层中的PO)仅负责数据持久化,是贫血模型(仅含属性和getter/setter)。
  4. 充血模型 vs 贫血模型
  5. 充血模型:Entity(或聚合根)不仅包含数据,还封装与其相关的业务行为(如order.cancel())。
  6. 贫血模型:Entity仅为数据容器,业务逻辑分散在Service中(不推荐)。
  7. 依赖方向
  8. Domain层完全独立,不依赖任何外部框架(如Spring、JPA)。Entity层(PO)通过仓储接口(Repository)与Domain层交互,由基础设施层实现具体持久化逻辑。

二、代码示例:电商订单系统

1. Domain层设计(充血模型)

// 聚合根:Order(包含业务逻辑)
public class Order {
    private String orderId; // 唯一标识
    private List<OrderItem> items; // 值对象集合
    private OrderStatus status;

    // 业务行为:取消订单
    public void cancel() {
        if (status == OrderStatus.SHIPPED) {
            throw new IllegalStateException("已发货订单不可取消");
        }
        this.status = OrderStatus.CANCELLED;
    }

    // 业务行为:添加商品项
    public void addItem(Product product, int quantity) {
        items.add(new OrderItem(product.getId(), product.getPrice(), quantity));
    }

    // 静态工厂方法(可选)
    public static Order create(String orderId) {
        return new Order(orderId, OrderStatus.CREATED);
    }
}

// 值对象:OrderItem(不可变)
public class OrderItem {
    private final String productId;
    private final BigDecimal price;
    private final int quantity;

    public OrderItem(String productId, BigDecimal price, int quantity) {
        this.productId = productId;
        this.price = price;
        this.quantity = quantity;
    }
}

// 领域服务:复杂逻辑(跨聚合)
public class OrderService {
    private final OrderRepository orderRepository;

    public void processPayment(String orderId, Payment payment) {
        Order order = orderRepository.findById(orderId);
        order.markAsPaid(payment.getAmount());
        orderRepository.save(order);
    }
}

2. Entity层设计(贫血模型,用于持久化)

// JPA实体(数据表映射)
@Entity
@Table(name = "orders")
public class OrderEntity {
    @Id
    private String orderId;
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    @OneToMany(cascade = CascadeType.ALL)
    private List<OrderItemEntity> items;
    // 仅含getter/setter
}

// 值对象持久化
@Embeddable
public class OrderItemEntity {
    private String productId;
    private BigDecimal price;
    private int quantity;
}

3. 仓储接口与实现

// Domain层定义接口
public interface OrderRepository {
    Order findById(String orderId);
    void save(Order order);
}

// 基础设施层实现(依赖JPA)
@Repository
public class JpaOrderRepository implements OrderRepository {
    @Autowired
    private OrderJpaRepository jpaRepository; // Spring Data JPA接口

    @Override
    public Order findById(String orderId) {
        OrderEntity entity = jpaRepository.findById(orderId).orElseThrow();
        return convertToDomain(entity);
    }

    // 转换:Entity -> Domain
    private Order convertToDomain(OrderEntity entity) {
        Order order = new Order(entity.getOrderId(), entity.getStatus());
        entity.getItems().forEach(item -> 
            order.addItem(item.getProductId(), item.getPrice(), item.getQuantity()));
        return order;
    }
}

三、关键实践

  1. 转换逻辑

Domain层对象(如Order)与Entity层对象(如OrderEntity)通过装配器(Assembler)或仓储实现转换,避免领域模型污染持久化细节。

  1. 事务边界

事务管理应在应用层(Application Service)完成,领域层仅关注业务规则。例如:

@Service
public class OrderApplicationService {
    @Transactional
    public void cancelOrder(String orderId) {
        Order order = orderRepository.findById(orderId);
        order.cancel(); // 领域逻辑
        orderRepository.save(order);
    }
}
  1. 领域事件

领域行为可触发事件,由应用层发布:

public class Order {
    private List<DomainEvent> events;

    public void cancel() {
        this.status = OrderStatus.CANCELLED;
        events.add(new OrderCancelledEvent(this.orderId));
    }

    public List<DomainEvent> getEvents() { return events; }
}

四、分层架构对比

分层

职责

依赖关系

Domain层

业务逻辑、领域模型

不依赖任何外部框架

Entity层(PO)

数据持久化

依赖JPA/Hibernate等ORM

基础设施层

实现仓储、消息队列等

依赖Domain层定义的接口

通过这种设计,Domain层保持业务纯净,Entity层仅作数据载体,二者通过明确的边界和转换逻辑解耦,符合DDD的高内聚、低耦合原则。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值