事件溯源、最终一致、CQRS

Event sourcing, Eventually Consistent, CQRS

我目前遇到了一个问题,希望你能在这里帮助我提供一些想法或最佳实践...

假设我有一个使用事件溯源和 CQRS 的应用程序。我有

我的应用程序的当前状态是

我有一个订阅了 Red Pot 服务、Green Pot 服务和设置服务的计算服务。我有一个 View Updater 服务,它另外订阅了 Calculation 服务并根据任何更改更新读取模型。

现在有以下活动:

  1. 绿锅:25
  2. 设置:绿锅

View Updater 服务今天有点忙,更新视图模型有一些延迟。

计算服务处理 Green Pot 事件。它从读取模型(仍设置为红色)中获取设置并且不执行任何操作。

之后计算服务处理设置事件。它从读取读取模型中获取绿色值(仍然是 20)并发送一个新事件(结果:20)

之后,View Updater 处理这两个事件并更新读取模型。

在这种情况下,我的申请不一致 - 甚至最终也不一致。

有没有想过如何处理这样的事情?感谢您的任何想法:-)

Calculation 服务应订阅并接收所有事件(GreenPotUpdatedRedPotUpdatedSettingsChanged)。

Calculation 服务不能依赖最终一致的读取模型。相反,它应该维护自己的私有状态,确保以正确的顺序接收事件。

首先想到的是,您是否在分享 eventual consistency 的共同理解并不清楚。 Martin Kleppmann 的 talk emphasized three ideas

  • 最终交付
  • 收敛
  • 无数据丢失

第二个想法是您似乎在计算服务设计中引入了竞争条件。如果 RedPot、GreenPot 和 Setting 是分开的 aggregates/streams,那么它们之间确实没有任何时间绑定。来自这些来源的事件的到来本质上是活泼的。

乌迪大汉写道Race Conditions Don't Exist

A microsecond difference in timing shouldn’t make a difference to core business behaviors.

这就是融合发挥作用的地方:您需要设计您的解决方案,以便即使消息的时间不同,它们也能达到相同的结果。这通常意味着您需要在模型中包含一些时钟或时间的概念,以及 defining the interval in which some result is true.

的某种方式

正如您所定义的问题,计算服务产生的结果更多是关于缓存快照而不是维护历史记录。因此,考虑您遇到的问题的另一种方式是考虑计算服务不应接受来自读取模型的任何任意数据,而应接受与它正在消耗的事件一致的数据。

Calculation service: "Hi, I can haz green pot as of event #40?"
Read model: "Sorry, no can haz, retry-after: 10 minutes"
Calculation service: "OK, I'll try again later."