如何使用事件溯源实现复杂表单的命令和事件?

How to implement Commands and Events for complex form using Event Sourcing?

我想使用 Axon 框架实现 CQRS 和 ES

我有一个非常复杂的 HTML 表格,它代表了包含六个步骤的招聘流程。
ES 将有助于生成选定日期的历史统计信息并跟踪表单的变化。

管理员始终可以执行多项操作:

表单更新(仅区别)从 UI 应用程序发送到后端。

假设我只想对服务器端应用程序进行更改,问题是什么应该是命令,什么应该是事件,我考虑三个选项:


  1. Form patch 是生成 Form Update Event

    的命令
    • 此解决方案的缺点是每个事件处理程序都需要检查表单中的更改是否引用此处理程序 ex。如果应该发送有关拒绝的电子邮件

  1. 表单补丁是一个生成多个事件的命令,例如:。 Interviewer Assigned, Notifications Turned Off, Rejected on technical interview

    • 此解决方案的缺点是某些事件可能会生成,而其他事件则不会,因为会破坏约束,例如:Notifications Turned Off 会成功,但 Interviewer Assigned 会因分配未授权用户而失败。也许我应该在命令生成之前检查所有约束?

  1. 表单补丁被转换为多个命令,例如:Assign InterviewerTurn Off Notifications,每个命令生成事件,例如:Interviewer Assigned, Notifications Turned Off

    • 此解决方案的缺点是某些命令可能会失败,例如:Assign Interviewer 可能会因分配未经授权的用户而失败。这将以不一致的状态结束,因为有些事件会存储在存储库中,有些不会。也许我应该在命令生成之前检查所有约束?

我想提请您注意的问题:您是在为您存储的信息创建权限,还是只是在跟踪来自外部世界的信息?

乌迪大汉写道Race Conditions Don't Exist;提出这个有趣的观点

A microsecond difference in timing shouldn’t make a difference to core business behaviors.

如果您的系统中有未经授权的用户,在他们被分配负责特定步骤之前获得授权对业务来说真的很重要吗?系统真的能说"fault"是责任分配给了错误的用户,而不是用户错误地未被授权吗?

Greg Young 谈到 exception reports in warehouse systems,指出在这种情况下模型的责任不是防止数据更改,而是在数据更改产生不一致状态时报告。

如果您仍然更新数据,业务成本是多少

如果消息的语义是 Decision Has Been MadeSomething In The Real World Has Changed,那么您的模型不应试图阻止该信息被记录。

由于您提到的原因,

FormUpdated 不是特别令人满意的事件;您必须做很多额外的工作才能将其转换为特定领域的术语。如果可以选择,您宁愿这样做一次。考虑事件从领域不可知形式转换为领域特定形式是合理的。

HttpRequestReceived ->
FormSubmitted ->
InterviewerAssigned

中间表示是短暂的。

VoiceOfUnreason 很好地解释了在开始使用这样一个系统时您应该考虑什么,所以一定要看看他的回答。

我唯一想补充的是,我建议您选择第三个选项。 对于您提供的示例,更通用的 commands/events 并不能说明您域中正在发生的事情。更详细的事件可以更好地解释到底发生了什么,因为事件消息的名称已经指出了这一点。

将 Axon Framework 拉入循环,我还可以添加一些提示。 从命令消息的角度来看,只走一条路线而不是想太多是安全的。该框架很容易让您稍后调整命令结构。在 Axon Framework 培训中,通常建议让命令消息采用您正在执行的特定操作的形式。因此,“将一个人分配到一个步骤通常是 AssignPersonToStepCommand,因为这是您希望系统执行的确切操作。

从事件来看,稍后决定您需要细粒度事件还是通用事件通常有点麻烦。这是在进行事件溯源之后得出的。由于事件是您的真实来源,因此您需要处理系统中所有形式的事件。

因此,我认为你的决定的重要性应该取决于你的事件变得多么精细。回到你的问题:在你给出的例子中,我会说选项 3 最合适。

我可以看到第一个选项的一大缺点。 CQRS/ES 与 Axon 的最大优势之一是可扩展性。我们可以添加新功能而不用担心回归错误。添加新功能是为它们定义新命令、事件和处理程序的结果。 None 不应干扰我们系统中现有的。

FormUpdate 作为命令需要在其中一个处理程序中添加额外的逻辑。将新属性添加到补丁并因此添加到命令将导致当前逻辑发生变化。在那种情况下,可扩展性不再是优势。