В чем разница интеграционных и доменных событий
Что проверяют
Понимаете ли вы, где проходит граница ограниченного контекста и что меняется, когда событие её пересекает. Следом обычно спрашивают, как не потерять интеграционное событие, если транзакция в базе прошла, а брокер в этот момент недоступен.
Как отвечать
Разница не в названии, а в области действия и гарантиях доставки:
- доменное событие: факт внутри одного домена, например
OrderPaid. Публикуется и обрабатывается в том же процессе, часто в той же транзакции. В .NET агрегат копит события в спискеDomainEvents, а передSaveChangesили сразу после него диспетчер (свой или черезMediatRсINotification) раздаёт их обработчикам; - интеграционное событие: сообщение для других сервисов, например
OrderPaidIntegrationEvent. Уходит через брокер (RabbitMQ, Kafka, Azure Service Bus), обрабатывается асинхронно, в другом процессе и другой транзакции. Согласованность только итоговая (eventual consistency); - контракт: доменное событие меняется свободно вместе с моделью, интеграционное это публичный контракт, его версионируют и не ломают обратную совместимость;
- содержимое: доменное событие может нести ссылку на сущность, интеграционное несёт плоский DTO с идентификаторами и нужными данными, без типов из доменной модели.
Типичная цепочка: обработчик доменного события OrderPaid пишет интеграционное событие в таблицу outbox в той же транзакции, а фоновый процесс публикует его в брокер. Это паттерн Transactional Outbox, в MassTransit и NServiceBus он есть готовым. На принимающей стороне обработчик делают идемпотентным, потому что доставка обычно гарантируется по схеме «хотя бы один раз».
Где спотыкаются
Публикуют в брокер прямо из обработчика доменного события до коммита. Транзакция откатывается, а соседний сервис уже получил сообщение о том, чего не было.
Отправляют наружу доменные сущности целиком. Любое переименование поля ломает потребителей в других командах.
Путают доменные события с Event Sourcing. Доменные события прекрасно живут в обычной CRUD-модели, хранить состояние через журнал событий для этого не нужно.