【问题标题】:Where to apply business logic in EventSourcing在 EventSourcing 中应用业务逻辑的位置
【发布时间】:2021-01-11 00:29:57
【问题描述】:

在事件源中,我对究竟必须在哪里应用业务逻辑有点困惑?我已经在 google 中搜索过,但所有示例都是非常基本的,即从事件对象更新 Handler 内部对象的状态,但在我的其他场景中,对于必须在哪里应用业务逻辑有一些困惑。

例如:让我们以一个场景来更新IntervieweeVO的状态,它存在于Interview聚合类中,如下所示:

class Interview extends AggregateRoot {

  private IntervieweeVO IntervieweeVO;

}

class IntervieweeVO {
  int performance;
  String status;
}

class IntervieweeSelectedEvent extends BaseEvent {
  private IntervieweeVO IntervieweeVO;
}

我有一个业务逻辑,即如果受访者表现

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

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

2) EventHandler 内部:在 EventHandler 类中应用业务逻辑,例如 handle(IntervieweeSelectedEvent intervieweeSelectedEvent) ,检查业务逻辑,然后更新 ReadModel 表中的 Object 状态。

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

请在上面澄清一下。

【问题讨论】:

  • 有什么想法吗?
  • 您应该能够从事件流中重建实体在特定时间点的状态,这意味着应用事件可能不应该负责随时间变化的逻辑,而应该只负责投影实体的状态。业务逻辑将进入生成事件 IMO 的实体方法中。如果今天的 performance < 3, then... 规则与当时不同,您仍然希望能够像使用旧规则一样投射旧状态。
  • 不,apply 只是为了投影状态,不应该有任何业务逻辑。 entity.doSomething() --> apply(SomeThingHappened)。逻辑在entity.doSomething()
  • 是的,否则如果在您从事件重新创建实体时业务逻辑发生变化,如果逻辑位于 apply 中,您将不会具有相同的状态。
  • 例如在您的情况下,您可能有 interviewee.assessPerformance(POOR) 产生 IntervieweePerformanceAssessedIntervieweeRejected 我认为的事件。然后,如果规则发生变化,您可以致电 interviewee.reevaluateSmartScreeningPolicy() 或类似的名称。

标签: microservices domain-driven-design cqrs event-sourcing event-driven-design


【解决方案1】:

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

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

单个事件流通常代表单个实体 - 法律程序、分类帐或个人...每个事件都是对实体状态的事务性更改(如 ACID)。

在您的情况下,这样的实体可能是一个职位。开放,宣布,被面试者邀请,接受邀请,技能评估,提供报价,接受报价,职位关闭。从我的头顶。

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

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

【讨论】:

    【解决方案2】:

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

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

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

    关键点在这里,您应该始终遵守以下链条:

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

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

    【讨论】:

      【解决方案3】:

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

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

      事件是定义状态变化的一种表达方式,而不是操作/命令。例如,如果IntervieweeRejected 表示IntervieweeStatusChanged(rejected),则该含义永远不会改变。 IntervieweeRejected 事件不能暗示除 status = rejected 之外的任何其他内容,除非在事件数据中捕获了一些其他状态(例如原因)。

      显然,表示状态的方式总是可以改变,但意义不能改变。例如,AR 可能从仅投影当前状态开始,然后投影整个状态历史记录。

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

      我有一个业务逻辑,即如果受访者表现

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

      另外,请注意,这种逻辑甚至可能不属于Interviewee AR 本身。智能筛选策略可能被视为在IntervieweePerformanceAssessed 事件之后/响应事件之后发生的事情。此外,我可以很容易地看到智能筛查政策如何变得非常复杂,由人工智能驱动,这可以证明它存在于专用的Screening 有界上下文中是合理的。

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

      【讨论】:

      • 非常感谢您的简短回复:) 一个疑问:业务逻辑是否意味着改变领域模型的状态?如果是,那么更改域的状态应该在 apply() 方法中,对吧?
      • 不,业务逻辑将决定给定起始状态和命令的新状态是什么。然后记录事件以捕获这些决策(新状态)。 apply 方法只负责在内存中实现新状态,以便可以为下一个命令做出决定。简而言之,如果这些状态不用于做出业务逻辑决策,则 apply 不需要投射事件中的所有状态。一个很好的例子可能是不投射description,因为该值从不涉及优柔寡断。
      猜你喜欢
      • 2011-10-16
      • 2019-05-22
      • 1970-01-01
      • 2016-01-18
      • 2015-11-10
      • 2014-10-23
      • 1970-01-01
      • 1970-01-01
      • 2013-01-10
      相关资源
      最近更新 更多