如何在 CQRS 中处理读取模型
How to process Read Model in CQRS
我们想在我们的新设计中实现 cqrs。我们对处理命令处理程序和读取模型有一些疑问。我们了解到,在处理命令时,我们应该对 aggregateId 进行乐观锁定。但是在处理 readModels 时应该考虑什么方法。我们应该锁定整个 readModel 还是锁定 aggregateId,或者在处理读取模型时从不锁定。
case 1. 对整个readmodel进行lock -> 最安全但速度较差
情况 2 - 锁定 aggregateId。这里可能会出现两个问题。如果我们明智地锁定 aggregateId -> 那么如果读取模型服务器重新启动怎么办。它不知道从哪里重新开始。
情况 3 - 从不锁定。在这种方法中,我认为数据可能处于损坏状态。例如,生成了一个订单插入事件,并且经过一些 workflow/saga,订单更新事件也发生了。如果订单更新事件先发生而订单插入事件尚未处理怎么办?
希望我能够解决我的问题。
For eg say an order inserted event is generated and thorugh some workflow/saga, order updated event took place as well. what if order updated event comes first and order inserted event is not yet processed ?
如果您考虑读取模型轮询有序的事件序列,而不是对无序的通知做出反应,那么它们通常更容易推理。
单个读取模型可能依赖于来自多个聚合的事件,因此聚合锁定不太可能是您最普遍的答案。
这也意味着,如果我们进行轮询,我们需要跟踪多个数据流的位置。换句话说,我们的读取模型可能包含元数据,告诉我们使用了每个源的哪个版本。
锁定可能取决于您的后备存储/缓存的性质。但乐观的做法
- 读取当前表示
- 计算新表示
- 比较和交换
同样,通常很容易推理。
如果您不在 Readmodel 中并发处理事件,则不需要锁。当您有一个 Readmodel 实例时就是这种情况,可能在微服务中轮询事件并按顺序处理它们。
如果您有同步读取模型(即与 Writemodel/Aggregate 在同一进程中),那么您很可能需要锁定。
要牢记的重要一点是,Readmodel 很可能与 Writemodel 不同。可能有很多 Writemodel 类型,它们的事件被投射到同一个 Readmodel 中。例如,在电子商务商店中,您可以有一个 ListOfProducts
来投影来自 Vendor
和 Product
聚合的事件。这意味着,当我们谈论 Readmodel 时,我们不能简单地引用 "Aggregate",因为不涉及单个聚合。在电子商务的情况下,当我们说 "the Aggregate" 时,我们可能指的是 Product
聚合或 Vendor
聚合。
但是锁什么?这里要看数据库技术了。您应该锁定最小的 affected 读取实体或可以锁定的集合。在包含产品列表(读取实体,而不是聚合!)的 Readmodel 中,当一个事件只影响一个产品时,您应该只锁定该产品(即 ProductTitleRenamed
)。
如果一个事件影响更多产品,那么您应该锁定整个系列。例如,VendorWasBlocked
影响所有产品(它应该删除该供应商的所有产品)。
您需要锁定具有非幂等副作用的事件,以防 Readmodel 的更新程序在事件处理期间失败,如果您想从它离开的地方 retry/resume。如果事件具有幂等的副作用,那么它可以安全地重试。
为了知道在 Readmodel 失败的情况下从哪里恢复,您可以在 Readmodel 中存储最后处理的事件的序列。在这种情况下,如果实体更新成功,那么最后处理的事件序列也会被保存。如果失败,则您知道该事件未被处理。
我们想在我们的新设计中实现 cqrs。我们对处理命令处理程序和读取模型有一些疑问。我们了解到,在处理命令时,我们应该对 aggregateId 进行乐观锁定。但是在处理 readModels 时应该考虑什么方法。我们应该锁定整个 readModel 还是锁定 aggregateId,或者在处理读取模型时从不锁定。
case 1. 对整个readmodel进行lock -> 最安全但速度较差
情况 2 - 锁定 aggregateId。这里可能会出现两个问题。如果我们明智地锁定 aggregateId -> 那么如果读取模型服务器重新启动怎么办。它不知道从哪里重新开始。
情况 3 - 从不锁定。在这种方法中,我认为数据可能处于损坏状态。例如,生成了一个订单插入事件,并且经过一些 workflow/saga,订单更新事件也发生了。如果订单更新事件先发生而订单插入事件尚未处理怎么办?
希望我能够解决我的问题。
For eg say an order inserted event is generated and thorugh some workflow/saga, order updated event took place as well. what if order updated event comes first and order inserted event is not yet processed ?
如果您考虑读取模型轮询有序的事件序列,而不是对无序的通知做出反应,那么它们通常更容易推理。
单个读取模型可能依赖于来自多个聚合的事件,因此聚合锁定不太可能是您最普遍的答案。
这也意味着,如果我们进行轮询,我们需要跟踪多个数据流的位置。换句话说,我们的读取模型可能包含元数据,告诉我们使用了每个源的哪个版本。
锁定可能取决于您的后备存储/缓存的性质。但乐观的做法
- 读取当前表示
- 计算新表示
- 比较和交换
同样,通常很容易推理。
如果您不在 Readmodel 中并发处理事件,则不需要锁。当您有一个 Readmodel 实例时就是这种情况,可能在微服务中轮询事件并按顺序处理它们。
如果您有同步读取模型(即与 Writemodel/Aggregate 在同一进程中),那么您很可能需要锁定。
要牢记的重要一点是,Readmodel 很可能与 Writemodel 不同。可能有很多 Writemodel 类型,它们的事件被投射到同一个 Readmodel 中。例如,在电子商务商店中,您可以有一个 ListOfProducts
来投影来自 Vendor
和 Product
聚合的事件。这意味着,当我们谈论 Readmodel 时,我们不能简单地引用 "Aggregate",因为不涉及单个聚合。在电子商务的情况下,当我们说 "the Aggregate" 时,我们可能指的是 Product
聚合或 Vendor
聚合。
但是锁什么?这里要看数据库技术了。您应该锁定最小的 affected 读取实体或可以锁定的集合。在包含产品列表(读取实体,而不是聚合!)的 Readmodel 中,当一个事件只影响一个产品时,您应该只锁定该产品(即 ProductTitleRenamed
)。
如果一个事件影响更多产品,那么您应该锁定整个系列。例如,VendorWasBlocked
影响所有产品(它应该删除该供应商的所有产品)。
您需要锁定具有非幂等副作用的事件,以防 Readmodel 的更新程序在事件处理期间失败,如果您想从它离开的地方 retry/resume。如果事件具有幂等的副作用,那么它可以安全地重试。
为了知道在 Readmodel 失败的情况下从哪里恢复,您可以在 Readmodel 中存储最后处理的事件的序列。在这种情况下,如果实体更新成功,那么最后处理的事件序列也会被保存。如果失败,则您知道该事件未被处理。