CQRS/ES 架构上的补偿事件

Leo*_*ira 6 events cqrs event-sourcing eventstoredb

所以,我正在做一个 CQRS/ES 项目,在这个项目中,我们对如何处理在其他架构中很容易处理的琐碎问题有一些疑问

我的情况如下:

我有一个客户 CRUD REST API,每个客户都有唯一的文档(编号),所以当我注册一个新客户时,我必须验证是否有另一个客户拥有该文档以避免重复,但是当涉及到 CQRS/在 ES 架构中,我们具有最终一致性,我发现这种验证很难解决。

需要注意的是,我的问题不是跨微服务,而是在同一个微服务的命令应用和查询应用之间。

我们也在使用eventstore

我目前的解决方案:

所以我今天要做的是,在我的命令应用程序中,在保存 CustomerCreated 事件之前,我询问查询应用程序(使用 PostgreSQL)是否有客户拥有该文档,如果没有,我允许该事件继续进行。但这并不能保证 100%,对吧?因为我的查询可以不同步,所以我不能 100% 信任它。那是我的第二次验证开始时,当我的查询应用程序正在处理事件并将它们保存到我的 PostgreSQL 时,我再次检查是否有客户拥有该文档,如果有,我拒绝该事件并发出补偿事件以撤消/cancel/inactivate 使用重复文档的客户,从而在 eventstore 上完成该客户流。

尽管这有效,但这里有两件事困扰我,第一件事是我的命令应用程序依赖于查询应用程序,因此如果我的查询应用程序关闭,我的命令会受到影响(今天,如果查询是下来但仍然......)第二件事是,查询/读取模型真的能够发出事件吗?如果是这样,正确的做法是什么?该命令是否应该为此提供某种 API?或者查询是否应该使用一些公共共享库将事件直接发送到事件存储?如果我有不止一个视图/阅读?我应该选择哪一个来处理这个问题?

真的希望有人能对这些问题有所了解,并帮助我解决这些问题。

Voi*_*son 4

作为参考,您可能需要查看 Greg Young 撰写的有关Set Validation 的文章。

我询问查询应用程序(使用 PostgreSQL)是否有客户拥有该文档,如果没有,我允许事件继续进行。但这并不能保证100%,对吧?

这是完全正确的 - 您的读取模型是过时的副本,并且可能没有写入模型收集的所有信息。

那是我的第二次验证开始的时候,当我的查询应用程序正在处理事件并将它们保存到我的 PostgreSQL 时,我再次检查是否有客户拥有该文档,如果有,我拒绝该事件并发出补偿事件以撤消/取消/停用具有重复文档的客户,从而在 eventstore 上完成该客户流。

这种拼写与通常的设计不太相符。更常见的实现是,如果我们在读取数据时检测到问题,我们会向写入模型发送一条命令消息,告诉它解决问题。

这通常称为流程管理器,但您可以将其视为系统人类主管的自动化。从概念上讲,流程管理器是要发送到命令模型的事件源消息集合。

您可能还需要考虑是否正确地对域进行建模。如果文档应该是唯一的,那么命令模型可能应该使用文档编号作为记录簿中的键,而不是使用客户。或者文档 ID 可能应该是客户数据的函数,而不是任意输入。

据我所知,eventstore 没有跨不同流的事务

是的 - 一般来说,您真正需要考虑的事情之一是您的流边界在哪里。如果集合验证具有重要的商业价值,那么您确实需要考虑将整个集合放入单个流中(或者找到一种在不使用集合的情况下限制唯一性的方法)。

我应该如何向写入模型发送命令消息?通过API?通过像 Kafka 这样的消息代理?

这就是管道;只要您确定该命令在其自己的事务/工作单元中运行,您如何执行并不重要。