领域驱动设计中的聚合是什么? - James Hickey


聚合是领域驱动设计DDD中最容易被误解的概念之一,只是一堆实体和值对象吗?还是更多?
什么是聚合?当然,这是领域驱动设计的核心模式……但这只是对象的集合吗?
马丁·福勒(Martin Fowler)解释说:

聚合是数据存储传输的基本元素–您请求加载或保存整个聚合。事务不应跨越聚合边界。
https://www.martinfowler.com/bliki/DDD_Aggregate.html

那些具有DDD经验的人可能会理解这意味着什么以及为什么应用它。但是对于那些开始熟悉聚合的人来说,这样的解释可能仍然过于详细和细微。
让我们开始看看什么不是聚合:

什么不是聚合?
聚合不是下列情况:

  • 只是实体图
  • 仅仅是一个行为丰富的对象
  • 您可以转储到数据库表中的一个实体或实体集合

那它是什么?
人们通常在这里开始谈论一致性边界、事务一致性、最终一致性、聚合边界、不变式、聚合根等。
当学习这些东西时,很自然地抓住所有熟悉的术语或想法。从那里,我们(错误地)形成了关于这一切的想法。让我们尝试使事情简单实用。

气泡
我喜欢使用一个非常简单的想法来帮助人们理解聚合的本质:气泡。
想象一下,您的软件项目不是一个庞大的代码库,而是一系列小气泡。每个气泡都可以独立处理。这意味着,您只需要考虑在任何给定时刻的气泡中发生了什么,而不是整个系统一次就想到。
聚合是相同的。他们是一个个较小泡泡。

用例:团队
想象一下,我们正在为系统构建新功能。此新功能包括项目,团队和团队成员的概念。
每个团队可以有多个与之关联的员工。
工作人员可以是多个团队的一部分。
每个团队可以有多个项目。
那看起来不像实体图吗?主管数据库表的DBA不会对您大喊大叫吗?显然,我们需要有一个组合表,将每个团队与每个团队成员联系起来。
等一下。
让我们考虑一下对象的行为,让我们将它们视为业务对象或概念。这些对象可以做什么?
好吧,不是很清楚吗?您可以创建一个团队。编辑团队。删除团队。
显然,删除团队意味着所有关联的项目也应该层叠并删除。


真正的业务不是那么简单
可是等等。您只是从用户那里发现这是行不通的。您对业务所做的假设是错误的……
有时项目从一个团队转移到另一个团队。在某些情况下,有时还会孤立项目。
我们的模型现在应该是什么样?编写代码时,是否应该将整个对象图加载到内存中?


孤立项目时会发生什么?团队会只引用一个空项目对象吗?

更多使事情复杂化的需求
现在,企业有了新的要求:团队成员的角色可以根据项目而改变。

那么……我们是否只是创建另一个组合表来使每个团队成员与他们所从事的每个项目以及每个项目的角色相匹配?
那就是我们通常要做的。开发人员自然会首先从数据库设计角度考虑系统
注意:是的,这是领域驱动的设计试图避免的一个巨大问题!

随着每个新的新需求,我们的模型变得越来越肿。随着时间的流逝,这也可能会占用我们系统中的大量内存。想象一个项目的团队有300名成员。是的,这些是我们正在谈论的大型项目。我们需要将所有工作人员及其所有数据加载到内存中!
有没有更好的办法?

聚合
聚合就是解决此类问题的方法。
他们帮助:

  • 当他们开始失控时简化我们的模型
  • 隔离复杂的业务规则
  • 将大型对象图加载到内存时处理性能问题
  • 提供灵活性,可以更轻松地应对未来的意外业务需求

这就是聚合的目的。好!但是他们是什么?在这里,我不告诉是什么,只是告诉您这种情况(否则,我们需要开始谈论一致性,事务边界,并发等等!)。
注意,我将原始成员模型分为三个部分吗?
团队Team和项目Project对成员member拥有自己专用的“版本”或“视图”:Team Member和Project Member,这两个相似又不同的Member具有特定的业务规则和行为所需的确切数据。例如,团队Team聚合不需要项目Project聚合的成员member才需要的角色ProjectRole,不属于它的时候为什么要放在那里呢?因此,我们将Member模型划分为两个“分支”:Team Member和Project Member。在这种情况下,就有了两个聚合。
但是,为了支持能够将同一个成员member分配给多个团队Team,我们必须创建一个专门的团队成员授权模型,并将另一个聚合的成员实体链接为类似外键的引用(再次,我们不是在谈论数据库)。

还有更多的要讨论。我们可以围绕使用值对象等对该模型进行更多改进。
我认为这足以帮助您看到聚合不仅仅是简单地创建实体图。
这是关于允许业务领域规则指导您的设计。