如何在CQRS中处理读取模型

时间:2018-09-12 10:07:15

标签: domain-driven-design cqrs wolkenkit

我们希望在新设计中实现cqrs。我们在处理命令处理程序和读取模型时存在一些疑问。我们了解到,在处理命令时,我们应该对AggregationId进行乐观锁定。但是在处理readModels时应考虑采用哪种方法。我们是应该锁定整个readModel还是aggregateId,还是在处理读取模型时不要锁定。

情况1.在整个readmodel上锁定时->最安全,但速度上不好。

情况2-锁定aggregateId。这里可能会出现两个问题。如果我们明智地使用了lock AggregateId->那么,如果读取模型服务器重新启动该怎么办。它不知道从哪里重新开始。

情况3-永不锁定。用这种方法,我认为数据可能处于错误状态。例如,说生成了订单插入事件,并且某些工作流程/传奇事件也发生了订单更新事件。如果首先出现订单更新事件而尚未处理订单插入事件怎么办?

希望我能够解决我的问题。

2 个答案:

答案 0 :(得分:1)

  

例如,说生成了订单插入事件,并且某些工作流程/传奇事件也发生了订单更新事件。如果首先出现订单更新事件而尚未处理订单插入事件怎么办?

如果您认为读取模型轮询事件的有序序列,而不是对无序通知做出反应,则通常更容易推理。

单个读取模型可能取决于来自多个聚合的事件,因此聚合锁定不太可能是您最普遍的答案。

这也意味着,如果要进行轮询,则需要跟踪多个数据流的位置。换句话说,我们的读取模型可能包含元数据,该元数据可以告诉我们所使用的每个源的版本。

锁定可能取决于后备存储/缓存的性质。但是乐观的方法

  1. 阅读当前的陈述
  2. 计算新的表示形式
  3. 比较并交换
同样,

通常很容易推理。

答案 1 :(得分:1)

如果您没有在Readmodel中同时处理事件,则不需要锁定。当您有一个Readmodel实例(在微服务中可能)会轮询事件并按顺序处理它们时,就是这种情况。

如果您拥有同步的Readmodel(即与Writemodel / Aggregate处于同一过程),则很可能需要锁定。

要记住的重要事情是Readmodel很可能不同于Writemodel。在相同的Readmodel中可能会有很多Writemodel类型的whos事件被投影。例如,在一家电子商务商店中,您可以有一个ListOfProducts来投影VendorProduct聚合中的事件。这意味着,当我们谈论Readmodel时,我们不能简单地引用“聚合”,因为其中没有单个聚合。就电子商务而言,当我们说“汇总”时,我们可能指的是Product汇总或Vendor汇总。

但是要锁定什么?这里取决于数据库技术。您应该锁定可以锁定的最小的受影响的读取实体或集合。在包含产品列表(读取实体,而不是聚合!)的Readmodel中,当仅影响一个产品的事件时,您应该仅锁定该产品(即ProductTitleRenamed)。

如果某个事件影响到更多产品,那么您应该锁定整个收藏集。例如,VendorWasBlocked影响所有产品(应从该供应商处删除所有产品)。

如果要在事件处理期间Readmodel的更新程序失败,则需要锁定具有非幂等副作用的事件,如果要从其离开的位置重试/恢复。如果该事件具有幂等的副作用,则可以安全地重试。

为了知道在发生读取模型失败的情况下从何处恢复,您可以在读取模型内部存储最后处理的事件的顺序。在这种情况下,如果实体更新成功,则还将保存最后处理的事件的序列。如果失败,则说明该事件未处理。