每个微服务的 CQRS 事件源和自己的数据库

CQRS Event-sourcing and own database per microservice

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

My first question is - Can microservice has his own database and store data inside it too? Or maybe in event-sourcing approach microservices don't have their own databases and everything is only stored inside event store?

My second question is - when I execute command in microservice and need some data for validation purposes do I need call ReadModel or what? Assuming microservices haven't got their own databases I have no choice?

Can microservice has his own database and store data inside it too?

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

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

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

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

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

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

when I execute command in microservice and need some data for validation purposes do I need call ReadModel or what?

命令由聚合体执行,它有自己的状态。此状态是通过处理此聚合的所有事件构建的,此状态应用于验证命令。

cannot/should not talk to Read Models in the command handler,主要是因为那些读取模型与聚合状态不一致。聚合状态一致。

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

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

但是在处理此命令 ({id:123, type:CREATE_USER, name:somename}) 时,您无法检查 "somename" 是否已被占用,因为用户 123 的聚合状态不包含已占用名称列表。您可以潜在地查询一些 AllUsernames 读取模型,但它可能是毫秒级的,并且其他一些用户可能已经接受了这个 "somename"。因此,在这种情况下,您会发现在将名称添加到读取模型时出现重复。那时你可以做一些补偿行动——通常是发出一个命令来暂停一个重名的用户,并要求他重新注册或以某种方式更改他的名字。

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