Отправка электронной почты или SMS с использованием CQRS и доменного проектирования
В настоящий момент мы строим новую архитектуру, основанную на принципах CQRS и доменного проектирования. Сейчас у нас есть некоторые дискуссии о том, как мы должны иметь дело с внешним сообщением. Чтобы сделать вопрос более конкретным, я использую пример отправки уведомления SMS, когда клиент создает заказ.
Клиент создает команду NewOrderCommand, которая обрабатывается соответствующим обработчиком команд. Обработчик создает новый объект Order в модели домена, который генерирует NewcustomerCreatedEvent. Объект сохраняется в хранилище событий, и событие публикуется всем слушателям.
До сих пор так хорошо, но теперь вопрос. Где мы должны отправить SMS-уведомление?
Наш первый инстинкт сказал нам, что мы должны отправить его, используя прослушиватель событий, который прослушивает NewCustomerCreatedEvent и отправляет сообщение. Проблема с этим подходом заключается в том, что отправка SMS также является частью нашей бизнес-логики. Мы продаем хостинговые услуги, чтобы наши клиенты могли видеть все SMS-сообщения, отправленные от их имени. Поскольку отправка сообщения происходит за пределами домена, мы не можем этого сделать.
Итак, мы создали домен SMS, и теперь, когда слушатель событий получает NewCustomerCreatedEvent, обработчик событий создает новую команду SendSmsMessageCommand, которая создаст новый объект SMSMessage в нашем домене, отправит уведомление SMS и создаст событие SmsSent, которое мы используем для создания представления.
Сначала мы отправляли SMS-сообщение в модели домена, но мы поняли, что это может привести к некоторым проблемам. Скажем, что после отправки SMS что-то происходит (исключение выбрасывается) и транзакция откатывается. Наш домен полностью поддерживает это, поэтому мы считаем, что мы в порядке, но SMS-сообщение уже отправлено, поэтому, когда команда повторно отправлена, уведомление SMS будет отправлено снова.
Мы думали о отправке SMS на мероприятие SmSSent, но это было бы немного странно, потому что событие говорит, что сообщение уже отправлено, но это не так.
В приведенном выше примере мы подходим к вопросу о том, как справляться с внешней связью в концепции CQRS и концепции управляемого доменом? Мы говорим не только о отправке SMS-уведомления, но также о отправке счета-фактуры и внешней биллинговой системы и всех других видов связи во внешний мир. Должны ли мы делать это в домене, потому что это бизнес-логика или мы всегда должны делать это на основе событий в наших обработчиках событий? И если мы это сделаем, допустимо ли использовать события, которые говорят, что сообщение отправлено, когда оно еще не выполнено?
Надеюсь, вы, ребята, уже справились с этой ситуацией и можете дать нам несколько советов по этому вопросу.
Ответы
Ответ 1
Я бы подумал, что объект домена для SMS-сообщения не нужен. Вам просто нужно сообщить SMS, отправленные клиенту, правильно? SMS-сообщения не используются в какой-либо логике домена, правильно?
Итак, я бы попросил обработчик отправить SMS, а затем опубликовать другое событие, в котором говорится, что отправлено SMS, и обработчик события прослушивает отправленное SMS сообщение и материализует эту информацию в модели для чтения, чтобы клиент мог их просматривать.
Ответ 2
Вы можете использовать сагу или диспетчер процессов, как ее называет Microsoft. Это в основном слушает события, которые изменяют состояние саги, и выдает команды на основе логики состояния, реализованной в саге.
В вашем случае это будет две государственные саги, которые ждут как CustomerCreatedEvent, так и OrderCreatedEvent, и либо выдают команду на отправку sms, если у вас есть специализированный ограниченный контекст для связи, либо вызов службы инфраструктуры через интерфейс, чтобы отправить смс.
Здесь вы можете найти статью 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/