与Event Sourcing结合使用时,是否有一种很好的方式来进行CQRS?
我想到的一种方法是在命令处理程序(持久化Actor)中执行此操作,只要将Command转换为事件并持久保存到事件日志(这些事件代表Write模型),我就会发送事件使用事件总线到感兴趣的订阅查询actor,以便他们可以更新他们的Query模型。
我想的另一种方式(假设期刊支持它)是使用持久性查询(通过Akka Streams),如allPersistenceIds
或currentPersistenceIds
,查询方(可能的查询参与者)可以执行此操作定期。
我是在正确的道路上吗?有没有更好的方法呢?
答案 0 :(得分:3)
我认为你首先提到的方法会毫无问题地运作。我不会说第二个。如果我理解正确,您希望将查询投影拉到更新而不是通过事件总线推送。我认为这里的问题是您必须区分已经处理的事件和新事件(更新)。我不确定Akka EventStore期刊是否能解决这个问题,但我对此表示怀疑。
答案 1 :(得分:2)
在进行一些研究并确认在处理命令(上面的第一种方法)期间读取端的更新可能失败的事实,这将涉及处理回滚和事务。更好的方法是使用持久性查询。这很大程度上依赖于您的Event Journal支持持久性查询的不同功能(如AllPersistenceIdsQuery
和EventsByTag
),这些功能目前由Cassandra,Event Store和其他一些期刊支持。
这个想法是将命令端与查询端分离,其中命令端根本不需要知道有任何查询端。这种解耦是使用Persistence Query给我们的。我们的想法是,命令端应该只关注验证传入的命令并将事件持久保存到事件日志中。就是这样,没有意识到查询方面。
现在,对于查询方,您可以使用EventsByTag
或AllPersistenceIdsQuery
等持久性查询从期刊中获取Source[Event]
背压} 来自Event Journal的Event
的实时流,您可以使用此抽象来提供您的Read Sides。您可以找到方法here
让我们考虑失败
如果命令端出现故障怎么办?
没有新的Event
将被持久保存到事件日志中,阅读端持久性查询将很乐意在流中不生成新的Event
。当命令端恢复时,一切都将恢复,现在读取持久性查询将在流中看到新的Event
。
如果其中一个读取面发生故障怎么办?
不是一个大问题,你会擦除读取端数据库并从头开始重新启动该持久性查询。我们可以通过使用Resumable Projections的概念来改善这一点。我们的想法是继续存储您读取的每个Event
的当前偏移量,这样如果您的“读取面”发生故障,我们可以简单地从我们正在阅读的点恢复,而不必从头开始。建议,如果您需要有效 - 一旦交付,那么您可能希望使用幂等过滤器来避免重复。如果这样做,您可以在不需要保留每个ID的情况下进行一些优化,但只是在一定的时间间隔内。
其他一些注意事项
如果您需要引入新的Read Sides并且从一开始就依赖于命令端事件,该怎么办?
这种解耦方法允许您这样做。我们的想法是,您不会从事件存储中删除事件,只需启动另一个持久性查询并让它为新的读取方提供信息,如果您需要保持最新,请不要忘记使用Resumable Projections。
这些方法隐含地暗示了持久性查询以及在同一系统上提供读取侧和可恢复投影的逻辑。
另一种方法是将持久性查询与Akka HTTP结合并公开一个流式端点,该端点公开您的事件日志,并允许您从开始或从某个偏移量获取所有事件/某些事件。这种方式允许你进行一些进一步的解耦,但是如果你使用这种方法,你真的希望有可恢复的预测,因为现在由于引入HTTP而导致失败。现在,您的Read Sides可以使用此流端点,并且可以使用Resumable Projection,并且可以以更加分离的方式引入新的Read Sides。
这些只是我自从开始与Akka合作以来收集的一些知识。也许一些经验丰富的人有更好的CQRS方法。
如果您正在寻找代码示例,我强烈建议您查看Christian Baxter的Mastering Akka,其中更详细地介绍了这种方法以及post。
感谢您阅读