我有一个相当简单的域模型,涉及Facility
聚合根列表。鉴于我正在使用CQRS和事件总线来处理从域引发的事件,您如何处理集合上的验证?例如,假设我有以下要求:
Facility
必须有一个唯一的名称。由于我在查询端使用最终一致的数据库,因此在事件处理或处理事件时,其中的数据不能保证准确。
例如,FacilityCreatedEvent
位于查询数据库事件处理队列中,等待处理并写入数据库。新的CreateFacilityCommand
将发送到要处理的域。域服务查询读取数据库以查看是否已使用该名称注册了任何其他Facility
,但返回false,因为CreateNewFacilityEvent
尚未处理并写入存储。新的CreateFacilityCommand
现在将成功并抛出另一个FacilityCreatedEvent
,当事件处理器尝试将其写入数据库并发现另一个Facility
已存在且具有该名称时,它会爆炸。 / p>
答案 0 :(得分:18)
我使用的解决方案是添加一个System
聚合根,可以维护当前Facility
名称的列表。在创建新的Facility
时,我使用System
聚合(只有一个System
作为全局对象/单例)作为它的工厂。如果给定的设施名称已经存在,那么它将抛出验证错误。
这使得验证约束保持在域内,并且不依赖于最终一致的查询存储。
答案 1 :(得分:4)
Eventual Consistency and Set Validation概述了三种方法:
另请参阅此相关问题:Uniqueness validation when using CQRS and Event sourcing
答案 2 :(得分:1)
在这种情况下,您可以实现一个简单的CRUD样式服务,该服务基本上在具有主键约束的Sql表中进行插入。
插入只会发生一次。当具有相同值的重复命令应该只存在一次聚合时,聚合调用服务,服务由于违反主键约束而失败插入操作,抛出错误,整个过程失败并且没有事件生成,在查询侧没有报告,可能是报告表中的失败以进行最终一致性检查,其中用户可以查询以了解命令处理的状态。要检查这一点,只需使用Command Guid一次又一次地查询命令状态视图模型。
显然,当命令包含主键检查表中不存在的值时,操作成功。
主键约束表应仅用作服务,但是,由于您实现了事件源,您可以重放事件以重建主键约束表。
答案 3 :(得分:0)
由于唯一性检查将在数据写入之前完成,因此更好的方法是构建事件跟踪服务,该服务将在流程完成或终止时发送通知。