在 EventSourcing 中应用业务逻辑的位置

Where to apply business logic in EventSourcing

在事件采购中,我对必须在何处应用业务逻辑感到有些困惑?我已经在 google 中搜索过,但所有示例都非常基本,即从事件对象更新 Handler 内部对象的状态,但在我的其他情况下,有些困惑不明白究竟必须在哪里应用业务逻辑。

例如:假设一个场景来更新 IntervieweeVO 的状态,它存在于 Interview 聚合 class如下:

class Interview extends AggregateRoot {

  private IntervieweeVO IntervieweeVO;

}

class IntervieweeVO {
  int performance;
  String status;
}

class IntervieweeSelectedEvent extends BaseEvent {
  private IntervieweeVO IntervieweeVO;
}

我有一个业务逻辑,即如果受访者表现 < 3,则状态 = REJECTED,否则状态应为 SELECTED。

所以,我的疑问是:我应该把业务逻辑放在什么地方?以下是 3 个场景

1) 在应用事件之前:做业务逻辑,然后应用(IntervieweeSelectedEvent) 然后eventstore.save(intervieweeSelectedEvent)

2) Inside EventHandler: 在EventHandlerclass中应用业务逻辑,比如handle(IntervieweeSelectedEvent intervieweeSelectedEvent),检查业务逻辑然后更新ReadModel中的对象状态table.

3) 在两个地方应用业务逻辑 即,在应用事件之前和处理事件时(结合以上 1 + 2)

请澄清以上内容。

事件溯源的主要问题是很难使用合成场景生成可行的示例。

但也许我可以提出比面试更好的建议。如果你比较 pre-computer 时代的事件源系统,你会发现事件流,它是构成某个实体生命周期的事件的存储,而不是 long-living 的东西。实体中的事件可能跨越几天(跟踪某些文档流的列表)、一年(某些组织的会计期间)或数十年(某些人的医疗记录)。

单个事件流通常表示单个实体 - 法律程序、账本或个人...每个事件都是对实体状态的事务性(如在 ACID 中)更改。

在你的例子中,这样的实体可以是一个职位。打开、宣布、邀请面试者、接受邀请、评估技能、提供报价、接受报价、关闭职位。从我的头顶。

当一个事件被添加到一个实体时,这意味着该实体的状态已经改变。这是关于实体的新真相。你要小心改变事实。所以,这就是业务逻辑发生的地方。你 运行 一些业务逻辑来决定是否改变真相。如果您决定更新事实状态 - 您保存事件。也就是说,“受访者被拒绝”在这种情况下是一个有效事件。

由于一个事件被持久化,一个实体的所有保存的事件按它们各自的顺序无条件地是关于该实体的真相的一部分。然后,您无需决定是“接受”还是“拒绝”持续存在的事件 - 只决定它如何影响投影。

你标记了你的问题 cqrs 但这实际上是你的示例中缺少的部分。

Eventsourcing 只是一种查看对象当前状态的方法。你要么保存现在出现的状态,要么从发生的一切中获取它。 (例如,银行账户当前余额作为所有交易的价值或总和)

所以事件是发生的事情的“事实”。在您的情况下,这将是具有一定分数的采访。并且(取决于您的业务逻辑)如果障碍预计会随时间变化,它还可以说明状态。

关键就在这里,要始终坚持以下链条:

“一个命令得到验证,如果它通过它创建一个持久的不可更改的事件”

这意味着在您的情况下,我会选择选项 1。应该验证 SelectIntervieweeCommand,如果一切正常,请创建一个 IntervieweeSelectedEvent,这是一个不可改变的事实。因此,无论受访者是否通过,业务逻辑都必须驻留在命令处理函数中。

您应该能够从事件流中重建实体在特定时间点的状态。

这意味着应用事件不应包含状态映射逻辑以外的任何逻辑。从事件中投射 AR 状态所需的所有状态都必须在这些事件中明确定义。

事件是定义状态变化的表达方式,而不是 operations/commands。例如,如果 IntervieweeRejected 表示 IntervieweeStatusChanged(rejected) 那么这个意思永远不会改变。 IntervieweeRejected 事件不能暗示除 status = rejected 之外的任何其他内容,除非在事件数据中捕获了一些其他状态(例如原因)。

显然,状态的表示方式可以随时更改,但含义不能更改。例如,AR 可能一开始只投影当前状态,然后投影整个状态历史记录。

apply(IntervieweeRejected) => status = REJECTED //at first
apply(IntervieweeRejected) => statusHistory.add(REJECTED) //later

I have a business logic, ie., if interviewee performance < 3, then status = REJECTED, otherwise status should be SELECTED.

业务逻辑将放置在标准 public AR 方法中。在这种特定情况下,您可能希望 interviewee.assessPerformance(POOR) 产生 IntervieweePerformanceAssessed(POOR)IntervieweeRejected 事件。如果您以后需要重新评估该智能筛选策略(例如,如果它发生了变化),那么您可以实施 reevaluateSmartScreeningPolicy 操作。

此外,请注意,这样的逻辑甚至可能不属于 Interviewee AR 本身。智能筛选策略可以看作是对 IntervieweePerformanceAssessed 事件发生的 after/in 响应。此外,我可以很容易地看出智能筛选策略如何变得非常复杂,AI-driven 这可以证明它存在于专用的 Screening 有界上下文中。

你的问题实际上让我思考如何有效地捕捉上下文或事件发生的原因,我已经问过这个 here :)