Dual-write problemi
Bir komut iki yere yazmak ister: (1) iş verisi (PostgreSQL), (2) integration event (RabbitMQ). Bunlar ayrı sistemler olduğu için atomik değildir:- DB commit oldu, ardından RabbitMQ publish başarısız → event kayboldu, downstream tutarsız.
- RabbitMQ publish oldu, ardından DB commit rollback → “olmayan” bir olay yayınlandı.
MassTransit EF Core Outbox kurulumu
Outbox, building block içinde değil host projede (DbContext’in olduğu yerde) kurulur —AddMassTransitRabbitMqEventBus bunu configureExtra callback’i ile kabul eder. Application/DependencyInjection.cs:
| Ayar | Değer | Anlamı |
|---|---|---|
UsePostgres() | — | Outbox/inbox tablolarını PostgreSQL üzerinde tutar. |
UseBusOutbox() | — | IPublishEndpoint.Publish artık RabbitMQ’ya değil, aynı transaction’da outbox_message’a yazar. |
QueryDelay | 1s | OutboxDeliveryService her saniye pending satırları tarar ve kuyruğa publish eder. |
DuplicateDetectionWindow | 30 dk | Inbox tarafında aynı mesaj iki kez gelirse atlanır (consumer idempotency güvencesi). |
messaging şeması tabloları
Tablolar iş aggregate’lerinden ayrı, ayrı bir şemada tutulur (MESSAGING_SCHEMA = "messaging"). OnModelCreating’de map edilir (DiyanetCleanArchitectureDbContext.cs):
| Tablo | Rol |
|---|---|
messaging.outbox_message | Gönderilmeyi bekleyen integration event’lerin gövdesi. |
messaging.outbox_state | Outbox delivery ilerlemesi/lock state’i. |
messaging.inbox_state | Consume edilen mesajların kaydı — duplicate detection bunun üzerinden çalışır. |
Bu tablolar EF Core migration’larıyla oluşur.
messaging şeması, business (public) şemasından ayrıdır; bkz. Veri Katmanı.Aynı transaction garantisi & lazy publish
UseBusOutbox aktifken IPublishEndpoint MassTransit pipeline’ında outbox wrapper’la sarılır. IEventBus’ı DbContext ctor’unda inject ettiğimiz için DI graph döngüye girebilir — host startup’ta (örn. MigrationService bir scope açıp DbContext resolve ettiğinde) soft deadlock gözlemlenir. Bu yüzden MassTransitEventBus IPublishEndpoint’i lazy resolve eder:
SaveEntitiesAsync → SaveChanges → domain event dispatch → PublishAsync → outbox satırı aynı transaction’a yazılır. DB commit fail olursa outbox da yazılmaz → exactly-once’a yaklaşır; RabbitMQ down ise satırlar outbox’ta birikir ve broker toparlanınca teslim edilir.
Inbox ile duplicate detection
Tüketici tarafındainbox_state, DuplicateDetectionWindow boyunca işlenen mesaj kimliklerini tutar. Aynı Id’li mesaj (retry/redelivery sonucu) tekrar gelirse atlanır. Bu, IntegrationEvent’in Id’sini serileştirmede koruması (bkz. Integration Events) ile birlikte çalışır. Yine de handler’ı idempotent yazmak en güvenli yoldur.
İlgili
RabbitMQ Topology
Outbox’tan çıkan mesajın gittiği exchange/queue.
Integration Events
IntegrationEvent base ve idempotency.Event Akışı
Command → SaveEntities → Outbox tam akışı.
Veri Katmanı
messaging şeması ve migration’lar.