День 1637. #DDD
Использование Доменных Событий для Построения Слабосвязанных Систем. Окончание
Начало
Публикация с помощью EF Core
Поскольку EF Core использует паттерн Единица Работы, вы можете использовать его для сбора всех доменных событий в текущей транзакции и их публикации.
Можно либо переопределить метод SaveChangesAsync, либо использовать перехватчик:
public class ApplicationDbContext : DbContext
{
public override async Task<int> SaveChangesAsync(
CancellationToken ct = default)
{
var result = await
base.SaveChangesAsync(ct);
await PublishDomainEventsAsync();
return result;
}
}
Важное решение: публиковать доменные события до или после вызова SaveChangesAsync (сохранения данных в БД)?
До:
- события являются частью той же транзакции,
- немедленная согласованность данных.
После:
- события – отдельная транзакция,
- конечная согласованность, т.к. сообщения обрабатываются после исходной транзакции,
- риск несогласованности БД, т.к. обработка события может привести к сбою.
Можно решить эту проблему с помощью паттерна исходящих сообщений (Outbox), когда изменения в БД и изменения, сделанные в доменном событии сохраняются (в виде исходящих сообщений) в одной транзакции.
Метода PublishDomainEventsAsync:
private async Task PublishDomainEventsAsync()
{
var events = ChangeTracker
.Entries<Entity>()
.Select(e => e.Entity)
.SelectMany(ent =>
{
var evnts = ent.GetDomainEvents();
ent.ClearDomainEvents();
return evnts;
})
.ToList();
foreach (var ev in events)
await _mediator.Publish(ev);
}
Обработка
Нужно определить класс, реализующий INotificationHandler<T>, где T – тип доменного события. Обработчик доменного события CourseCompleted ниже публикует CourseCompletedIntegrationEvent для уведомления других систем.
public class CourseCompletedDomainEventHandler
: INotificationHandler<CourseCompleted>
{
private readonly IBus _bus;
public CourseCompletedDomainEventHandler(IBus bus)
{
_bus = bus;
}
public async Task Handle(
CourseCompleted de,
CancellationToken ct)
{
await _bus.Publish(
new CourseCompletedIntegrationEvent(de.CourseId),
ct);
}
}
Итого
- Доменные события могут помочь построить слабосвязанную систему, отделяя основную логику домена от побочных эффектов, которые можно обрабатывать асинхронно.
- Для реализации доменных событий, можно использовать библиотеки EF Core и MediatR.
- Нужно решить, когда публиковать доменные события: до или после сохранения изменений в БД.
- Публикация доменных событий после сохранения изменений в БД и паттерн Outbox для добавления транзакционных гарантий обеспечивают окончательную согласованность, но также бОльшую надёжность.
Источник: https://www.milanjovanovic.tech/blog/using-domain-events-to-build-loosely-coupled-systems