【问题标题】:CQRS relationship between Domain Command and Events域命令和事件之间的 CQRS 关系
【发布时间】:2014-09-30 07:30:51
【问题描述】:

我正在研究 CQRS 架构。现在我们已经准备好所有的部分,我们按照这个流程处理一个命令:

  1. 命令由客户端发送并接收到 HTTP 端点 ChangePersonLanguage
  2. 命令由ChangePersonLanguageCommandHandler调度和处理
  3. 在命令处理程序中,我们封装了业务逻辑,因此我们加载根聚合并执行方法Person.ChangeLanguage(language)
  4. 此时,PersonRootAggregate 引发域事件PersonLanguageChanged,其中包含根聚合对象
  5. 一个事件处理程序执行逻辑以保持聚合,另一个处理程序执行逻辑以发送通知电子邮件

这是正确的顺序吗? 我可以在命令处理程序中执行持久化逻辑并删除一个事件处理程序吗?

【问题讨论】:

    标签: cqrs


    【解决方案1】:

    是的,您必须在命令处理程序中执行持久化逻辑,因为:

    1. 如果持久性失败,您可能需要重新尝试处理该命令。
    2. 如果命令被接受但持久化失败,一旦您将更多事件处理程序添加到游戏中,您可能会遇到严重问题:假设那些其他事件处理程序对发布的事件做出反应并因此执行它们的操作(例如 sagas/long running业务流程等),但另一方面,持久性实际上失败了,并且您的聚合处于不同的状态。那么您的 AR 和您剩余的域将不可恢复地不同步。

    因此,您必须确保在发布事件之前将您的聚合保存到数据库中。因为,你可以

    1. 两者都使用事务:将更改保留在与事件发布相同的事务中。这可能需要 XA 事务(2 阶段提交),具体取决于您的持久性/消息总线,因此可能很昂贵。
    2. 轮询您的数据库以查找要发布的任何新更改/事件(轮询自然意味着一些额外的延迟)
    3. 使用结合了持久性和事件发布的事件存储。

    【讨论】:

      【解决方案2】:

      作为旁注,PersonLanguageChanged 事件不应包含根聚合,而应仅包含有关更改内容的信息。

      不包括整个聚合的主要原因是将事件与实体分开。例如,在 DDD 场景中,您可能有另一个有界上下文在侦听此特定事件,并且该有界上下文具有不同的 Person 聚合,因为它可能处理另一个域的复杂性,例如计费等。

      这意味着订阅者不需要依赖于您的特定聚合。

      【讨论】:

      • 我在书籍和文章中找到的有关此概念的大多数示例都会在消息中发送整个根聚合,但实际上你是对的,就内存而言,发送整个聚合而不是说“嘿,看,这是人的 ID,这是新语言的 ID”会更方便 恕我直言
      • 不包括整个聚合的主要原因是将事件与实体分开。例如,在 DDD 场景中,您可能有另一个有界上下文正在侦听此特定事件,并且该有界上下文具有不同的 Person 聚合,因为它可能处理另一个域的复杂性,例如计费等。这意味着订阅者不需要依赖于您的特定聚合
      • 这里有一个简单的经验法则是始终使用(如果可能)本机类型(字符串、整数、日期时间等)。这迫使你将你的 AR 分解/映射到你的事件,这样你就可以避免 Niklas Arbin 已经说过的内容。您的其他 BC(很可能是消费这些事件的那些)应该不知道其他 BC AR。
      猜你喜欢
      • 2019-05-09
      • 2020-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-08
      • 1970-01-01
      • 2019-01-27
      • 1970-01-01
      相关资源
      最近更新 更多