15

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

假设 aNewOrderRegisteredEvent从域中分派,并由发送确认消息的事件侦听器接收。完成后,其他一些事件处理程序会抛出异常或出现其他问题,并且工作单元会回滚。我们现在已经向用户发送了一条回滚的确认消息。

解决此类问题的“cqrs”方式是什么,您想在完成一个工作单元后做某事?另一个复杂的因素是重播事件。每当我重播记录的事件以构建新的视图/投影时,我不希望重新发送旧的确认消息。

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

4

2 回答 2

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

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

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

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

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

    • 生产系统 -> 注册所有事件处理程序
    • 测试 -> 仅注册测试的事件处理程序
    • 重播 -> 仅注册投影/非规范化处理程序

另一个——甚至更松散耦合,虽然有点复杂——可能是拥有一个Saga句柄并向适当的有界上下文NewOrderRegisteredEvent发出SendMail命令(感谢 Yves Reynhout,在问题的评论中指出了这一点)。

于 2012-06-14T12:35:38.903 回答
3

有两种可能的解决方案

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

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

解决方案 (2) 通常是您在 DDD/CQRS 圈子中看到的推广,因为它是更松散耦合的解决方案。解决方案 (1) 在事件存储和投影位于单个数据库中且投影不经常更改的小型系统中非常实用。解决方案 (2) 允许各种事件处理程序以自己的方式工作。解决方案(1)可能会导致许多不重叠的关注点变得纠缠不清。在这种情况下,您的订单业务规则要等到电子邮件中发生的许多奇怪的事情都得到处理后才能完成。一方面,它可能会减慢你的速度。

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

于 2012-06-14T01:35:23.453 回答