我正在构建微服务。我的MicroService之一正在使用CQRS和事件来源。集成事件在系统中引发,我将聚合保存在事件存储中,同时还更新了我的读取模型。

我的问题是,为什么要针对聚合聚合更新事件流,为什么需要聚合聚合版本?我读到我们需要这样做以保持一致性,并且事件要按顺序重播,并且我们需要在保存(https://blog.leifbattermann.de/2017/04/21/12-things-you-should-know-about-event-sourcing/)之前检查版本。我仍然无法解决这个问题,因为事件是按顺序引发和保存的,所以我真的需要具体以了解我们从版本中获得什么好处以及为什么我们甚至需要它们的示例。

非常感谢,

伊姆兰

最佳答案

让我描述一个汇总版本有用的情况:
在我们的reSove framework中,聚合版本用于乐观并发控制。
我将通过示例进行解释。假设InventoryItem集合接受命令AddItemsOrderItemsAddItems增加库存的项目数,OrderItems-减少。
假设您有一个事件的InventoryItem聚合#123-数量为5的ITEMS_ADDED。聚合#123状态说库存中有5件商品。
因此,您的用户界面向用户显示有5件库存。用户A决定订购3件商品,用户B-4件商品。两者都几乎同时发出OrderItems命令,比方说用户A首先是几毫秒。
现在,如果您在内存中具有聚合#123的单个实例,则在单线程中没有问题-来自用户A的第一个命令将成功,将应用事件,状态说数量为2,因此第二个命令来自用户B的操作将失败。
在一个分布式或无服务器的系统中,来自A和B的命令将在单独的进程中,如果我们不使用某些并发控制,则这两个命令将成功执行,并将聚合置于错误的状态。有几种方法可以做到这一点-悲观锁定,命令队列,聚合存储库或乐观锁定。
乐观锁定似乎是最简单,最实用的解决方案:
我们说每个聚合都有一个版本-流中的事件数。因此,我们的总和#123的版本为1。
当聚合发出事件时,此事件数据具有聚合版本。在我们的案例中,来自用户A和B的ITEMS_ORDERED事件的事件聚合版本为2。显然,聚合事件的版本应按顺序增加。因此,我们需要做的就是放一个数据库约束,使元组{aggregateId, aggregateVersion}在写入事件存储时应该是唯一的。
让我们看看我们的示例如何在具有乐观并发控制的分布式系统中工作:

  • 用户A为聚合#123发出命令OrderItem
  • 从事件{version 1, quantity 5}恢复
  • 聚合#123
  • 用户B为聚合#123发出命令OrderItem
  • 从事件(版本1,数量5)恢复了聚合#123的另一个实例
  • 用户A的聚合实例执行命令,成功后,将ITEMS_ORDERED {aggregateId 123, version 2}事件写入事件存储。
  • 用户B的聚合实例执行命令,成功,事件ITEMS_ORDERED {aggregateId 123, version 2}尝试将其写入事件存储,并因并发异常而失败。
  • 在针对用户B的此类异常命令处理程序上,只需重复整个过程-聚合#123将处于{version 2, quantity 2}状态,并且命令将正确执行。

  • 我希望这可以清除聚合版本有用的情况。

    10-01 18:58
    查看更多