Domain driven design 如何处理DDD/CQR中的硬聚合范围约束?

Domain driven design 如何处理DDD/CQR中的硬聚合范围约束?,domain-driven-design,cqrs,event-sourcing,Domain Driven Design,Cqrs,Event Sourcing,我是DDD新手,我正在尝试建立一个基于DDD、CQR和事件源的简单CRM系统的模型并加以实现,以了解该范例。然而,我遇到了一些困难,我不知道如何处理。我不确定我的困难是否源于我没有正确地对域进行建模,或者我缺少了其他东西 P>为我的问题做一个基本的说明,考虑到我的CRM系统有聚合 Cuffer-Copys(这对我来说是合理的)。此聚合的目的是确保每个客户是一致的,并且其不变量有效(需要名称,社会保险号必须采用正确的格式,等等)。到目前为止,一切顺利 但是,当系统收到创建新客户的命令时,需要确保

我是DDD新手,我正在尝试建立一个基于DDD、CQR和事件源的简单CRM系统的模型并加以实现,以了解该范例。然而,我遇到了一些困难,我不知道如何处理。我不确定我的困难是否源于我没有正确地对域进行建模,或者我缺少了其他东西

<> P>为我的问题做一个基本的说明,考虑到我的CRM系统有聚合<代码> Cuffer-Copys<代码>(这对我来说是合理的)。此聚合的目的是确保每个客户是一致的,并且其不变量有效(需要名称,社会保险号必须采用正确的格式,等等)。到目前为止,一切顺利

但是,当系统收到创建新客户的命令时,需要确保新客户的社会保险号码不存在(即,该号码在整个系统中必须是唯一的)。当然,这不是一个可以由
CustomerAggregate
aggregate强制执行的不变量,因为客户没有关于其他客户的任何信息


我看到的一个建议是在自己的聚合中处理此类约束,例如,
SocialSecurityNumberUniqueAggregate
。如果社会保险号码尚未在系统中注册,
SocialSecurityNumberUniqueAggregate
发布一个事件(例如
socialsecuritynumberofnewcustomerwasuniquevent
),该事件由
客户聚合订阅并发布自己的事件以响应此事件(例如,
CustomerCreatedEvent
)。这有意义吗?
CustomerAggregate
在响应
SocialSecurityNumberOfNewCustomerWasUniqueEvent
时,会如何响应缺少的名称或其他硬约束?

您要查找的搜索词是

如果可以将整个集合放入一个数据库中,那么关系数据库在域无关集验证方面非常出色

但是,这是有成本的;以这种方式设计模型会限制您选择哪些类型的数据存储可以用作记录簿,并且会将您的“域逻辑”分为两部分

另一个常见的选择是在运行域逻辑时忽略冲突(毕竟,此约束的业务价值是什么?),而是监视持久化数据,寻找潜在的冲突,并在出现问题时上报给人

您可以将两者结合起来(例如:在运行域逻辑时通过查询检查可能的重复项,并在以后监视结果以减少数据争用)

但是如果您需要在一个集合上维护一个不变量,并且您需要它成为您的写模型的一部分(而不是分离到您的持久性层),那么您需要在进行更改时锁定整个集合

这可能意味着有一个“SSN分配注册表”这本身就是一个聚合,您必须开始考虑有多少其他客户数据需要成为该聚合的一部分,而有多少数据存在于通过公共标识符可访问的不同聚合中,以及通过不同锁控制数据集时可能出现的所有复杂情况

没有任何规则规定所有客户数据都必须属于一个“聚合”;参见Mauro Serventi的谈话。权衡比比皆是


在建模过程中,您需要非常小心的一件事是将数据输入验证与域逻辑混淆的风险。除非您为社会保障管理局编写域模型,否则SSN分配不在您的控制之下。您的模型具有缓存副本,在这种情况下可能是损坏的副本

例如,考虑一个声称:

000-00-0000 is assigned to Alice
000-00-0000 is assigned to Bob
显然存在冲突:如果社会保障管理局维持唯一的分配,这两种说法不可能都是正确的。但在其他条件相同的情况下,你无法判断哪一种说法是正确的。特别是,有人建议“你碰巧先写下的说法必须是正确的”没有太多的逻辑支持

在这种情况下,推迟自动判断通常是有意义的,相反,将问题交给人来处理


虽然它们在许多方面机械上相似,但“我们的标识符分配集应无冲突”和“已知第三方标识符分配集应无冲突”之间存在重要差异。您是否还需要验证社会保险号(SSN)是真的有效?还是您只是想验证在您的CRM系统中不能创建具有相同SSN的其他客户聚合

如果是后者,我建议使用CustomerService,通过查找数据库(例如通过存储库)执行整个SSN检查,然后创建新的客户聚合(再次检查您已经提到的自身不变量)这整个过程-现有SSN和客户创建的查找需要在一个事务<强>中发生<强>以确保一致性。当我考虑这个域逻辑时,域服务是它的最佳位置。它本身不保存数据,而是编排与业务需求相关的工作流。必须在我们的CRM中创建具有相同SSN的客户

如果您还需要验证社会安全号码是否真实,您还需要执行一些呼叫另一个服务,我猜,或者在您的CRM中保留一些SSN的缓存数据。在这种情况下,您还可以拥有一些社会安全号码服务域服务,该服务将注入CustomerService。这将只是域层中的一个接口,但