CQRS - 何时发送确认消息?

时间:2012-06-13 13:34:03

标签: domain-driven-design cqrs

  

示例:业务规则规定客户在下订单时应收到确认消息(电子邮件或类似信息)。

假设从域中调度NewOrderRegisteredEvent,并由发送确认消息的事件侦听器拾取。完成后,其他一些事件处理程序抛出异常或其他错误,并回滚工作单元。我们现在已经向用户发送了回滚确认消息。

解决这样的问题的“cqrs”方法是什么?在你提交工作单元之后你想做什么?另一个复杂因素是重播事件。每当我重播录制的事件以构建新的视图/投影时,我都不希望重新发送旧的确认消息。

到目前为止我最好的理论:我刚刚开始研究cqrs迷人的世界,并想知道这是否会被实现为传奇?如果一个传奇就像一个状态机,每次转换只能进行一次,那么我想这会解决这个问题吗?我很难想象这将如何与命令总线和域事件一起使用..

2 个答案:

答案 0 :(得分:16)

  1. 事件应仅在事务完成后发生。如果出现任何问题并且存在回滚,则从外部角度来看事件不会发生。因此,它根本不应该发表。虽然必要时可以发布OrderRegistrationFailed事件。

  2. 除非已成功执行该命令,否则您不希望发送邮件。

    首先说明命令处理程序(在另一个答案中提出)的错误位置的几个原因:在某些情况下,命令处理程序无法判断命令最终是否成功。让命令处理程序调用邮件发送也会将进程知识放入命令处理程序中,这会破坏SRM并使业务规则与应用程序层过于紧密。

    邮件应在事后发送,即从事件处理程序发送。

    要防止此处理程序在重放期间触发,您可以不注册它。这与您测试应用程序的方式类似。您只需注册实际需要的处理程序。

    • 生产系统 - >注册所有事件处理程序
    • 测试 - >仅注册经过测试的事件处理程序
    • 重播 - >仅注册投影/非规范化处理程序
  3. 另一个 - 更松散耦合,虽然有点复杂 - 可能是Saga处理NewOrderRegisteredEvent并向适当的有界上下文发出SendMail命令(谢谢,Yves Reynhout,在问题的评论中指出这一点。)

答案 1 :(得分:2)

有两种可能的解决方案

1)事件的发布和事件的处理(即电子邮件)是单个事务的一部分。在这种情况下,您的事务框架会为您处理它。如果电子邮件失败,则回滚该事件。您可能会重试该命令。这在概念上很干净,易于思考。没有任何事件完成发布,直到有话要说的每个人都有发言权。但实际上,这可能很痛苦,因为它通常涉及分布式事务。这些很难得到。您的电子邮件客户端是否可以注册与举办活动的数据库相同的交易?

2)事件的发布是事务性的,但事件处理程序每​​个都以自己的方式处理事务。发送电子邮件的事件处理程序可以跟踪它看到的事件。如果它崩溃了,它会请求旧事件并处理它们。如果人们丢失或重复发送电子邮件,您可以做出商业决策。 (对于与金钱相关的交易,答案可能是您不应该允许的。)

解决方案(2)通常是您在DDD / CQRS圈子中看到的,因为它是更松散耦合的解决方案。解决方案(1)在小型系统中非常实用,其中事件存储和投影位于单个数据库中,并且投影不经常改变。解决方案(2)允许各种事件处理程序以自己的方式工作。解决方案(1)可能导致许多不重叠的问题变得混乱。在这种情况下,您的订单业务规则在完成电子邮件中发生的许多奇怪事情之前不会完成。首先,它可能会让你慢下来。

如果发送电子邮件比“看到事件,发送电子邮件”更有趣,那么你是对的,你可能手上有传奇或工作流程。大型操作中的电子邮件本身通常是一个复杂的系统,您不太可能必须实现它。您只需要确保将电子邮件放入某种请求队列(使用方法(2)),电子邮件系统可能会重试/批处理/垃圾邮件避免/夜间工作/等等。