L'event sourcing ti chiede di smettere di sovrascrivere i tuoi dati. In un'applicazione CRUD tradizionale, aggiornare l'indirizzo di spedizione di un utente significa trovare la riga, cambiare il valore e scartare lo stato precedente. L'event sourcing segue un percorso diverso. Memorizza ogni modifica come un fatto immutabile: un utente ha creato un account, ha aggiornato il proprio indirizzo, ha verificato la propria email. Lo stato attuale del sistema non viene memorizzato direttamente. Viene calcolato riproducendo questi eventi in ordine.

Questo pattern risolve problemi reali. Gli audit trail diventano sottoprodotti gratuiti. Puoi ricostruire lo stato di un ordine in qualsiasi momento passato. Puoi fare debugging riproducendo esattamente ciò che è accaduto. Il compromesso è la complessità. Ora gestisci flussi di fatti, read model e consistenza eventuale invece di semplici righe.

PostgreSQL può fungere da tuo event store. La maggior parte dei team lo utilizza già. Offre transazioni ACID, JSONB per payload flessibili e strumenti di backup collaudati. Non è necessario introdurre Kafka, Cassandra o un database specializzato per l'event store fin dal primo giorno. Un'istanza standard di Postgres ti fornisce le garanzie transazionali e gli audit trail richiesti dall'event sourcing senza espandere l'impronta della tua infrastruttura.

La struttura di un Postgres Event Store

Lo schema può essere quasi imbarazzantemente semplice. Al minimo, hai bisogno di una tabella che aggiunga eventi e non li aggiorni mai in loco. Un design pratico è il seguente:

  • id come bigserial o UUID, che funge da ordinamento globale.
  • stream_id per raggruppare eventi correlati, come tutte le modifiche per un singolo utente o ordine.
  • event_type come testo semplice: UserEmailChanged, PaymentReceived, InventoryAdjusted.
  • payload come JSONB, che contiene i dati specifici per quell'occorrenza.
  • occurred_at con precisione del fuso orario.
  • version per stream, per imporre la concorrenza ottimistica.

Applichi la regola dell'immutabilità nel codice dell'applicazione o con un vincolo del database. Un indice unico su (stream_id, version) impedisce a due scrittori di aggiungere lo stesso numero di sequenza. Quando arriva un comando, leggi la versione corrente per quello stream, la incrementi e inserisci il nuovo evento all'interno di una transazione. Se un altro processo ti ha preceduto, il vincolo di unicità fallisce e tu riprovi o rifiuti il comando.

Considera un esempio concreto. Gestisci un sistema di inventario. Invece di una singola riga inventory con una colonna quantity, aggiungi eventi a una tabella inventory_events. ItemReceived aggiunge dieci unità. ItemReserved ne rimuove due. ItemShipped ne rimuove tre. Per conoscere la scorta attuale per SKU-42, sommi i payload degli eventi rilevanti. Per conoscere la scorta di tre giorni fa, sommi solo fino a quel timestamp. Se un bug nella tua logica di spedizione ha causato un errore martedì scorso, riproduci gli eventi attraverso il codice corretto per ottenere lo stato reale. Non puoi farlo con una semplice istruzione UPDATE.

Principi per evitare problemi

Costruire su Postgres non elimina la necessità di disciplina. I seguenti principi si applicano direttamente ai sistemi basati su event sourcing.

Mantieni la semplicità. La complessità uccide l'affidabilità. Resisti alla tentazione di costruire un framework di eventi generico prima di aver rilasciato un singolo flusso funzionante. Una singola tabella, una funzione di repository per aggiungere eventi e un worker di proiezione per costruire i read model sono sufficienti per dimostrare il valore. Aggiungi strumenti solo quando appare un problema concreto.

Inizia in piccolo. Non riscrivere l'intero monolito. Scegli un singolo bounded context in cui l'audit trail giustifichi il sovraccarico. Un registro di fatturazione, un motore di workflow o un sistema di prenotazione dell'inventario sono buoni candidati. Costruisci quel singolo pipeline end-to-end. Lascialo girare in produzione. Poi decidi se espanderti.

Definisci prima il successo. L'event sourcing non è un'architettura predefinita; è una soluzione a esigenze specifiche. Se il tuo requisito è solo tracciare lo stato più recente, il CRUD è più veloce ed economico. Se hai bisogno di query temporali, una rigorosa auditabilità o la capacità di ricostruire i read model su richiesta, allora gli eventi hanno senso. Sappi quale problema stai risolvendo prima di impegnarti.

Misura prima di ottimizzare. Un moderno PostgreSQL su hardware modesto può ingerire migliaia di eventi al secondo con una semplice tabella append-only. Non frammentare (shard) il tuo event store o introdurre schemi di partizionamento complessi finché il tuo monitoraggio non dimostra di aver esaurito le soluzioni più semplici. Indicizza i campi che interroghi. Ottimizza autovacuum per carichi di lavoro append-only. Poi misura di nuovo.

Test everything. Unit test your event handlers. Integration test the append path. Most importantly, test failure scenarios. What happens when two nodes append to the same stream simultaneously? What happens when a projection worker crashes mid-batch? Write tests that verify your optimistic concurrency and your at-least-once delivery guarantees.

Monitor in production. The events table will grow. Unlike a normalized schema where updates keep row counts flat, event sourcing is intentionally additive. Track table size, disk I/O, and the lag between your write model and your read model projections. Set alerts on projection lag before your users notice stale data.

Automate manual tasks. Manual schema changes, manual projection rebuilds, and manual event replay are ticking time bombs. Script your migration strategy. If you evolve an event schema, automate the upcasting or transformation so that old events can be replayed through new logic without human intervention at midnight.

Document your choices. Write down why specific streams exist, what each event type means, and when the team should choose CRUD over events. Event sourcing introduces cognitive load. Good documentation prevents a new engineer from guessing wrong and appending malformed events to a critical stream.

Traps That Waste Months

Event sourcing has a way of sounding elegant in a diagram and painful in production. Watch for these traps.

Underestimating complexity. Replaying events to rebuild state is conceptually simple. Managing idempotency, snapshotting for performance, and compensating transactions across aggregates is not. Break your system into small pieces. Solve one stream at a time.

Over-engineering. Do not provision a multi-node Kafka cluster because you imagine your event volume will one day require it. Postgres can carry you surprisingly far. Introduce new infrastructure only when you have a measured bottleneck that you cannot fix within your current setup.

Ignoring technical debt. Old event schemas linger forever. If you change your OrderCreated payload, you still have ten million historical events in the old shape. Track this debt. Plan backward-compatible readers or migration scripts. Do not let the burden of legacy events slow every new feature.

Choosing tools the team cannot run. The best architecture fails if only one person understands it. If your team knows Postgres and SQL, start there. If you introduce a specialized event store, make sure you have the operational expertise to debug it at two in the morning.

A Practical Starting Point

If this approach fits your problem, do not wait for a five-quarter rewrite. Start this week.

Audit your current systems. Find a place where an audit trail would solve real pain. Maybe it is an order state machine that currently maintains a single status column. Perhaps it is a financial ledger where balance corrections require manual database patches. Pick one gap where overwriting state has hurt you.

Then pick one small improvement you can make today. Create one event table. Model one stream. Write one projection that builds a read model from those events. Deploy it behind a feature flag. Watch it handle real traffic.

Event sourcing with PostgreSQL is not magic. It is a practical tool for teams that need to know not just where things are, but how they got there. Build slowly, measure honestly, and let your actual requirements guide the architecture.

Source: https://dev.to/therizwansaleem/event-sourcing-with-postgresql-using-the-database-as-an-event-store-2kd4