域命令和事件之间的CQRS关系

时间:2014-09-30 07:30:52

标签: cqrs

我正在研究CQRS架构。现在我们已经准备好了所有部分,并按照以下流程处理命令:

  1. 客户端发送命令并将其接收到HTTP端点 ChangePersonLanguage
  2. 该命令由 ChangePersonLanguageCommandHandler
  3. 调度和处理
  4. 在命令处理程序中,我们封装了业务逻辑,因此我们加载根聚合并执行方法 Person.ChangeLanguage(language)
  5. 此时 PersonRootAggregate 会引发包含根聚合对象的域事件 PersonLanguageChanged
  6. 事件处理程序执行逻辑以持久聚合,而另一个处理程序则发送通知电子邮件的逻辑
  7. 这是正确的顺序吗? 我可以在命令处理程序中执行持久性逻辑并删除一个事件处理程序吗?

2 个答案:

答案 0 :(得分:8)

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

  1. 如果持久性失败,您可能需要重新尝试处理该命令。
  2. 如果命令被接受但持久性失败,一旦你在游​​戏中添加更多的事件处理程序,你可能会遇到严重的问题:假设那些其他事件处理程序对发布的事件作出反应,从而执行他们的行动(比如说sagas /长时间运行)业务流程等),但另一方面,持久性实际上失败了,你的聚合处于不同的状态。然后,您的AR和您剩余的域名无法恢复不同步。
  3. 因此,您必须确保在发布事件之前将聚合持久保存到数据库。因为,你可以

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

答案 1 :(得分:7)

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

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

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