domain-driven-design - DDD:在单个事务中创建具有共享生命周期的多个聚合
问题描述
据我所知,我知道每个事务只应修改一个聚合的一般规则,主要用于并发和事务一致性问题。
我有一个用例,我想在单个事务中创建多个聚合: a RestaurantManager
、 aRestaurant
和 a Menu
。它们看起来像一个单一的聚合体,因为它们的生命周期是一起开始和结束的:在域中创建RestaurantManager
没有 a 的a 是没有意义的Restaurant
,反之亦然;aRestaurant
和 a 也是如此Menu
。此外,如果Restaurant
或RestaurantManager
被删除(未注册),则应将它们全部删除。
但是,我已将它们拆分为单独的聚合,因为一旦创建它们,它们就会单独更新,维护它们自己的不变量,并且我不想将它们全部加载到内存中只是为了更新Restaurant
.
唯一将它们联系在一起的是它们的生命周期。
我的问题是,这是否代表可以违反“规则”的情况,即每个事务只能在单个聚合上运行。
我还想知道我是否应该通过让每个聚合根保存它所依赖的聚合根的标识符来强制它们在域模型中的共享生命周期,即通过将Restaurant
require aMenuId
作为构造函数参数,同样对于Menu
and RestaurantId
,因此没有另一个就不能创建任何一个。但是,这仍然不会强制应用程序服务将它们保存在一起,因为它可以在内存中创建它们,然后只保存Menu
.
解决方案
恕我直言,您的要求是 DDD 中的一个非常正常的用例。总是有多个聚合协同工作以支持应用程序,并且它们在它们的生命周期中相互关联。但建模概念仍然适用。让我尝试借助一些 DDD 规则来解释您的模型的外观:
聚合是事务边界
聚合确保在任何时候都不会破坏业务不变量。这意味着,如果您将多个聚合串在一起作为一个事务的一部分,则必须将它们全部加载到内存中以进行验证。
当您的应用程序数据丰富并将数据存储在数据库集群中时,这尤其是一个问题——分区、分布式(想想 Mongo 或 Elasticsearch)。作为单个事务的一部分,您将遇到从可能不同的集群加载数据的问题。
聚合全部加载
聚合及其关联的数据对象全部加载到内存中。这意味着交易的不必要对象(例如,餐厅下个月的日程安排)可能会被加载到内存中。就其本身而言,这不是问题。但是当多个聚合聚集在一起时,需要考虑加载到内存中的数据量。
聚合通过其唯一标识符相互引用
这很简单,意味着每个聚合通过其标识符存储其引用的聚合,而不是将其他聚合的数据包含在其中。
跨聚合的状态更改通过领域事件处理
如果您希望一个聚合中的状态更改对其他聚合产生副作用,您可以发布一个域事件,并且订阅者在后台处理其他聚合上的更改。这就是您想要处理级联删除要求的方式。
通过遵循这些规则,您实际上是一次放大一个聚合并确保复杂性保持在较低水平。当您将多个聚合串起来时,尽管在第一天就很清楚并且可以理解,但最终,应用程序往往会变成一个大泥球,因为依赖项和不变量开始相互交叉。
推荐阅读
- javascript - 当谷歌浏览器中有很多 websocket 连接时,socket io 会中断
- c# - 使用带有 C# 和 Nunit 的 Selenium Webdriver 选择下拉字段中的选项后,我无法单击该选项
- typescript - 键入将对象中的属性映射到另一个属性的函数
- c# - C# 无法使用 List 将 int[] 转换为 int
- javascript - 服务工作者获取事件:为什么没有无限循环?
- javascript - SyntaxError:JSON.parse 处的 JSON 输入意外结束(
) - 角度和弹簧靴 - html - 更改活动导航链接的颜色
- amazon-web-services - 点击计数器会减慢我的 DynamoDB API 的速度吗?
- charts - 谷歌图表时间线:带有返回线的标签
- javascript - 如何在旋转后重新计算 Canvas 中的图像大小?