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

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


一、核心设计原则

  1. Domain层主导业务逻辑

    • Domain层包含聚合根(Aggregate Root)​实体(Entity)​值对象(Value Object)​领域服务(Domain Service)​,是业务逻辑的集中地。
    • Entity层(或基础设施层中的PO)仅负责数据持久化,是贫血模型(仅含属性和getter/setter)。
  2. 充血模型 vs 贫血模型

    • 充血模型 :Entity(或聚合根)不仅包含数据,还封装与其相关的业务行为(如order.cancel())。
    • 贫血模型:Entity仅为数据容器,业务逻辑分散在Service中(不推荐)。
  3. 依赖方向

    • Domain层完全独立,不依赖任何外部框架(如Spring、JPA)。Entity层(PO)通过仓储接口(Repository)​与Domain层交互,由基础设施层实现具体持久化逻辑。

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

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

arduino 复制代码
// 聚合根: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层设计(贫血模型,用于持久化)​

less 复制代码
// 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. ​仓储接口与实现

scss 复制代码
// 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)​或仓储实现转换,避免领域模型污染持久化细节。
  2. 事务边界

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

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

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

      csharp 复制代码
      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的高内聚、低耦合原则。

相关推荐
今天过得怎么样几秒前
彻底搞懂 Spring Boot 中 properties 和 YAML 的区别
后端
qq_12498707533 分钟前
基于springboot的幼儿园家校联动小程序的设计与实现(源码+论文+部署+安装)
java·spring boot·后端·spring·微信小程序·小程序
武子康42 分钟前
大数据-189 Nginx JSON 日志接入 ELK:ZK+Kafka+Elasticsearch 7.3.0+Kibana 实战搭建
大数据·后端·elasticsearch
断春风1 小时前
订单超时自动取消系统架构解析
后端·系统架构
Angletank1 小时前
SpringBoot中ORM组件通过JAP组件的使用
spring boot·后端·orm·jpa
moxiaoran57531 小时前
Go语言的map
开发语言·后端·golang
小信啊啊1 小时前
Go语言数组
开发语言·后端·golang
白宇横流学长1 小时前
基于SpringBoot实现的零食销售商城设计与实现【源码+文档】
java·spring boot·后端
superman超哥1 小时前
仓颉语言中异常捕获机制的深度剖析与工程实践
c语言·开发语言·后端·python·仓颉
驱动探索者1 小时前
[缩略语大全]之[INTEL]篇
java·后端·spring·intel