目前,我们正在构建一个基于CQRS和域驱动设计原则的新架构。我们现在正在讨论如何处理外部沟通。为了使问题更具体,我使用了在客户创建订单时发送SMS通知的示例。
客户端创建一个由关联的命令处理程序处理的NewOrderCommand。处理程序在域模型中创建一个新的Order对象,该对象生成一个NewcustomerCreatedEvent。对象保存在事件存储中,事件将发布给所有侦听器。
到目前为止这么好,但现在问题。我们应该在哪里发送短信通知?
我们的第一直觉告诉我们,我们应该使用侦听NewCustomerCreatedEvent并发送消息的事件侦听器将其发送出去。这种方法的问题在于发送SMS也是我们业务逻辑的一部分。我们正在销售托管服务,因此我们的客户应该能够看到代表他们发送的所有SMS消息。因为消息的发送发生在域之外,所以我们无法做到这一点。
所以我们创建了一个SMS域,现在当事件监听器收到NewCustomerCreatedEvent时,事件处理程序创建一个新命令SendSmsMessageCommand,它将在我们的域中创建一个新的SMSMessage对象,发出SMS通知并创建我们使用的SmsSent事件创建视图。
起初我们在域模型中发送了SMS消息,但我们意识到这可能会带来一些问题。假设在发送SMS之后发生了某些事情(抛出异常)并且事务被回滚。我们的域名完全支持这一点,所以数据方面我们没问题,但SMS消息已经发送,所以当重新发送命令时,SMS通知将再次发送。
我们正在考虑在SmSSent事件上发送短信,但这有点奇怪,因为事件说消息已经发送但事实并非如此。
上面的例子给我们带来了如何处理CQRS和域驱动设计概念中的外部通信的问题?我们不仅讨论发送短信通知,还讨论向外部计费系统发送发票以及与外界进行的所有其他类型的通信。我们应该在域中执行此操作,因为它是业务逻辑还是应该始终基于事件处理程序中的事件执行此操作?如果我们这样做,是否可以使用事件来表明消息在尚未实际完成时发送?
希望你们已经处理过这种情况,并就这个问题给我们一些建议。
答案 0 :(得分:2)
我认为不需要SMS消息的域对象。您只需要向客户报告发送的短信,对吗? SMS消息不在任何域逻辑中使用,对吗?
所以我会让处理程序发送一条短信,然后发布另一个事件,说明已发送短信,并让事件处理程序侦听SMS发送的消息,并在读取模型中实现该信息,以便客户可以查看它们。
答案 1 :(得分:0)
您可以使用Saga或Microsoft称之为进程管理器。这基本上监听事件,这些事件改变了传奇的状态,并根据传奇中实现的状态逻辑发出命令。
在你的情况下,它将是一个两个状态的传奇,它等待CustomerCreatedEvent和OrderCreatedEvent,并且,如果你有一个专门的有限上下文用于通信,或者发出一个命令来发送短信,或者通过一个基础设施服务来调用一个接口,发送短信。
在这里,您可以找到关于传奇/流程管理器模式的Microsoft文章:
https://msdn.microsoft.com/en-us/library/jj591569.aspx
两篇包含实现的文章:
http://danielwhittaker.me/2015/03/31/how-to-send-emails-the-right-way-in-a-cqrs-system/
http://blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/