聚合根中子实体的目的是什么?

dee*_* zg 5 domain-driven-design

[从这个问题和评论中跟进:实体是否应该有方法,如果有,如何防止它们被外部聚合调用]

正如标题所说:我不清楚作为孩子的实体的实际/确切目的是什么?

根据我在很多地方读到的内容,这些是作为聚合子项的实体的属性:

  1. 它具有本地身份以进行聚合
  2. 它不能直接访问,只能通过聚合根访问
  3. 应该有方法
  4. 它不应该从聚合中暴露出来

在我看来,这转化为几个问题:

  1. 实体应该是私有的聚合
  2. 我们需要一个只读副本 Value-Object 来公开来自实体的信息(例如,至少让存储库能够读取它以保存到数据库)
  3. 我们在实体上拥有的方法在 Aggregate 上重复(或者,反之亦然,我们必须在 Aggregate 上拥有的处理实体的方法在实体上重复)

那么,为什么我们有一个实体而不是只有值对象?只有值对象、聚合和公开值对象的所有方法(我们已经复制实体信息)看起来更方便。

附注。我想将子实体集中在聚合上,而不是实体集合上。


[更新以回应康斯坦丁·加尔贝努的回答和评论]

那么,实际上,你会有这样的事情吗?

public class Aggregate {
    ...
    private _someNestedEntity;

    public SomeNestedEntityImmutableState EntityState {
       get {
          return this._someNestedEntity.getState();
       }
    }

    public ChangeSomethingOnNestedEntity(params) {
       this._someNestedEntity.someCommandMethod(params);
    }
}
Run Code Online (Sandbox Code Playgroud)

Con*_*enu 0

您遇到的这些问题在 CQRS 架构中不存在,其中写入模型(聚合)与读取模型不同。在扁平架构中,聚合必须公开读取/查询方法,否则就没有意义。

  1. 实体应该是私有的才能聚合

是的,这样你就清楚地表达了它们不供外用的事实。

  1. 我们需要一个只读副本值对象来公开来自实体的信息(例如,至少存储库能够读取它以便保存到数据库)

存储库是一种特殊情况,不应以与应用程序/演示代码相同的方式查看。它们可以是同一包/模块的一部分,换句话说,它们应该能够访问嵌套实体。

实体可以被视为/实现为具有不可变 ID 的对象和表示其状态的 Value 对象,如下所示(伪代码):

class SomeNestedEntity
{
    private readonly ID;
    private SomeNestedEntityImmutableState state;

    public getState(){ return state; }
    public someCommandMethod(){ state = state.mutateSomehow(); }
}
Run Code Online (Sandbox Code Playgroud)

所以你看?您可以安全地返回state嵌套实体的 ,因为它是不可变的。德墨忒尔法则会有一些问题,但这是你必须做出的决定;如果你通过返回状态来打破它,你第一次可以使代码更容易编写,但耦合度会增加。

  1. 我们在实体上拥有的方法在聚合上重复(或者反之亦然,我们在聚合上拥有的处理实体的方法在实体上重复)

是的,这保护了聚合的封装,并且还允许聚合保护它的不变量。