Transactional Outbox, Inbox e Idempotenza: eventi affidabili
Outbox, Inbox e Idempotenza rendono affidabili i flussi event-driven senza richiedere transazioni distribuite tra database e broker.
Il problema del dual write
Supponiamo che ADT salvi un ricovero e poi pubblichi PatientAdmitted. Se il database fa commit ma il processo muore prima del publish, il ricovero esiste ma nessun consumer riceve l'evento. Se si pubblica prima e poi fallisce il commit, i consumer reagiscono a un fatto che non esiste. Due sistemi separati non condividono automaticamente la stessa transazione.
Transactional Outbox
La soluzione è scrivere nella stessa transazione locale sia la modifica di dominio sia un record outbox. Dopo il commit, un publisher indipendente legge l'outbox e invia il messaggio al broker. Solo dopo conferma marca l'evento come pubblicato o avanza il proprio offset.
Sequenza
BEGIN transaction.
Aggiorna l'aggregate, per esempio Encounter.
Inserisce OutboxEvent con eventId, type, aggregateId, payload e timestamp.
COMMIT: dominio ed evento diventano persistenti insieme.
Outbox publisher invia l'evento a RabbitMQ.
Il messaggio può essere ritentato finché non viene confermato.
Perché serve l'idempotenza
La consegna affidabile è normalmente at-least-once: lo stesso evento può arrivare più di una volta. Il consumer non deve quindi assumere unicità della consegna. Deve rendere idempotente l'effetto o memorizzare gli eventId già processati.
Inbox Pattern
Un consumer può registrare in una tabella inbox l'identificatore del messaggio dentro la stessa transazione con cui applica la modifica locale. Se riceve di nuovo lo stesso eventId, riconosce la ridelivery e non ripete l'effetto.
Idempotency Key per API
Lo stesso principio vale per comandi HTTP a rischio di retry, per esempio creazione ordine o richiesta documento. Il client invia una idempotency key; il server restituisce lo stesso risultato a richieste duplicate invece di creare due risorse.
Cose da non confondere
Outbox garantisce che l'intenzione di pubblicare sia persistita con la transazione di dominio.
Inbox/dedup protegge il consumer dalle rideliveries.
Retry decide quando riprovare; non sostituisce l'idempotenza.
Exactly-once end-to-end è spesso un'illusione costosa: progettare effetti idempotenti è più robusto.
