使用 CQRS/event 采购时,事件是否应按顺序存储以及如何存储

Should events be stored in order, and how, when using CQRS/event sourcing

最近我关注了一个 CQRS 研讨会,这让我想知道在使用事件溯源时事件是如何存储的。

我认为在每个事件之后,整个应用程序都应该处于有效状态(否则重放功能将毫无用处)。这意味着事件应该按照它们发生的顺序存储。我还认为所有聚合的事件顺序很重要。例如,用户可以制作产品和订单(产品和订单都是单独的聚合,订单通过 ID 引用产品)。这意味着 'create product event' 应该存储在 'add product to event' 之前。否则重放可能会导致无效状态,其中引用产品的订单在产品存在之前就已存在。

如何处理这种情况?您是否应该始终使用同步方法将事件发送到数据库,例如通过锁定数据库?该解决方案是否可扩展?还是应该将每个聚合的事件存储在不同的 table 中?但是你如何确保订购呢?另一种选择是存储每个事件的时间,并按时间排序。 PC 上计时器的精度是否足够高以能够做到这一点?

您可以查看 NEventStore 的源代码,它几乎可以满足您的需求。所有事件都按照它们提交的顺序存储,事件存储并不真正关心您的特定业务对象,它只是以原子方式持久保存事件集合。由您的应用程序以正确的顺序生成它们。

此外,请注意您的业务对象应该是一个适当的聚合根,这意味着它最多只有另一个 AR 的 ID。一个 AR 不关心另一个,但业务流程会告诉您应该按什么顺序完成事情,例如:OrderCreated => order has been persisted => Create invoice (referenced the order) => Invoice Created。

For example, a user could make a product and an order (product and order both being separate aggregates, order references product through an ID).

不,不能。在能够从中创建订单之前,它需要产品信息。客户不会将看不见的产品添加到他们的购物车中。客户也不创造产品。

关于这些东西有很多要讨论的,但为了让事情正常工作,你确实需要正确设计的域对象并了解事件源 (ES) 的真正含义(提示:它将对象状态表示为流事件数)。

顺便说一句,当使用 ES 时,您的重点应该是在对象级别正确实现它,您不应该构建自己的事件存储。

我知道这是一个旧线程,但你对此提出质疑是完全正确的。事件存储中事件的持久性与您的问题无关。时间戳是无效的解决方案,用户在逻辑上可以做什么与您的问题无关。

你的问题,如果我错了请纠正我,一旦事件存储通过可扩展的东西(例如消息总线)发布事件 - 你如何保证非规范化器以正确的顺序接收事件。消息总线通常不保证事件的顺序。

答案是你不能,所以我建议的一个选项是为事件提供版本号,并且仅当最后已知状态的版本号与事件中的版本号匹配时才更新预计模型。如果没有,请将事件配置为在等待下一个事件时重试。