【问题标题】:Loading events from inside event handlers?从内部事件处理程序加载事件?
【发布时间】:2025-12-26 02:35:11
【问题描述】:

我们有一个使用 GetEventStore 的事件源系统,其中命令端和非规范化程序在两个单独的进程中运行。

我有一个事件处理程序,它作为用户保存应用程序的结果发送电子邮件(ApplicationSaved 事件),我需要更改它以便给给定应用程序只发送一次电子邮件。

我可以看到几种方法,但我不确定哪种方法是正确的。

1) 我可以在读取存储中查看是否有匹配的应用程序,但不能保证当我的电子邮件处理程序处理事件时数据会在那里。

2) 我可以在我的 ApplicationSaved 事件中附加一些东西,也许是Revision,它会在每次后续保存时递增。然后我只在Revision 为 1 时发送电子邮件。

3) 在我的事件处理程序中,我可以使用存储库从我的事件存储中为匹配的客户加载事件,并建立一个与我的域中的聚合分开的聚合。它可以包含我可以用来做出决定的应用程序列表。

我的想法:

1) 这似乎是不行的,因为数据可能在也可能不在读取存储中

2) 如果数据可以从事件流中派生,那么它不需要在事件本身上。

3) 我倾向于这样做,但是读取和写入端之间应该有一个明确的分隔,这感觉像是违反了。这是否允许?

【问题讨论】:

  • 你想出了什么解决方案?我的第一个想法是自定义投影可以帮助你。但实际上,如果您遵循 CQRS/ES,那么您将拥有一个 ReadModel 和一个企业服务总线。在更新 ReadModel 和阅读 ES 之间的某个地方应该有其他东西决定是否发送电子邮件。您可能不应该使用 EventStore 作为事件发射器。它只是存储事件。您需要根据业务规则做出其他决定。也许SAGA的?可能只是域之外的其他一些持久订户

标签: domain-driven-design cqrs event-sourcing ddd-repositories eventstoredb


【解决方案1】:

我可以看到几种方法,但我不确定哪种方法是正确的。

没有完美的答案 - 在大多数情况下,外部可观察到的副作用与您的记录无关;您总是可能有一些失败模式,即发送了一封电子邮件但系统不知道,或者系统记录了一封电子邮件已发送但实际上发生了故障。

对于一个很好的答案:您通常会从一个发送和发送电子邮件的工具开始,并将报告作为电子邮件发送成功与否的事件。这基本上是一个事件流 - 您的模型不会否决电子邮件是否已发送。

有了这个部分,您实际上可以运行一个查询,询问“我现在需要发送哪些电子邮件?”您将ApplicationSaved 事件与EmailSent 事件折叠起来,从中计算出需要完成哪些新工作。

Rinat Abdullin,写信 Evolving Business Processes a la Lokad,建议使用人工操作员来驱动流程。想象一下,构建一个屏幕,显示需要发送的电子邮件,然后在人们说要实际执行“操作”的地方设置按钮,当人们点击按钮时,发送电子邮件的工作就会发生。

人类正在查看的是视图或投影,即根据记录的事件计算出的系统状态的读取模型。按钮点击向“写入模型”发送消息(点击按钮事件告诉系统尝试发送电子邮件并写下结果)。

当您需要采取行动的所有信息都包含在您正在响应的事件的表示中时,通常会考虑将数据“推送”给订阅者。但是当订阅者需要有关先前状态的信息时,基于“拉”的方法通常更容易推理。交付的事件向项目发出唤醒信号(减少延迟)。

Greg Young 在他的 Polyglot Data 演讲中详细介绍了推与拉。

【讨论】: