使用 CQRS 和事件溯源加载数据以验证业务规则

Loading data to validate a business rule with CQRS and Event Sourcing

我有一个名为 CreateItem 的命令,其中包含要创建的项目(聚合根)的名称。现在我有一个业务规则,规定所有项目必须唯一命名。

如果我正在使用事件源,则无法从写入模型中查找它,因此我需要有一些我认为称为投影的东西,它将是一个 table,其中包含每个 "Item" 的名称,这些名称将根据 ItemCreated 事件保持最新。

现在可以让处理 CreateItem 命令的代码检查这个 table(应该通过查询来完成吗?)。有了最终一致性,这些数据完全有可能过时,那么我应该如何处理呢?

Now I have a business rule that says all items must be uniquely named

您遇到了一个问题 space,称为集合验证。这意味着您需要先阅读 Greg Young 的这篇文章,然后再做任何事情。

With eventual consistency it's perfectly possible for this data to be out of date, so how should I handle that?

我只知道三个答案

接受最终一致性也就是改变需求。 (这是 "aggregate root" 的来源)的部分承诺是,您不仅可以使模型与业务保持一致,而且还可以制定将实际需求与想象需求分开的策略,并且探索替代实施 -- 不仅是模型,还有业务本身。

对于唯一性约束 - 如果聚合真诚地努力检查唯一性,并且您有一个允许您报告重复(基于读取事件流)的流程,以及合理的缓解策略确实出现重复,模型拒绝所有重复名称真的那么重要吗?

您还需要清楚您的模型负责所讨论的 属性。您的模型 决定 名字是什么,还是只是被告知其他人做出了决定?在后一种情况下,您谈论的是事件,而不是命令,并且您需要不同的策略(流程管理器,也称为下游事件处理器)。

移动业务规则 关系数据库在集合验证方面非常好。如果您的事件存储恰好是一个关系数据库,那么您将用户名写入用户名 table,并在保留事件的同一事务中使用列约束。领域知识已经从您的模型中泄漏出来,这有一些负面影响——您失去了将事件历史联合到多个数据库中的能力。

理论上,关系数据库可以与事件存储分开,通过两阶段提交协调两者。我还没有找到任何当局描述他们乐于拥有该替代方案的情况。

更改聚合边界 如果您需要集合中的事务一致性,则集合是域模型的一部分,需要在某些聚合边界内表示 - - 对于您的示例,也许是 "ItemCatalog"。与领域专家一起探索无处不在的语言可能有助于发现负责此约束的新聚合。

如果 Item 行为与名称紧密耦合(有一条业务规则规定当名称 current 以元音字母开头时您只能对项目使用 DoubleThePrice?),那么 Item 实体本身将必须成为相同的聚合,您将需要接受对不同上下文的并发编辑中的争用。

同样值得牢记的是,您是否在正确的模型(即正确的限界上下文)中实施业务约束。例如,市场部可能非常关心项目名称的唯一性,而仓库部并不关心。这表明您错过了无处不在的语言的变化。请记住,如果这个概念对业务的不同部分意味着不同的东西,那么应该有不同的 模型 .