事件Sourcing/CQRS 关于聚合、原子性、并发和最终一致性的疑惑

Event Sourcing/CQRS doubts about aggregates, atomicity, concurrency and eventual consistency

我正在研究事件溯源和command/query隔离,我有一些疑问希望有更多经验的人能够轻松回答:

我制作了一个序列图来更好地说明所有这些问题

(抱歉英语不好)

If my command handler generates more than one event to store, how do you guys push all those events atomically to the event store?

最合理的事件存储实现将允许您将多个事件批处理到同一个事务中。

In many articles I read people suggest using optimistic locking to write the new events generated, but in my use case I will have around 100 requests / second.

如果您有很多并行线程试图维护一个复杂的不变量,那么就出现了严重错误。

对于不希望建立或保持任何不变量的“事件”,您只是将内容写入流的末尾。换句话说,您可能不会尝试将事件写入流中的特定位置。所以你可以使用批处理来减少冲突写入的数量,以及一个简单的重试机制。实际上,您使用的是与并发写入者插入队列时出现的相同类型的“扇入”模式。

对于 establishing/maintaining 不变量的情况,通常不会有很多并发编写器。相反,特定的作者有权编写事件(想想“分片”);那里的并发控制主要是为了避免在异常情况下造成混乱。

How to deal with the fact that the command handler can crash after storing the events in the event store but before publishing them to the event bus?

使用拉取而不是推送作为主要的订阅机制。确保订阅者可以安全地处理重复的消息(又名“幂等”)。当您需要严格排序事件时,不要使用可以重新排序事件的消息订阅。

How you guys deal with the eventual consistency in the projections? you just live with it?

差不多。视图和报告中包含元数据信息,让您知道报告在“时间”的哪个固定点是准确的。

除非您在使用报告时锁定所有编写者,否则任何数据都有可能过时,无论您使用的是事件模型还是其他数据模型,无论您是否使用单一数据模型数据模型或多个。

这都是权衡的一部分;我们接受报告时间和当前时间之间会有更大的window,以换取较低的响应延迟、“不可变”事件历史等


should a command handler work with more than one aggregate?

可能不会 - 这与永远不会是一回事。

通常的框架是这样的:聚合不是域建模模式,就像实体一样。这是一种生命周期模式,用于确保我们一次所做的所有更改都是一致的。

如果您发现您希望命令处理程序同时修改多个域实体,并且这些实体属于不同的聚合,那么您是否真的选择了正确的聚合边界?

有时您可以做的是使用一个命令处理程序来管理多个事务,并在每个事务中更新不同的聚合。但在较长的 运行 中,拥有两个不同的命令处理程序可能更容易,每个处理程序接收命令的副本并独立决定要做什么。