什么是聚合根?

Max*_*Max 6 domain-driven-design aggregate aggregateroot

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

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

  • 聚合是多个值对象\实体引用和规则的集合。
  • 聚合始终是一个命令模型(旨在更改业务状态)。
  • 聚合代表单个工作单元(数据库 - 因为本质上更改将被持久化),这意味着它必须保持一致。
  • 聚合根是与外部世界的接口。
  • 聚合根必须在系统内具有全局唯一标识符
  • DDD 建议每个聚合根都有一个存储库
  • 聚合中的简单对象在其 AR(聚合根)不知道的情况下无法更改

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

这个网站上它说

聚合根是与外部世界的接口。与聚合的所有交互都是通过聚合根进行的。因此,聚合根必须在系统内具有全局唯一标识符。存在于聚合中但不是聚合根的其他实体仅需要本地唯一标识符,即聚合内唯一的 Id。

但是,在这个示例中,我可以看到聚合根是由充当Transfer聚合的静态类和名为TransferedRegisteredAR 的内部静态函数实现的。

所以问题是:

  1. 如果该函数必须有一个全局唯一标识符,但实际上没有,原因是它是一个函数,那么该函数怎么可能是 AR?具有全局唯一标识符的是该函数产生的领域事件。
  2. 以下问题 - 聚合根在代码中看起来如何?这是事件吗?是返回的实体吗?是Aggregate类本身的功能吗?
  3. 如果函数返回的领域事件是 AR(如上所述,它必须具有全局唯一标识符),那么我们如何与这个聚合交互?第一篇文章明确指出,与聚合的所有交互都是由 AR 进行的,如果 AR 是一个事件,那么我们除了对其做出反应之外什么也做不了。
  4. 聚合有两个主要作用,这样说是否正确:
    • 根据收到的输入和了解的规则应用所需的更改
    • 返回需要从 AR 中保存的数据和/或需要在 AR 的领域事件中引发的数据

如果某些/全部错误,请在开始时纠正我的任何要点,如果我遗漏了任何要点,请随时添加更多!

感谢您澄清事情!

Voi*_*son 7

我感觉我没有完全理解它。

那不是你的错。文学很糟糕。

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

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

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

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

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

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

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

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

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


jlv*_*ero 2

请记住,Mike Mogosanu 使用的是事件溯源方法,但无论如何(没有 ES),他的方法都可以很好地避免主流 OOP 语言中出现不需要的工件。

  1. 如果该函数必须有一个全局唯一标识符,但实际上没有,原因是它是一个函数,那么该函数怎么可能是 AR?具有全局唯一标识符的是该函数产生的领域事件。

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

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

订单 A 有 2 个订单行(1 和 2),而订单 B 有 4 个订单行(1、2、3、4);订单行的唯一标识符是其ID和订单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);
Run Code Online (Sandbox Code Playgroud)

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

  1. 以下问题 - 聚合根在代码中看起来如何?这是事件吗?是返回的实体吗?是Aggregate类本身的功能吗?

这是函数本身。您可以在帖子中阅读:

AR是角色,功能是实现。

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

  1. 如果函数返回的领域事件是 AR(如上所述,它必须具有全局唯一标识符),那么我们如何与这个聚合交互?第一篇文章明确指出,与聚合的所有交互都是由 AR 进行的,如果 AR 是一个事件,那么我们除了对其做出反应之外什么也做不了。

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

4 聚合有两个主要工作的说法是否正确: 根据收到的输入和它知道的规则应用所需的更改 返回需要从 AR 持久保存的数据和/或需要在领域事件中引发的数据增强现实

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

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