CQRS最终一致性事件依赖性

时间:2013-04-27 02:09:04

标签: cqrs event-sourcing eventual-consistency

我是CQRS和最终一致性模型的新手,如果这是一个愚蠢的问题,请原谅我。

鉴于我刚刚开始,我在内存中有一个本地CommandBus和EventPublisher。我的事件被持久化到RavenDB数据库以进行重放,但事件被发布并且处理程序在本地调用(不通过NServiceBus等在外部排队)。 EventPublisher确实异步发布事件(la Task.Factory.StartNew)。

有时我的事件有依赖关系(例如,在OrderShipmentStatusUpdated事件可以正确处理到ReadModel之前,必须将OrderReceived事件处理到ReadModel中。)

我该如何处理这种情况?使用Sagas?如何在如上所述的简单内存模型中使用Sagas?是否可以接受“推迟”事件(可能将其标记为延期并且只是偶尔尝试重新处理所有延期事件)?

有什么策略可以解决这个问题?

谢谢。

5 个答案:

答案 0 :(得分:3)

大多数时候重试的队列解决了这些问题。如果您的处理程序/聚合/非规范化程序无法处理消息,因为不满足前置条件 - 请将其失败。然后,您将从队列中处理更多消息,直到此消息再次可见。如果消息失败超过3次 - 将其丢弃到错误队列中以进行进一步分析。

如果这是预期的工作流程而您实际上必须等待 - 如果不使用DDD /事件采购进行建模,请创建Saga。如果使用DDD /事件源进行建模 - 聚合将在大多数时间内涵盖此类功能。

答案 1 :(得分:2)

问题是为什么事件无法到达?

如果是因为“技术力量”,您可以查看您的基础设施,但这通常需要一个完整的消息队列。

如果消息/事件由于“业务流程”而不同步,那么您可以查看Sagas / ProcessManager。看看Greg Young使用event store as queue的方法。

要了解有关推迟事件的问题:您可以考虑使用简化方法,通过使用聚合建模所需的排序。它将消耗事件并仅在满足条件时通知读取端。该视图将仅投影已由聚合“验证”的事件。您可以在模型中显式隐式概念。缺点是您将生成更多事件。 (是的,没有什么是免费的)

在此查看Rinat Abdullin’s approach。这是一篇古老但有趣的帖子,也许可以参考Being The Worst播客,其中也包含了一些内容。

答案 2 :(得分:2)

我同意ILICH。

事件发生了,阅读模型应该只是报告。读取模型不应该用于验证事件。

在域模型中使用运输逻辑可能更容易,而不是创建新的传奇。

另一种在较小项目上“作弊”的方法是使用事件作为触发器来读取事件库。因此,当您的读取模型获得事件通知时,它应该从eventstore加载所有“新”事件。这可确保在传输过程中不会丢失任何内容,并简化您使用总线的过程。

在原型制作阶段,它还可以让您随意吹走您的阅读模型,让它们自动重建。

我保留了一份“工作历史”表,反映了处理特定阅读模型所消耗的最新事件。如果我想更改读模型的模式,我只需删除相应的记录。

答案 3 :(得分:1)

最好使用Events Store中的数据而不是Read Model中的数据来处理OrderShipmentStatusUpdated事件,这将解决与最终一致性相关的问题。

事件存储是唯一的事实来源。

答案 4 :(得分:1)

关于同步/异步事件发布的问题:

通过将具有自己的提交ID的DomainEvents对象的给定EventCommit包装到事件存储中,我们可以异步处理EventCommit个对象,而DomainEventsEventCommit内同步发布。包装发生在我们的UnitOfWork中,当请求对给定聚合的更改时,它会跟踪我们的内存中聚合。

在读取方面,在将事件分派给处理程序之前,commit-id可用于直接在dequeing过程中验证正确的排序。