使用 CQRS 和域驱动设计发送电子邮件或 SMS
目前,我们正在构建一个基于 CQRS 和领域驱动设计原则的新架构。我们现在正在讨论如何处理外部沟通。为了使问题更加具体,我使用了在客户创建订单时发送短信通知的示例。
客户端创建一个 NewOrderCommand,由关联的命令处理程序处理。该处理程序在域模型中创建一个新的 Order 对象,该对象生成 NewcustomerCreatedEvent。该对象保存在事件存储中,并将事件发布给所有侦听器。
到目前为止一切顺利,但现在问题来了。我们应该向哪里发送短信通知?
我们的第一直觉告诉我们应该使用事件侦听器来侦听 NewCustomerCreatedEvent 并发送消息。这种方法的问题在于发送短信也是我们业务逻辑的一部分。我们正在销售托管服务,因此我们的客户应该能够看到代表他们发送的所有短信。由于消息的发送发生在域之外,因此我们无法执行此操作。
因此,我们创建了一个 SMS 域,现在当事件侦听器收到 NewCustomerCreatedEvent 时,事件处理程序会创建一个新命令 SendSmsMessageCommand,它将在我们的域中创建一个新的 SMSMessage 对象,发送 SMS 通知并创建一个 SmsSent 事件,我们用它来创建看法。
起初,我们在域模型中发送 SMS 消息,但我们意识到这可能会带来一些问题。假设发送短信后发生了一些事情(抛出异常)并且事务被回滚。我们的域完全支持这一点,因此数据方面我们没问题,但是短信已经发送,因此当重新发送命令时,短信通知将再次发送。
我们正在考虑在 SmSSent 事件上发送短信,但这有点奇怪,因为该事件说消息已经发送,但实际上并未发送。
上面的例子给我们带来了一个问题:如何在 CQRS 和领域驱动设计概念中处理外部通信?我们不仅谈论发送短信通知,还谈论向外部计费系统发送发票以及与外界的所有其他类型的通信。我们应该在域中执行此操作,因为它是业务逻辑,还是应该始终基于事件处理程序中的事件执行此操作?如果我们这样做,那么使用表示消息已发送而实际上尚未完成的事件是否可以接受?
希望你们已经处理过这种情况,并可以就此问题给我们一些建议。
At this moment we are building a new architecture that is based on the principles of CQRS and domain-driven-design. We are now having some discussions about how we should deal with external communication. To make the question more concrete I use the example of sending a SMS notification when a customer creates a order.
The client creates a NewOrderCommand that is handled by the associated command handler. The handler creates a new Order object in the domain model, that generates a NewcustomerCreatedEvent. The object is saved in the event store and the event is published to all the listeners.
So far so good but now the question. Where should we sent out the SMS notification?
Our first instinct told us we should send it out by using a event listener that listens for the NewCustomerCreatedEvent and sends out the message. The problem with this approach is that the sending of the SMS is also part of our business logic. We are selling hosted services so our clients should be able to see all the SMS messages that are sent on their behalf. Because the sending of the message takes place outside of the domain we are not able to do that.
So we created an SMS domain and now when the event listener receives the NewCustomerCreatedEvent the event handler creates a new command SendSmsMessageCommand that will create a new SMSMessage object in our domain, sends out the SMS notification and creates a SmsSent event that we use to create the view.
At first we were sending the SMS message in the domain model, but we realized that this could give some problems. Let's say that after sending the SMS something happens (an exception is thrown) and the transaction is rolled back. Our domain supports this completely so data wise we are ok, but the SMS message is already sent, so when the command is resent the SMS notification will be sent again.
We were thinking about sending out the SMS on the SmSSent event but that would be a little bit strange, because the event says the message is already sent but is isn't.
The example above brings us to the question how to deal with external communication in the CQRS and domain-driven-design concept? We are not only talking about sending an SMS notification but also about sending an invoice to and external billing system and all other sort of communication to the outside world. Should we do this in the domain because it business logic or should we always do that based on events in our event handlers? And if we do so, is it acceptable to use events that say that the message is sent when it's not actually done yet?
Hope you guys have already dealt with this situation and can give us some advice on this subject.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为 SMS 消息的域对象是不必要的。您只需向客户报告发送的短信,对吗? SMS 消息未在任何域逻辑中使用,对吗?
因此,我会让处理程序发送一条短信,然后发布另一个事件,表示一条短信已发送,并让一个事件处理程序侦听短信发送的消息,并在读取模型中具体化该信息,以便客户可以查看它们。
I would think a domain object for the SMS message is not necessary. You just need to report the SMS's sent to the customer, correct? The SMS messages are not used in any domain logic, correct?
So I would have the handler send an SMS and then publish another event that says an SMS was sent and have an event handler listen for the SMS sent message and materialize that info in a read model so that the customer can view them.
您可以使用 Saga,或者 Microsoft 所说的 Process Manager。这基本上监听改变 saga 状态的事件,并根据 saga 中实现的状态逻辑发出命令。
在您的情况下,这将是一个两种状态的传奇,等待 CustomerCreatedEvent 和 OrderCreatedEvent,并且,如果您有专门的有界上下文用于通信,则可以发出发送短信的命令,或者通过接口调用基础设施服务,发送短信。
您可以在这里找到 Microsoft 关于 saga/process manager 模式的文章:
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-source-part-ii-of-ii/
You could use a Saga, or a Process Manager as Microsoft calls it. This basically listens to events, which change the saga's state, and issues commands based on the state logic implemented in the saga.
In your case it would be a two state saga, that waits for both CustomerCreatedEvent and OrderCreatedEvent, and, either issue a command to send an sms, if you have a specialised bounded context for communication, or call an infrastructure service, through an interface, to send the sms.
Here you can find Microsoft's article on the saga/process manager pattern:
https://msdn.microsoft.com/en-us/library/jj591569.aspx
And two articles containing implementations:
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/