什么是聚合根?

What is an Aggregate Root?

不,这不是重复问题。 我有很多关于这个主题的资源,但我仍然觉得我没有完全理解它。

这是我目前掌握的关于什么是聚合和聚合根的信息(来自多个来源,无论是文章、视频等...):

考虑到所有这些,让我们进入我感到困惑的部分:

this 网站上说

The Aggregate Root is the interface to the external world. All interaction with an Aggregate is via the Aggregate Root. As such, an Aggregate Root MUST have a globally unique identifier within the system. Other Entites that are present in the Aggregate but are not Aggregate Roots require only a locally unique identifier, that is, an Id that is unique within the Aggregate.

但是,在 this 示例中,我可以看到聚合根是由一个名为 Transfer 的静态 class 实现的,它充当一个聚合和一个名为 TransferedRegistered 作为 AR。

所以问题是:

  1. 这个函数怎么可能是一个 AR,如果它必须有一个全球唯一的标识符,而没有,原因是它是一个函数。具有全局唯一标识符的是此函数产生的领域事件。
  2. 以下问题 - 聚合根在代码中是什么样子的?是事件吗?是返回的实体吗?它是 Aggregate class 本身的功能吗?
  3. 如果returns函数是AR的领域事件(如前所述,它必须具有全球唯一标识符),那么我们如何与该聚合进行交互?第一篇文章明确指出所有与聚合的交互都是通过 AR,如果 AR 是一个事件,那么我们只能对其做出反应。
  4. 聚合有两个主要工作,这样说对吗:
    • 根据它收到的输入和它知道的规则应用所需的更改
    • Return 需要从 AR and/or 持久化的数据需要在 AR
    • 的领域事件中提出

如果 some/all 有任何错误,请纠正我开头的任何要点,如果我遗漏了任何要点,请随时添加更多!

感谢您澄清问题!

I feel like I don't fully understand it.

那不是你的错。文学烂透了

据我所知,使用领域驱动设计实施解决方案的核心思想来自 Java 大约 2003 年的世界。因此 Evans 在蓝皮书第 5 章和第 6 章中描述的模式被理解为面向对象(在 Java 意义上)领域建模正确。

讨论聚合模式的第 6 章专门讨论了生命周期管理;您如何在域模型中创建新实体,应用程序如何找到与之交互的正确实体,等等。

因此我们有 Factories,它允许您创建域实体的实例,Repositories,它为检索对域实体的引用提供抽象。

但是还有第三个谜题,那就是:当您的域中有一些规则要求域中的两个实体之间进行同步时会发生什么?如果您允许应用程序以不协调的方式与实体对话,那么最终可能会导致数据不一致。

所以聚合模式就是对此的答案;我们将协调的实体组织成图表。关于更改(和存储),实体图成为允许应用程序与之交互的单个单元。

聚合根的概念是应用程序和图之间的接口应该是图的成员之一。因此应用程序与根实体共享信息,然后根实体与聚合的其他成员共享该信息。

聚合根作为聚合的入口点,起到粗粒度锁的作用,确保对聚合成员的所有更改同时发生。

将此视为一种封装形式并非完全错误——对于应用程序而言,聚合看起来像一个单一的实体(根),聚合的其余复杂性从视图中隐藏。

现在,在过去的 15 年中,出现了一些语义漂移;人们试图以更适合他们的问题或更适合他们喜欢的设计的方式调整模式。因此,您必须谨慎设计如何翻译他们正在使用的标签。

简单来说,聚合根 (AR) 是一个拥有自己生命周期的实体。对我来说这是最重要的一点。一个 AR 不能包含另一个 AR,但可以通过 Id 或至少包含所引用 AR 的 Id 的某个值对象 (VO) 来引用它。我倾向于让 AR 只包含其他 VO 而不是实体 (YMMV)。为此,AR 负责一致性和变体 w.r.t。应收账款。每个 VO 都可以有自己的不变量,例如需要有效电子邮件格式的 EMailAddress。即使有人要调用包含的 类 个实体,我也会称之为语义,因为可以用 VO 完成同样的事情。存储库负责 AR 持久性。

您链接到的示例实现不是我会做或推荐的。我关注了一些评论,正如一位评论者提到的那样,我也宁愿使用域服务在两个帐户之间执行类似 Transfer 的操作。转移的注册不一定是允许的,因此,需要域服务来确保转移的有效性。事实上,根据我的经验,从会计角度来说,转移请求的注册可能是 Journal。一旦期刊获得批准,它可能会尝试实际转移。

在我的 DDD 旅程中的某个时刻,我认为一定有问题,因为理解聚合应该不那么困难。有很多意见和解释w.r.t。到 DDD 和聚合,这就是为什么它会变得混乱的原因。另一个方面是,在恕我直言中,涉及相当多的设计需要一些创造力,并且基于对领域本身的理解。创造力无法传授,设计属于隐性知识领域。隐性知识的一个流行例子是学习骑自行车。现在,我们可以阅读所有我们想要的关于如何骑自行车的信息,这可能有帮助,也可能没有帮助。一旦我们骑上自行车并自己学会平衡,我们就能取得进步。然后有些人最终在自行车上做了绝对 疯狂 的事情,即使我阅读了操作方法,我也不认为我会尝试 :)

继续练习和建模,直到它开始变得有意义或直到您对模型感到满意为止。如果我没记错的话,埃里克·埃文斯 (Eric Evans) 在蓝皮书中提到,可能需要进行一些设计才能使模型更接近我们的需求。

请记住,Mike Mogosanu 使用的是事件溯源方法,但在任何情况下(没有 ES)他的方法都非常好,可以避免主流 OOP 语言中不需要的工件。

  1. How can it be that the function is an AR, if there must be a globaly unique identifier to it, and there isn't, reason being that its a function. what does have a globaly unique identifier is the Domain Event that this function produces.

TransferNumber作为自然的唯一ID;还有一个 GUID 可以避免在某些情况下需要完整的值对象。

计算机内存中没有唯一的ID状态,因为它是一个参数但是想想看;为什么你想要一个全球唯一的ID?它只是为了持久性目的(查找、修改或删除它)定位根元素及其(非唯一 ID)子元素。

订单A有2个订单行(1和2)而订单B有4个订单行(1,2,3,4);订单行的唯一标识是其ID和Order ID的组合:A1、B3等。就像关系数据库中的关系模式。

所以你需要那个 ID 只是为了持久化,而持久化的元素是一个表达变化的域事件;保持一致性所需的所有更改,因此如果您使用全局唯一 ID 持久化领域事件以在持久性中找到您必须修改的内容,系统将处于一致状态。

你可以做到

var newTransfer = New Transfer(TransferNumber); //newTransfer is now an AG with a global unique ID
var changes = t.RegisterTransfer(Debit debit, Credit credit)
persistence.applyChanges(changes);

但是如果您不打算对这个对象做不止一件事,那么实例化一个对象以在计算机内存中创建状态有什么意义呢?这是毫无意义的,大多数 OOP 批评者使用这种糟糕的 OOP 设计来批评 OOP 并倾向于函数式编程。

  1. Following question - How does an Aggregate Root looks like in code? is it the event? is it the entity that is returned? is it the function of the Aggregate class itself?

就是函数本身。您可以阅读 post:

AR is a role , and the function is the implementation.

聚合表示单个工作单元,这意味着它必须是一致的。您可以看到该函数如何遵守这一点。它是使系统保持一致状态的单个工作单元。

  1. In the case that the Domain Event that the function returns is the AR (As stated that it has to have that globaly unique identifier), then how can we interact with this Aggregate? the first article clearly stated that all interaction with an Aggregate is by the AR, if the AR is an event, then we can do nothing but react on it.

上面的回答是因为域事件不是 AR。

4 Is it right to say that the aggregate has two main jobs: Apply the needed changes based on the input it received and rules it knows Return the needed data to be persisted from AR and/or need to be raised in a Domain Event from the AR

是;再次,您可以看到静态函数如何支持这一点。

您可以尝试联系 Mike Mogosanu。我相信他能比我更好地解释他的方法。