如何在CQRS中处理基于集合的一致性验证?

JD *_*toy 28 validation domain-driven-design eventual-consistency cqrs

我有一个相当简单的域模型,涉及Facility聚合根列表.鉴于我正在使用CQRS和事件总线来处理从域引发的事件,您如何处理集合上的验证?例如,假设我有以下要求:

  1. Facility必须有一个独特的名字.

由于我在查询端使用最终一致的数据库,因此在事件处理或处理事件时,其中的数据不能保证准确.

例如,a FacilityCreatedEvent在查询数据库事件处理队列中等待处理并写入数据库.将新的CreateFacilityCommand内容发送到要处理的域.域服务查询读取数据库以查看是否Facility已经注册了具有该名称的任何其他数据库,但返回false,因为CreateNewFacilityEvent尚未处理并写入存储.新的CreateFacilityCommand将成功并抛出另一个FacilityCreatedEvent当事件处理器尝试将其写入数据库并发现另一个Facility已存在该名称时会爆炸.

JD *_*toy 18

我使用的解决方案是添加一个System聚合根,可以维护当前Facility名称的列表.在创建新的时Facility,我使用System聚合(只有一个System作为全局对象/单例)作为它的工厂.如果给定的设施名称已存在,那么它将抛出验证错误.

这使得验证约束保持在域内,并且不依赖于最终一致的查询存储.

  • 好吧,措辞不好。他修改了全局的“系统”聚合,同时创建了一个新的“设施”聚合。如果在单个事务中修改两个聚合(例如,如果您在符合 ACID 的数据库上使用“传统”DDD),则没有问题,但 CQRS“纯粹主义者”会说“聚合代表事务上一致的边界!您限制了可扩展性!”。但如果你不需要高可扩展性(我相信大多数人都不需要)那么上面的解决方案就可以了。 (3认同)
  • 我知道这是一个老问题,但您的方法意味着 CreateFacility 命令处理程序实际上修改了两个聚合,即“全局”系统聚合和新创建的设施聚合。还是我理解错了? (2认同)

Ric*_*aca 6

最终一致性和设置验证中概述了三种方法:

  1. 如果问题很少见或不重要,请通过管理方式处理它,可能是通过向管理员发送通知。
  2. 调度 DuplicateFacilityNameDetected 事件,这可能会启动自动解析过程。
  3. 维护一个知道使用过的设施名称的服务,可能是通过监听域事件并维护一个持久的名称列表。在创建任何新设施之前,请先检查此服务。

另请参阅此相关问题:Uniqueness validation when using CQRS and Event sourcing

  • 如果 (3.) 中的服务侦听域事件,它不会最终保持一致吗?上一个命令可能已完成,但在处理下一个命令之前,服务未接收和处理该事件。 (2认同)