首页 > 解决方案 > CQRS 事件溯源和每个微服务自己的数据库

问题描述

我对微服务架构中的事件溯源和 cqrs 有一些疑问。我知道在发送命令之后,一些微服务会执行它并发出事件。事件存储订阅它并保存在他的数据库中。还有一些基于此事件的 ReadModel 会在读取数据库中生成和保存优化数据。

我的第一个问题是 - 微服务可以拥有自己的数据库并在其中存储数据吗?或者,在事件溯源方法中,微服务没有自己的数据库,所有内容都只存储在事件存储中?

我的第二个问题是 - 当我在微服务中执行命令并需要一些数据进行验证时,我需要调用 ReadModel 还是什么?假设微服务没有自己的数据库,我别无选择?

标签: eventsmicroservicescqrsevent-sourcing

解决方案


微服务能否拥有自己的数据库并在其中存储数据?

当然,微服务可以拥有自己的数据库。但是让我们使用 ES/CQRS 中的术语。数据库可以表示事件存储(不可变事件的仅附加日志)和读取模型 - 一些用于回答由处理事件填充的查询的数据库。

因此,微服务可以有自己的读取模型,由其他微服务的事件填充。

或者微服务可以处理命令并将事件保存到共享的 Event Store。

或者微服务可以处理命令并将事件保存到自己的事件存储中。

选择权在您手中,这取决于您希望在微服务之间实现的分离程度。

我会将通常一起使用的所有事件放入同一个事件存储中。这意味着我应该能够查询这些事件并因此得到一个有序的流。

当我在微服务中执行命令并需要一些数据进行验证时,我需要调用 ReadModel 还是什么?

命令由具有自己状态的聚合执行。此状态是通过处理此聚合的所有事件来构建的,并且应使用此状态来验证命令。

不能/不应该在命令处理程序中与读取模型对话,主要是因为这些读取模型与聚合状态不一致。聚合状态是一致的。

您可以在发送命令之前查询读取模型(以确保可以发送)。但是在命令处理程序中,您只需要依赖聚合状态。

有一个著名的案例,即要求唯一名称的注册用户。作为主要验证,在您的 UI 代码中,您可以查询读取模型并告诉用户输入的名称已被采用。如果未使用名称,则 UI 让用户发出命令。我假设您的聚合根是用户。

但是在处理此命令 ( {id:123, type:CREATE_USER, name:somename}) 时,您无法检查是否使用了“somename”,因为用户 123 的聚合状态不包含已使用名称的列表。您可能会查询一些 AllUsernames 读取模型,但它可能是几毫秒以前的,并且其他一些用户可能已经使用了这个“somename”。因此,在这种情况下,您会在将名称添加到读取模型时发现重复。那时您可以采取一些补偿措施 - 通常发出命令以暂停具有重复名称的用户并要求他重新注册或以某种方式更改他的名称。

这可能看起来很奇怪,但如果你有一个真正的分布式系统,有多个用户列表副本,你也会遇到同样的问题,那么为什么不接受数据总是不完全一致的事实,然后处理它呢?


推荐阅读