Должны ли события домена вызываться внутри или вне транзакции?
В нашем приложении мы генерируем доменные события, когда что-то меняется в доменной модели. Некоторые из задач, выполняемых обработчиками событий, должны выполняться в той же транзакции, которая использовалась при возникновении события, другие задачи должны выполняться вне этой транзакции.
Например,
Когда Orderline добавляется к сущности Order, возникает событие домена OrderLineAdded, одно событие домена изменяет состояние модели домена (поэтому должно выполняться в той же транзакции), затем, когда транзакция завершена, пользовательский интерфейс должен быть обновлен.
Как бы вы подошли к этой проблеме?
- Вызовите два события: одно внутри транзакции, а другое вне транзакции.
- Вызвать событие внутри транзакции, но использовать обработчик события для отправки асинхронного запроса на обновление пользовательского интерфейса?
Вариант 1 выглядит сбивающим с толку, поскольку имена событий должны как-то сообщать о том, что они находятся в транзакции или вне транзакции, но в случае обработчиков варианта 2 события домена всегда должны предполагать, что они вызываются синхронно из транзакции.
Может быть, есть лучший подход?
2 ответа
У меня была похожая проблема. Модель предметной области публиковала события (используя технику, описанную здесь Уди Даханом). Затем я понял, что мои обработчики, связанные с пользовательским интерфейсом, вызываются, даже если что-то идет не так и транзакция откатывается позже.
Чтобы исправить это, я ввел в систему другую роль, другой тип обработчика событий. У меня есть ITransactionalEventHadneler
а также INonTransactionalEventHandler
, Первые были вызваны синхронно сразу в DomainEvents.Publish()
метод. Последние были поставлены в очередь для вызова, как только транзакция была зафиксирована (с использованием хуков System.Transactions). Решение работало нормально и было вполне читабельным и обслуживаемым.
Я думаю, что оба подхода могут быть хорошими, просто придерживайтесь одного и того же подхода в каждой части вашего кода:
- вам понадобятся два (или более) обработчика событий, один для контекста модели предметной области, которая находится внутри области транзакции, и другие для вспомогательных контекстов, таких как пользовательский интерфейс. Код вашего домена не должен заботиться о том, что делают другие части кода, просто уведомите их об изменении данных домена.
- Ваш метод обработчика событий кода домена может отправлять асинхронные события в пользовательский интерфейс или другие модули. События домена должны быть синхронными, в противном случае вам понадобятся двухфазные коммиты для сохранения транзакций.
Мне лично больше нравится вариант 2, потому что он поддерживает чистоту кода домена и, благодаря асинхронной связи, ядро и другие модули будут отделены, поэтому проблемы во внешних модулях не будут мешать работе ядра. С другой стороны, могут быть обстоятельства, когда вариант 1 является более выгодным.