You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DDD领域对象结构、角色及设计相关问题咨询

DDD领域对象常见疑问解答

1. 聚合根(Aggregate Root)相关

1.1 客户端操作实体/值对象是否必须经过聚合根?

答:是。

补充说明:聚合的核心作用是保证业务规则的一致性,聚合根作为边界守护者,所有聚合内部的对象创建、修改都必须由聚合根暴露的公共方法触发,禁止客户端直接操作聚合内部的非根实体或值对象,避免出现违反领域规则的无效状态。

1.2 聚合根是否可以仅包含值对象?

答:完全可以,不属于不良设计,聚合根也没有「必须仅包含实体」的要求。

补充说明:聚合根的核心是作为聚合的唯一入口、维护聚合内的业务规则,和它内部持有的是实体还是值对象没有关系。比如你提到的User作为聚合根,仅持有Address、PhoneNumber、Email这些值对象的场景非常常见,只要这些对象的业务规则都由User聚合根统一管控,就是完全合理的设计。

2. 实体(Entity)相关

实体是否可以包含其他实体?

答:可以,实体既可以包含值对象,也可以包含其他实体。
简单示例参考(电商场景):

// 订单实体(同时是聚合根)
public class Order {
    private OrderId orderId; // 实体唯一标识,值对象
    private List<OrderItem> items; // 持有其他实体
    private Address deliverAddress; // 持有值对象

    // 聚合根统一管控订单项的增改逻辑,外部不能直接操作items集合
    public void addItem(Product product, int buyCount) {
        // 前置校验逻辑:比如商品是否可售、购买数量是否在合法区间等
        items.add(new OrderItem(OrderItemId.next(), product.getBaseInfo(), buyCount));
    }
}

// 订单项实体
public class OrderItem {
    private OrderItemId itemId; // 实体唯一标识,值对象
    private ProductInfo productInfo; // 值对象
    private Count buyCount; // 值对象
}

3. 值对象(Value Object)相关

是否需要把所有基本类型都封装成值对象?如何界定封装边界?

答:不需要把所有基本类型都封装为值对象,不存在绝对的封装边界,判断标准是该属性是否有独立的业务规则,或者是否会在多个场景下复用:

  • 如果某个属性有专属的校验规则、业务行为,建议封装为值对象。比如PhoneNumber需要校验号码格式、拆分国家码和本地号码、做脱敏展示,Email需要校验格式、提取域名,这类场景下封装值对象可以把相关规则内聚,避免重复编写校验逻辑。
  • 如果只是没有特殊业务规则的通用字段,比如用户备注、纯描述性文本,直接用String/Integer等基本类型即可。
    过度封装确实不合理:如果一个属性没有任何独立业务规则,只需要简单存储读取,强行封装为值对象只会增加不必要的代码量,降低开发效率。

4. 工厂(Factory)相关

是否必须使用工厂?领域对象内部写静态构造方法是否错误?

答:没有要求必须使用独立的工厂类,领域对象内部编写静态构造方法是完全合理的做法,不属于错误。

  • 如果领域对象的构造逻辑比较简单,只有一两种构造场景,直接在类内部写静态方法(比如User.create()、PhoneNumber.of())就足够,符合高内聚的设计原则。
  • 只有当构造逻辑非常复杂,比如需要组合多个子对象、做大量前置校验,或者同一种类型有非常多不同的构造场景,代码量太大不适合放在领域类内部的时候,才需要抽出来做独立的工厂类或者工厂方法。

内容的提问来源于stack exchange,提问作者Alphas Supremum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 14:06:03