【问题标题】:CQRS Eventual Consistency Event DependenciesCQRS 最终一致性事件依赖
【发布时间】:2013-04-27 02:09:04
【问题描述】:

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

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

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

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

有哪些应对策略?

谢谢。

【问题讨论】:

  • 您有什么理由异步发布事件?如果事件是同步发布的,那么事件的顺序不会有问题,最终的一致性也不会有问题,因为它们将与命令在同一个线程中运行。
  • 性能。除非我在所有 Web 请求中同步并且只有 1 个服务器,否则我仍然会遇到问题,不是吗?
  • @Jeff:我认为 Sarmaad 的意思是 OrderReceived 和 OrderShipmentStatusUpdated 应该同步处理。
  • 您如何建议使用内存中的事件发布者这样做?创建在后台处理的单线程队列?

标签: cqrs event-sourcing eventual-consistency


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

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

    【讨论】:

      【解决方案3】:

      我同意伊利奇的观点。

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

      在域模型中包含运输逻辑可能比创建新的 saga 更容易。

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

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

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

        • 仅使用内存中甚至 tpublisher 时,如何确保同步处理事件?
        • 你是说我的读取模型事件处理程序(反规范化器)应该使用我的 EventSource 作为更新读取模型的一种手段?
        【解决方案5】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-25
          • 1970-01-01
          • 2018-12-20
          • 2018-01-16
          • 2015-06-05
          • 1970-01-01
          相关资源
          最近更新 更多