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