在CQRS“架构”中使用EventStore使消息总线的使用变得不必要

本文关键字:总线 不必要 消息 EventStore 架构 CQRS | 更新日期: 2023-09-27 18:02:51

我对诸如CQRS之类的范例和相关体系结构非常陌生。我开始了一个我认为适合这种技术的项目。我发现在项目中使用EventStore很有趣,但我在文档中读了一点,我看到使用EventStore使得没有必要拥有消息总线,因为EventStore本身允许订阅事件-这是正确的吗?在EventStore之上实现总线是否会有一些优势?

在CQRS“架构”中使用EventStore使消息总线的使用变得不必要

消息总线和事件存储是两个不同的东西,服务于两个不同的目的。

EventStore (GES)允许通过使用订阅(客户端跟踪或服务器跟踪(竞争消费者))以及ATOM提要的长轮询来订阅事件。事件被组织成流,每个流都有自己的名称,包含该流的事件。由于CQRS和EventSourcing通常应用于DDD项目,流通常表示聚合,读取单个流允许重新创建聚合状态,从流类别(聚合类型)读取事件用于预测(构建读取模型)。每个订阅者控制自己的读取事件的过程,由于事件是存储的,因此您可以根据需要多次读取它们。

消息总线与保存事件无关。它的目标是在端点之间可靠地传递消息。消息总线通常支持代理或联合拓扑以及不同的模式,如直接发送、发布-订阅和请求-响应。一旦消息使用者确认了消息,消息就会从队列中删除,并且无法再次读取。如果您没有正在发布的消息的订阅者,您将永远失去它们。

意思是,两者都是有用的模式,但事件存储更多地用于持久性,消息总线/代理更多地用于持久队列和可靠交付。

我倾向于这样想,我们是这样设置的:

消息总线:处理命令和事件代理。它用于各种服务之间的通信和广播事件。这里的"Event"是暂时的。它只是供订阅者触发的。

EventStore:这用于存储针对域实体采取的操作。在此上下文中,"Event"是持久的。它的存在是为了重放和获得对象的"状态"。

不能重放瞬态消息,因为它们只触发一次,然后被消耗。EventStore中的事件是持久的/可重放的,并被重放以获得对象状态或构建投影。一个是经纪人,一个是线人。

在物理和概念上保持两者之间的粗线。

我看到EventStore的使用使得没有必要使用消息总线…这是正确的吗?

是的,使用事件存储可以使消息总线变得不必要。事件存储是一个持久队列,您可以在其中直接读取事件,在GES的情况下,您还可以在事件发生时订阅事件。除了GES之外,在其他事件存储中,您还可以通过数据库轮询从数据库中获取新消息。(定期发送"自X以来的所有消息"的请求)

消息总线和事件存储之间的主要区别之一是订阅将是"拉"而不是"推"。消息总线在决定您已经看到的内容和您仍然需要的内容时进行"推送"。我见过的事件存储使用"拉"模型(用于退出进程),您可以在其中跟踪自己的检查点。有一些小的计算(定期保存检查点),但也有很多功能,你将在下面看到。

注意:GES确实有一个推送模型,但它用于生产者/消费者场景,而不是将相同的消息传递到多个端点。

另一个区别是,您通常不能告诉事件总线向您发送所有以X开头的消息。即使它具有该功能,也可能不再具有这些消息,这取决于X是多久以前的。因此,如果您发现代码中的错误,您可能不得不从一个系统到另一个系统使用ETL来修复它。错误总是会发生,所以你最终有两个不同的过程来传播数据,一个基于事件的快乐路径,另一个基于ETL的数据修复。

但是对于事件存储,您可以使用相同的过程来进行正常路径和数据修复。所有事件仍然存在(默认情况下,除非另有配置),并且使用pull模型,您可以控制希望看到的消息。因此,给定相同的错误,您可以修复代码,然后清空受影响的读取模型,将其检查点设置为零,并且它将在重新启动时从头开始重建。不需要开发ETL流程。(Ops关注:如果您的事件存储很大,重新构建可能需要一些时间,但您可以与旧模型并排创建初始重新构建,并在维护窗口中交换它。)

在这里可以找到一些很好的信息/经验。

公共汽车在其他情况下仍然有意义…例如,只有最新的消息才重要。或者当消息太多而无法存储时。

在EventStore的顶部实现总线的使用会有一些优势吗?

我不这么认为。对于这种情况,可能有意义的拓扑是让一些代码订阅事件存储并将新消息发布到总线以更新许多侦听器(例如用于扩展读取)。然而,使用特定于ges的特性可以更好地解决这个问题。即,使用AtomPub和缓存代理的HTTP API。由于事件是不可变的,在它们被看到一次之后,它们可以在每个级别被无限期地缓存。这将避免除了第一次发布消息外需要访问事件存储。这仍然允许客户端保留对自己订阅的控制。