我有以下问题。 给出。 CQRS + EventSourcing应用程序。 如何才能改变历史中聚合根的状态?
例如,会计应用程序,会计者想要应用转换但是过去的日期。将存储在Event Store中的事件将具有比最近事件更早的日期,但此事件的序列号将更大。
存储库将通过按序列号排序事件来恢复聚合根的状态。如果我们将拍摄过去日期的快照 - 我们将在没有此事件的情况下聚合根。
我们可以肯定地更改存储库的逻辑以按日期排序事件,但我们使用外部框架进行CQRS,这是不可取的。
这种情况有一些优雅的解决方案吗?
答案 0 :(得分:9)
您正在寻找的是双时间实现。
e.g。在12月3日,我们认为X == 12(as-at),但是在12月5日我们纠正了错误,现在知道X == 14在12月3日(as-of)
有两种方法可以实现此
1)事件存储保存as-at数据,并且投影保存数据(可能的变化既是as-of,也是as-at投影)
2)聚合有一个重载方法,表明对事件存储中as-of和as-at值的需求。这很可能涉及使用自定义辅助快照流作为数据值。
您的解决方案很可能同时使用这两种实现,因为一个是以命令为中心,另一个是以查询为中心。
在收到纠正事件时,需要重建第二个选项中聚合根的快照。
Martin Folwler在此article
中谈到了这一点注意:事件存储仍然只附加。
答案 1 :(得分:5)
在会计方面,如果您更改过去的预订,您可能会最终入狱。不要改变过去。改为使用补偿命令。
很抱歉,但是你提出了一个会计示例,这可能是一个非常严格的关于摆弄过去的数据但没有明确更改的域名。
如果以上内容不适用于您的域名,您可以轻松地在更改域名对象的状态(可能还有历史记录)的旧事件之上应用新事件。
例如,以帐户预订。该事件可能在今天发生,但它可以将实际预订日期设置为过去的某个时间。
答案 2 :(得分:0)
您已声明您的业务逻辑允许您添加过期交易;现在我不知道你为什么要这样,但没有什么限制你的集合不接受它。当然,事件将获得更晚的事件序列号/版本,但这是预期的。
您无需使用基础架构,存储库或其他任何操作来执行此操作。
答案 3 :(得分:0)
会计不允许您更改历史记录。它只允许您添加条目。您可以根据自己的业务逻辑来解释这些事件的日期。在这种情况下,事件序列不仅仅是事件源的持久性技巧,而是域的实际内容!
答案 4 :(得分:0)
对此的一个解决方案是将事件视为明确的补偿操作。例如,当您的银行撤销费用时,他们不会删除现有交易,而是会添加补偿交易。该交易可以参考它们希望通过相应约会来补偿的交易。通过这种方式,事件是对现实的恰当表现。