【问题标题】:Should events be stored in order, and how, when using CQRS/event sourcing在使用 CQRS/事件溯源时,事件是否应该按顺序存储,以及如何存储
【发布时间】:2023-04-08 08:57:01
【问题描述】:

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

我认为在每一个事件之后,整个应用程序都应该处于有效状态(否则重放功能是无用的)。这意味着事件应该完全按照它们发生的顺序存储。我也相信所有聚合的事件顺序很重要。例如,用户可以制作产品和订单(产品和订单都是单独的聚合,订单通过 ID 引用产品)。这意味着“创建产品事件”应存储在“将产品添加到事件”之前。否则重播可能会导致引用产品的订单在产品存在之前就存在的无效状态。

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

【问题讨论】:

    标签: c# domain-driven-design cqrs event-sourcing


    【解决方案1】:

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

    另外,请注意您的业务对象应该是一个适当的聚合根,这意味着它最多只有另一个 AR 的 id。一个 AR 不关心另一个,但业务流程会告诉您应该按照什么顺序完成事情,例如:OrderCreated => 订单已持久化 => 创建发票(参考订单)=> 创建发票。

    例如,用户可以制作产品和订单(产品和订单都是单独的集合,订单通过 ID 引用产品)。

    不,它不能。它需要产品信息才能从中创建订单。客户不会将隐形产品添加到他们的购物车中。客户也不创造产品。

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

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

    【讨论】:

    • 产品/订单示例是一个不好的示例。当然,产品总是首先被创造出来。但是当处理多个事件流时,我担心它们坚持的顺序最终可能会出现问题。 NEventStore 似乎是一个无需担心的完美解决方案。谢谢!
    【解决方案2】:

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

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

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

    【讨论】:

    • 感谢您的回答,这更好地解释了这个概念:)。
    • 但这给我们留下了如何提出特定版本号架构的问题。你如何分配版本号?我同意时间戳是不够的,但我认为您也不应该使用全局序列。如果没有,那你怎么做?我已经把它变成了一个自己的问题,但还是想在这里发表评论:stackoverflow.com/questions/60050722/…
    猜你喜欢
    • 1970-01-01
    • 2020-05-18
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    • 2015-09-24
    • 1970-01-01
    相关资源
    最近更新 更多