在CQRS和ES中处理事件时如何解决持久化延迟或错误?
How to solve persistence delay or error while handling event in CQRS and ES?
我正在尝试使用 CQRS 和 ES 实现另一个 DDD 有界上下文。
我想知道,因为有 CreateUserCommand
在我的域模型中创建了 User
(没有关于保存的字眼)。然后它触发 UserCreatedEvent
.
我有两个事件处理程序:
PersistUserEventHandler
(更新应用程序状态)和
SendWelcomeEmailEventHandler
(向用户发送欢迎电子邮件)
现在,我知道了:
- 事件处理程序中处理事件的顺序无关紧要
- 保存状态应该是详细的,因为真实来源在我的事件存储中。
但是,如果我不想在阅读模型完全更新之前发送欢迎电子邮件怎么办?因为,如果例如流程延迟或发生某些错误并且我现在无法将该用户坚持到读取模型怎么办?然后我不想现在发送那封欢迎邮件,因为如果用户点击 link 到他在邮件中的个人资料,他会看到 "user does not exists".
我看到人们直接在命令处理程序中通过存储库持久化更改(这将解决这个问题),但这对事件溯源没有意义,因为我希望能够重播所有事件(使用事件处理程序坚持只是为了防止所有其他副作用)并在持久层中获取应用程序的实际状态。
或者我应该只使用实际上将其保存到读取模型中的事件处理程序来收听 UserCreatedEvent
,然后在该事件处理程序中引发另一个事件 CreatedUserSavedEvent
并且所有电子邮件等都将由他们的管理员?
我想也不是,因为它让我想起了一些事件地狱,而且如果我让 EventBus 进入某个事件处理程序,我就会遇到循环引用问题,这只是因为我违反了每个依赖项都应该指向的规则深入到我系统的较低组件而不是另一侧。
那么,这通常是如何解决的,还是我遗漏了什么?
如果用户有随机的 UUID 那么这应该不是问题。如果用户到达 url 而 readmodel 不是最新的,那么您可以显示一条 "loading in progress,please wait" 消息。
如果您 really 想知道用户 really 是否存在 - 例如您想要查看 "user does not exists" 之间的区别] 和 "read model is not sunchronized yet" 然后你可以发送一个不生成任何事件的特殊命令(或者如果你的命令调度程序支持干命令 运行 则只测试命令)并在用户不存在时抛出异常。
- PersistUserEventHandler (updates state of app)
您可能会将读取模型误认为是准确表示应用程序当前状态的同类整体,即除事件日志之外的第二个绝对真实来源。
我更倾向于将它们视为一堆片面的、自以为是的状态包裹,它们可能不会同时更新,并且可能反映不同的事实。
我不建议将读取模型作为其他上下文中的数据源,而不是它们设计的用例。在您的示例中,SendWelcomeEmail
可能不应依赖于用户读取模型,而应仅依赖于 UserCreated
事件中包含的数据。
现在您可以在读取模型投影仪和其他类型的事件处理程序之间共享 代码 以避免重复,但共享数据似乎有风险。
我正在尝试使用 CQRS 和 ES 实现另一个 DDD 有界上下文。
我想知道,因为有 CreateUserCommand
在我的域模型中创建了 User
(没有关于保存的字眼)。然后它触发 UserCreatedEvent
.
我有两个事件处理程序:
PersistUserEventHandler
(更新应用程序状态)和SendWelcomeEmailEventHandler
(向用户发送欢迎电子邮件)
现在,我知道了:
- 事件处理程序中处理事件的顺序无关紧要
- 保存状态应该是详细的,因为真实来源在我的事件存储中。
但是,如果我不想在阅读模型完全更新之前发送欢迎电子邮件怎么办?因为,如果例如流程延迟或发生某些错误并且我现在无法将该用户坚持到读取模型怎么办?然后我不想现在发送那封欢迎邮件,因为如果用户点击 link 到他在邮件中的个人资料,他会看到 "user does not exists".
我看到人们直接在命令处理程序中通过存储库持久化更改(这将解决这个问题),但这对事件溯源没有意义,因为我希望能够重播所有事件(使用事件处理程序坚持只是为了防止所有其他副作用)并在持久层中获取应用程序的实际状态。
或者我应该只使用实际上将其保存到读取模型中的事件处理程序来收听 UserCreatedEvent
,然后在该事件处理程序中引发另一个事件 CreatedUserSavedEvent
并且所有电子邮件等都将由他们的管理员?
我想也不是,因为它让我想起了一些事件地狱,而且如果我让 EventBus 进入某个事件处理程序,我就会遇到循环引用问题,这只是因为我违反了每个依赖项都应该指向的规则深入到我系统的较低组件而不是另一侧。
那么,这通常是如何解决的,还是我遗漏了什么?
如果用户有随机的 UUID 那么这应该不是问题。如果用户到达 url 而 readmodel 不是最新的,那么您可以显示一条 "loading in progress,please wait" 消息。
如果您 really 想知道用户 really 是否存在 - 例如您想要查看 "user does not exists" 之间的区别] 和 "read model is not sunchronized yet" 然后你可以发送一个不生成任何事件的特殊命令(或者如果你的命令调度程序支持干命令 运行 则只测试命令)并在用户不存在时抛出异常。
- PersistUserEventHandler (updates state of app)
您可能会将读取模型误认为是准确表示应用程序当前状态的同类整体,即除事件日志之外的第二个绝对真实来源。
我更倾向于将它们视为一堆片面的、自以为是的状态包裹,它们可能不会同时更新,并且可能反映不同的事实。
我不建议将读取模型作为其他上下文中的数据源,而不是它们设计的用例。在您的示例中,SendWelcomeEmail
可能不应依赖于用户读取模型,而应仅依赖于 UserCreated
事件中包含的数据。
现在您可以在读取模型投影仪和其他类型的事件处理程序之间共享 代码 以避免重复,但共享数据似乎有风险。