Ответ
Событие следует отправлять в очередь после успешного завершения транзакции в базе данных. Это гарантирует консистентность: если транзакция откатится, событие о её успехе не будет отправлено.
Проблемный подход (отправка внутри транзакции):
using (var transaction = context.Database.BeginTransaction())
{
try
{
var order = new Order { /* ... */ };
context.Orders.Add(order);
context.SaveChanges(); // Сохраняем в БД
// ОПАСНО: событие отправляется в очередь (например, RabbitMQ)
_eventBus.Publish(new OrderCreatedEvent(order.Id));
// Если здесь произойдет сбой, транзакция откатится,
// но событие уже ушло и не может быть отозвано.
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
Корректный подход (отправка после транзакции):
using (var transaction = context.Database.BeginTransaction())
{
try
{
var order = new Order { /* ... */ };
context.Orders.Add(order);
context.SaveChanges();
transaction.Commit(); // Транзакция успешно завершена
}
catch
{
transaction.Rollback();
throw;
}
}
// Только теперь, гарантированно после фиксации транзакции, отправляем событие.
_eventBus.Publish(new OrderCreatedEvent(order.Id));
Исключение: Если брокер очередей (например, база данных, используемая как очередь) поддерживает распределённые транзакции (XA), то отправку можно выполнять внутри общей транзакции. Однако этот паттерн сложен и менее распространён в микросервисных архитектурах.