Outbox Pattern in .NET
#dotnet #EFCore #PostgreSQL
✅
Проблему двойной записи (БД + брокер) решают, сводя её к одной ACID-транзакции: сообщение пишется в таблицу outbox вместе с бизнес-данными, а отдельный воркер публикует его позже.
📈
Вместо bus.Publish() в обработчике команды — SaveChangesAsync, который сохраняет и заказ, и OrderPlaced в одной транзакции; событие и данные либо оба durable, либо оба исчезают.
🔒 FOR UPDATE SKIP LOCKED (в SQL Server — READPAST) даёт горизонтально масштабировать publisher: реплики разбирают непересекающиеся строки. Ошибка публикации помечает строку через per-message try/catch, чтобы одно poison-сообщение не блокировало весь batch.
⚙️ Outbox даёт at-least-once; effectively exactly-once получается только в связке с идемпотентными консьюмерами — таблица processed_messages с проверкой по MessageId перед обработкой.
Читаем:
В статье — рабочий код на .NET 10 (EF Core SaveChanges-интерцептор для доменных событий, BackgroundService-publisher), список типичных ошибок (полинг каждые 100мс, отсутствие индекса на ProcessedOnUtc, забытая транзакция на стороне publisher) и разбор случаев, когда outbox не нужен (CDC/Debezium, Kafka, эфемерные уведомления).
https://thecodeman.net/posts/outbox-pattern-in-dotnet
@aStateOfNet
#dotnet #EFCore #PostgreSQL
✅
Проблему двойной записи (БД + брокер) решают, сводя её к одной ACID-транзакции: сообщение пишется в таблицу outbox вместе с бизнес-данными, а отдельный воркер публикует его позже.
📈
Вместо bus.Publish() в обработчике команды — SaveChangesAsync, который сохраняет и заказ, и OrderPlaced в одной транзакции; событие и данные либо оба durable, либо оба исчезают.
🔒 FOR UPDATE SKIP LOCKED (в SQL Server — READPAST) даёт горизонтально масштабировать publisher: реплики разбирают непересекающиеся строки. Ошибка публикации помечает строку через per-message try/catch, чтобы одно poison-сообщение не блокировало весь batch.
⚙️ Outbox даёт at-least-once; effectively exactly-once получается только в связке с идемпотентными консьюмерами — таблица processed_messages с проверкой по MessageId перед обработкой.
Читаем:
В статье — рабочий код на .NET 10 (EF Core SaveChanges-интерцептор для доменных событий, BackgroundService-publisher), список типичных ошибок (полинг каждые 100мс, отсутствие индекса на ProcessedOnUtc, забытая транзакция на стороне publisher) и разбор случаев, когда outbox не нужен (CDC/Debezium, Kafka, эфемерные уведомления).
https://thecodeman.net/posts/outbox-pattern-in-dotnet
@aStateOfNet