Domain driven design DDD使用两个聚合中的不变量修改每个事务的一个聚合

Domain driven design DDD使用两个聚合中的不变量修改每个事务的一个聚合,domain-driven-design,aggregateroot,Domain Driven Design,Aggregateroot,假设我有一个聚合根租户和一个聚合根组织。多个组织可以链接到单个租户租户在其集合中只有组织的Id 假设我在组织聚合中有以下不变量:组织只能有一个特定产品类型的订阅 假设我在租户聚合中有以下不变量:与租户相关的所有组织中必须只存在一个产品类型订阅 我们如何使用每个事务一个聚合规则来强制执行这些不变量? 向组织添加订阅时,我们可以轻松验证第一个不变量,并触发域事件以更新租户(最终一致性),但是如果在租户聚合中违反了该不变量,会发生什么 这是否意味着触发另一个域事件以回滚组织聚合中发生的事情?如果在成功

假设我有一个聚合根租户和一个聚合根组织。多个组织可以链接到单个租户租户在其集合中只有组织的Id

假设我在组织聚合中有以下不变量:组织只能有一个特定产品类型的订阅

假设我在租户聚合中有以下不变量:与租户相关的所有组织中必须只存在一个产品类型订阅

我们如何使用每个事务一个聚合规则来强制执行这些不变量? 向组织添加订阅时,我们可以轻松验证第一个不变量,并触发域事件以更新租户(最终一致性),但是如果在租户聚合中违反了该不变量,会发生什么

这是否意味着触发另一个域事件以回滚组织聚合中发生的事情?如果在成功修改第一个聚合后将响应发送到UI,则看起来很棘手

或者,真正的方法是在启动更新之前使用域服务来验证这两个聚合的不变量吗?如果是这样,我们是将不变量/规则直接放在域服务中,还是将布尔验证方法放在聚合上以保持逻辑在那里

更新
如果违反了一个不变量,UI必须阻止用户在UI中保存,该怎么办?在这种情况下,我们甚至没有尝试更新聚合。

我想到的第一个想法是,组织有一个名为“tenantHasSubscription”的属性,该属性可以用域事件更新。拥有此属性后,可以在组织聚合中强制执行不变量

我们如何使用每个事务一个聚合规则来强制执行这些不变量

有几个不同的答案

一种是放弃“规则”——将自己限制在每笔交易的一个集合并不重要。真正重要的是,中的所有对象都存储在一起,因此事务是一个全有或全无事件

BEGIN TRANSACTION
    UPDATE ORGANIZATION
    UPDATE TENANT
COMMIT
这种设计中的一个挑战是聚合不再描述存储的原子单元——这个组织和这个租户需要存储在同一个碎片中这一事实是隐式的,而不是显式的

另一个是重新设计你的聚合-边界是很难的,通常情况下,我们对边界的第一选择是错误的。Udi Dahan在他的演讲中观察到(作为一个例子)与书名相关的领域行为通常与书价几乎没有关系;它们是两个独立的事物,与一个共同的事物有关,但它们没有共同的规则。因此,它们可以被视为单独骨料的一部分

因此,您可以重新设计组织/租户边界,以便更正确地捕捉它们之间的关系。因此,我们需要正确评估此规则的所有关系都在一个聚合中,因此必须存储在一起

第三种可能性是接受这两个集合彼此独立,“不变量”更像是一个指导原则,而不是一个实际规则。这两个聚合就像协议中的参与者,我们在协议中不仅设计了快乐路径,还设计了失败模式

这些协议的简单形式称为sagas,其中我们可以通过可逆的操作来解决问题。在2015年对此进行了一次广受欢迎的演讲,或者你可以阅读或阅读;加西亚·莫利纳(Garcia Molina)和塞勒姆(Salem)在他们的研究中引入了这个术语


是协调协议的另一个常见术语,在这里你可能有一个比提交/回滚更复杂的状态图。

< P>你想考虑的一件事是在你的域中有一个丢失的概念的可能性。您可能希望探索您的场景是否有可能包含订阅计划概念,该概念本身就是一个聚合,并强制执行您当前试图放入租户/组织聚合中的所有这些规则

当面对这种情况时,我倾向于想“如果没有任何系统来促进这种操作,一个组织会怎么做”。在您的案例中,如果有多个人来自同一租户,每个人负责一个组织。。。他们将如何同步订阅以符合不变量

在这样的练习中,您可能会遇到一些已经探讨过的场景:

  • 进行收集事件(如电话会议)以确保没有进行冗余订阅:这是域服务路径

  • 每个人都进行自己的订阅,并相互通知,最终对多余的订阅收取费用:这就是事件+回滚路径

  • 他们可能会妥协并保留一个共享的分类账,在那里他们可以检查整个公司的订阅情况,而分类账是此类决策的权威:这就是缺少的聚合路径


如果您足够强调这个问题,您可能会找到其他选项。

如果您希望100%确保不违反不变量,那么所有命令
SubscribeToProduct(TenantId,OrganizationId)
都必须由相同的聚合(可能是
租户
)管理,内部具有所有要检查不变量的值。
否则,要执行操作,您将始终必须查询“外部”值(从聚合的角度来看),这将在打开的操作中引入“延迟”