Transactions 可靠的事件日志(并非真正的事件来源)

Transactions 可靠的事件日志(并非真正的事件来源),transactions,cqrs,event-sourcing,consistency,eventual-consistency,Transactions,Cqrs,Event Sourcing,Consistency,Eventual Consistency,我正在实现一个系统,该系统应该以一致的方式存储所有事件,但同时我希望通过使用某种混合方法来保持一致性。尽管事件源的概念非常清楚,所有的变化都会进入ES日志,然后消费者会创建具体化的视图,但困扰我的恰恰是最终的一致性,因为系统现在在整个系统范围内是异步的 CQRS+ES方法建议,这应该在UI级别上解决,让客户端等待。假设我有一个接近stackoverflow的东西。当我完成这个问题时,我会点击“发布你的问题”按钮,网站会马上带我去我的问题。使用ES方法,点击“发布您的问题”将意味着我的问题将被推送

我正在实现一个系统,该系统应该以一致的方式存储所有事件,但同时我希望通过使用某种混合方法来保持一致性。尽管事件源的概念非常清楚,所有的变化都会进入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.

  • 一些数据库将允许访问提交日志,提交日志可能是事件的来源,但这种方法有点枯燥,因为读取提交日志并没有说明可能需要的任何应用程序业务逻辑,即您只需从提交日志中读取
    updateusers SET loggedIn=“2018-12-30 10:00:00”,其中id=1
    ,本例中的事件只是
    UserUpdated
    ,但在应用程序级别,它可能是一个更好的
    UserLoggedIn
ebay采用的另一种方法(据称,我在一本关于ES的书中读到过,不记得书名tho)

2.

  • 启动事务1
  • updatedomain.users设置loggedIn=“2018-12-30 10:00:00”,其中id=1
  • 启动事务2(内部trx)
  • INSERT INTO events.log SET event=“{name:UserLoggedIn,userId:1}”,timestamp=“2018-12-30 10:00:00”
  • 提交事务2
  • 提交事务1
然后,另一个进程可以轮询此
events.log
,将数据发布到ES存储中。 根据设计,应强制每个数据转换器返回一个
事件和一个指向
事务1
的指针,这就是系统永远不会“忘记”将事件提交到数据库的方式

第三种方法是使用2PC(两阶段提交),它需要分布式事务管理器,消息代理和数据库都支持2PC,由于这个限制,我甚至不考虑这个方向

因此,我只是想知道,根据您的经验,保持系统“老式”(同步)但同时保证可靠的事件日志的最佳方法是什么?(是的,我想要两个世界所有的好东西:))

免责声明:我知道这个问题有多夸张,如果你认为这个问题不属于社区,让我知道,我会把它记下来

这种方法可行吗?这将解决ES的缺点,即最终的一致性,并仍然保留其光明的一面,即始终可靠的事件日志

这并没有什么错,仔细地做——重要的是确保事件和状态存储在同一个事务中,因此存储在同一个数据库中

将事件存储在状态一侧的想法已经存在了一段时间。您经常会发现这些讨论集中在“域事件”的概念上,并使用一个聚合引发的事件触发其他地方的行为。例如,请参见Udi Dahan关于

在这样做的过程中,我们放弃了CQR——取而代之的是一个单一的逻辑数据模型,它将当前状态和事件历史存储在一起

这很好;如果CQR不能解决您的问题,那么您不应该使用它

将读模型与写模型分离相当于缓存;要获得这些好处,您必须解决缓存失效问题,这是最重要的问题之一