领域事件与事件溯源和CQRS的因果关系



假设我们有一个生成两个事件的写模型(域):

  • CarrierAdded(…)
  • BusConnectionCreated(载体,…)

Carrier和BusConnection类是独立聚合的(一部分)。BusConnection被分配给一个Carrier并包含它的CarrierId(单独的聚合仅通过id引用)。

在正常的命令和事件流中,写模型和读模型都很好,但是当我们想从头开始重建/添加新的读模型时,问题就出现了。

许多人建议(例如akka-persistence库)将每个聚合的事件存储在事件存储中。当反规格化器请求回复事件时,他会从每个聚合中获得两个独立的事件流。问题是,来自不同聚合的一些事件(如上面的例子)需要以添加到事件存储的相同顺序进行回复。这意味着我们需要某种因果依赖/偏序。

最后是我的问题:

  • 我应该重新考虑我的设计域(坏的聚合边界?)或
  • 我只需要强制部分排序吗?

如果是后者,最有效的方法是什么?

  • 全球计数器?似乎不可伸缩。
  • 某种矢量时钟?
  • 当反规范化器出现此类问题时检测它们?例如,我们得到了CarrierId,我们还没有这个id的CarrierAdded事件,所以我们把事件藏起来,等待预期的第一个
  • 在重播模式下,在处理事件方面引入一些顺序?例如,首先是所有与运营商相关的事件,然后是与BusConnection相关的事件?

不,在我看来你的设计很好,很普通。你必须以某种方式强制部分排序。

我不熟悉akka-persistence,但是一些事件存储通过记录事件的时间戳并按照时间戳的顺序重播事件来实现部分排序。一些事件存储确实使用全局计数器,例如,当底层数据库是关系型数据库并且系统不需要向外扩展时——数据库提供的序列号在这里工作得很好。

时间戳当然只能保证给定粒度的排序,通常是1ms。在某些情况下,这已经足够了,例如,如果您可以确保CarrierAdded 永远不会在与BusCarrierAdded相同的毫秒内发生。

至于可扩展性,确保横向扩展在您的情况下确实是一个问题,公共汽车运输系统似乎并不"大规模"…如果您可以使用单个主数据库服务器,那么事件流中的事件序列号是实现期望的部分排序的直接而可靠的方法。序列号可能会因为"交织"/并行提交而失败,因此您需要确保CarrierAdded事件总是在BusCarrierAdded之前添加到事件存储/数据库中,但这在您的场景中似乎是可能的。

如果您确实需要横向扩展,并且两个事件可能在同一毫秒内出现,那么您确实必须在处理程序中检测问题并使用反规范化。这听起来比实际困难,因为它可以很容易地通过一个非常简单的状态机实现。Jonathan Oliver在这里写了一篇关于传奇背景下的状态匹配的博文,但是这个原则在这里也适用。

最新更新