当然,唯一可能的答案是“视情况而定。”但这并不是特别有用。
查看 Evan 书中聚合的定义:
AGGREGATE 是一组关联对象,我们将其视为
用于数据更改目的的单位...不变量,它们是
每当数据更改时必须维护的一致性规则,将
涉及聚合成员之间的关系。任何规则
spans AGGREGATES 不会一直是最新的
...但是在 AGGREGATE 中应用的不变量将被强制执行
随着每笔交易的完成。
所以“什么对象构成了我的聚合”和“什么是我的聚合根?”的问题。取决于需要在哪些业务交易中强制执行哪些业务不变量?
您不会像在关系数据库中设计表格那样设计聚合。您不关心“现实生活”中实体之间关系的多样性。您正在寻找在影响(改变其数据)这些实体的操作结束时哪些事实(属性、值)必须为真。
看看你的要求。看看你的系统需要支持什么样的行为。你可以用工作做什么?创建它们?启动它们?完成它们?您可以将工作从一家商店转移到另一家商店吗?工作可以在公司之间调动吗?
哪些事实需要保持一致?例如,您是否执行了每个商店的最大工作数量?在“添加工作”结束时,车间中当前的工作数量是否需要与工作的车间分配一致?
由于您只能通过其根与聚合进行交互,因此您需要考虑如何添加新数据的上下文。例如,您可以创建一个没有初始车间分配的工作吗?还是只能通过商店创建?
在事务中更新聚合时,聚合的大小/范围与数据争用的可能性之间也存在折衷。
考虑到所有这些事情,您可能想知道为什么还要打扰聚合?嗯,他们擅长以下几件事:
- 命令的验证和执行速度很快,因为您需要的所有数据都在聚合中自包含
- 它们非常适合基于文档的持久性存储(例如 MongoDB 具有用于聚合对象的嵌套文档),这使得聚合状态的检索简单而高效,并且通过文档级原子更新轻松执行聚合事务边界
- 它们非常易于测试,因为它们可以实现为简单的类(C# 或 Java 中的 POCO/POJO)。因为它们包含您的大部分业务逻辑,这意味着您的整个应用的行为也很容易进行单元测试!
- 他们是专心的;每个聚合都有一个目的,从它们实现的数据和功能中可以清楚地看出它们在您的系统中所做的事情。结合利用您在代码本身中编码的上下文中无处不在的语言,它们是您在代码库中业务行为的最直接表达(比单独的一组数据表更重要)
- 因为它们是特定于用例的,聚合通常可以避免在更通用的解决方案中出现的泄漏抽象
如果您有兴趣阅读更多内容,Vaughn Vernon 在他的 Effective Aggregate Design 帖子中有一个很好的总结,这是他的厚书“实施领域驱动设计”的基础。