我正在实现一个系统,该系统应该以一致的方式存储所有事件,但是同时,我想通过使用某种混合方法来保持一致性。尽管事件源的想法很明确,但是所有的变种都进入了ES日志,然后消费者创建了物化视图,但令我感到困扰的是,由于系统现在在整个系统范围内都是异步的。
CQRS + ES方法建议应在UI级别解决此问题,要求客户端等待。假设我有一些接近stackoverflow的东西。当我完成此问题后,我将点击“发布您的问题”按钮,该网站将立即带我回答我的问题。使用ES方法,单击“发布您的问题”将意味着我的问题将被推送到ES商店中,以便稍后进行处理,而我会看到“我们正在发布您的问题,请稍等”,对吗?那就是我要避免的。我想出了以下模式:
CreateQuestionCommand command
= new CreateQuestionCommand(uint userId, string questionBody, string[] tags)
QuestionSavedEvent result
= command.execute() // save to DB as usual, return events
saveToES(result) // ugghhhhh...
如果我忘记了saveToES
,则ES日志会变得不一致并且几乎没有用。如果我们团队中的任何开发人员忘记了它,那么同样的事情-整个ES日志只是一堆东西。
这种方法是否可行?这样可以解决ES的缺点,即最终的一致性,并且仍然保留其始终可靠的事件日志的光明面。
我遇到了一些解决方案。
1。
UPDATE users SET loggedIn = "2018-12-30 10:00:00" WHERE id = 1
,在这种情况下,事件仅为UserUpdated
,但在应用程序级别,事件可能会好得多UserLoggedIn
。ebay使用的另一种方法(据说,我已经在一本关于ES的书中读过它,想不起来标题了,这样)
2。
UPDATE domain.users SET loggedIn = "2018-12-30 10:00:00" WHERE id = 1
INSERT INTO events.log SET event = "{name: UserLoggedIn, userId: 1}", timestamp = "2018-12-30 10:00:00"
然后另一个进程可以轮询此events.log
,以将数据发布到ES存储中。
通过设计,应强制每个数据更改器返回一个event
和一个指向transaction 1
的指针,这是系统永远不会“忘记”将事件提交到数据库的方式。
第三种方法是使用需要分布式事务管理器的2PC(两阶段提交),以及消息代理和数据库对2PC的支持,由于这种限制,我什至没有考虑这个方向。
因此,我只是想知道,以您的经验,什么是使系统保持“老式”(同步)的最佳方法,同时又能确保可靠的事件同时记录? (是的,我想要两个世界的所有好东西:))
免责声明:我知道这个问题有多大,如果您因此认为这个问题不属于社区,请告诉我,我将其删除。
答案 0 :(得分:1)
这种方法是否可行?这样可以解决ES的缺点,即最终的一致性,并且仍然保留其始终可靠的事件日志的光明面。
小心翼翼地做到这一点没有错-重要的是要确保事件和状态存储在同一事务中,因此也存储在同一数据库中。
沿着状态存储事件的想法已经存在了一段时间。您经常会发现围绕“域事件”的思想进行这种讨论,并使用一个聚集引发的事件来触发其他地方的行为。例如,请参见Udi Dahan在Reliable Messaging without Distributed Transactions上的演讲。
为此,我们删除了CQRS,取而代之的是,我们只有一个逻辑数据模型来将当前状态和事件历史记录一起存储。
哪个是 fine ?如果CQRS不能解决您遇到的问题,则不应使用它。
将读取模型与写入模型分开等同于缓存;为了获得好处,您必须解决two hard problems之一的缓存失效问题。