0

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

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

有时我的事件有依赖关系(例如,必须先将 OrderReceived 事件处理到 ReadModel 中,然后才能将 OrderShipmentStatusUpdated 事件正确处理到 ReadModel 中)。

我该如何处理这种情况?使用传奇?如何在如上所述的简单内存模型中使用 Sagas?“推迟”一个事件是否被认为是可以接受的(也许将其标记为推迟并简单地尝试“偶尔”重新处理所有推迟的事件)?

有哪些应对策略?

谢谢你。

4

5 回答 5

3

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

如果这是预期的工作流程并且您实际上必须等待 - 如果不使用 DDD/事件溯源建模,请创建 Saga。如果使用 DDD/事件溯源建模 - 聚合将在大多数情况下涵盖此类功能。

于 2013-05-01T15:22:36.207 回答
2

问题是为什么事件会乱序到达?

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

如果由于“业务流程”而导致消息/事件不同步,那么您可以查看 Sagas/ProcessManager。看看 Greg Young 将事件存储用作 queue的方法。

要解决有关延迟事件的问题:您可以考虑使用简化的方法,通过使用聚合对所需的排序进行建模。只有在满足条件时,它才会消费事件并通知读取方。该视图将仅投射已被聚合“验证”的事件。您可以在模型中明确隐含概念的好处。不利的一面是您将生成更多事件。(是的,没有什么是免费的)

看看Rinat Abdullin在这方面的做法。这是一篇古老但有趣的帖子,也许是指Being The Worst podcast,它也涵盖了其中的一些内容。

于 2013-04-29T08:53:37.800 回答
2

我同意伊利奇。

事件发生了,读取模型应该只报告它。读取模型不应该能够验证事件。

在域模型中包含运输逻辑可能更容易,而不是创建一个新的 saga。

在较小的项目上“作弊”的另一种方法是使用事件作为从事件存储中读取的触发器。因此,当您的读取模型收到事件通知时,它应该从事件存储中加载所有“新”事件。这可确保在运输过程中不会丢失任何东西,并简化您使用巴士的目的。

在原型设计阶段,它还可以让您随意删除已读取的模型并让它们自动重建。

我保留了一张“作业历史”的小表格,它反映了处理特定读取模型所消耗的最新事件。如果我想更改读取模型的架构,我只需删除相应的记录。

于 2013-04-29T14:23:44.083 回答
1

处理 OrderShipmentStatusUpdated 事件最好使用事件存储中的数据而不是读取模型中的数据,这将解决与最终一致性相关的问题。

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

于 2013-04-28T04:19:44.043 回答
1

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

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

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

于 2014-07-08T07:33:12.227 回答