In che modo Event Sourcing ti aiuta a tenere traccia dello stato della tua applicazione

L'event sourcing è un'architettura software in cui le modifiche allo stato dell'applicazione vengono acquisite come una serie di eventi memorizzati perpetuamente. Mentre la maggior parte dei sistemi espone solo il proprio stato attuale, l'origine eventi crea una registrazione completa di tutti gli stati precedenti.
I Fondamentali
L'origine degli eventi viene spesso utilizzata con sistemi basati su processi lineari o temporali. Un'app per gli ordini in negozio potrebbe trasferire le transazioni tra gli stati in sospeso, approvato e spedito. La transizione tra ogni stato è un evento distinto nel ciclo di vita di quell'ordine.
Questo sistema potrebbe essere modellato utilizzando un database relazionale tradizionale:
order_id | order_status
---------|-------------
1000 | APPROVED
Nella maggior parte dei sistemi, però, conoscere lo stato attuale non è sufficiente. Vuoi anche vedere quando l'ordine è stato approvato. Potremmo farlo aggiungendo campi extra:
order_id | order_created_time | order_approved_time | order_shipping_time
---------|--------------------|---------------------|---------------------
1000 | 2021-04-30 | 2021-05-01 | NULL
Ora è chiaro che l'ordine è stato creato il 30 aprile e approvato il 1° maggio; la sua spedizione è ancora in sospeso. Per molte applicazioni, questa struttura funziona bene. Tuttavia, può diventare restrittivo man mano che vengono aggiunti più stati.
Diamo ora un'occhiata a questo esempio ristrutturato per utilizzare l'origine eventi:
event_id | event_order_id | event_type | event_timestamp
---------|---------------------|----------------|----------------
2 | 1000 | order.approved | 2021-05-01
1 | 1000 | order.created | 2021-04-30
Le transizioni di stato dell'ordine sono state separate in un registro eventi dedicato. Con questo modello, è banale aggiungere un nuovo tipo di evento in futuro. Se un cliente ha bisogno di annullare un ordine, possiamo scrivere un evento order.cancelled
nel log.
Event sourcing potrebbe anche essere utilizzato per tenere traccia dei dati sull'ordine stesso. Potresti aggiungere tipi di evento per order.apply_discount
o order.process_refund
. Puoi registrare un evento in qualsiasi momento, creando un nuovo stato per l'ordine mantenendo accessibili gli stati precedenti.
Ricostruire lo stato di un oggetto
Puoi determinare lo stato corrente di un oggetto recuperando tutti gli eventi ad esso correlati. Se vuoi sapere se un ordine è stato approvato, controlla se la sua raccolta di eventi contiene un evento order.approved
.
Puoi ricostruire gli stati storici esattamente allo stesso modo. Se hai bisogno di conoscere lo stato dell'ordine in una data particolare, recupera tutti gli eventi registrati fino a quel giorno o prima.
L'origine degli eventi è un modello incrementale che traccia automaticamente la cronologia di un oggetto. Ogni modifica allo stato dell'oggetto deve essere acquisita come evento con timestamp. Quando utilizzi l'origine eventi, hai il controllo automatico della versione e un registro di controllo completo delle transizioni di stato.
Se hai bisogno di ricostruire la cronologia di un oggetto, puoi scartare qualsiasi evento creato dopo una determinata data. Immagina che l'account di un utente sia stato compromesso da un attore malintenzionato che ha intrapreso varie azioni per suo conto. In un sistema completamente basato sugli eventi, è possibile eliminare qualsiasi evento collegato a quell'utente nel periodo di tempo discutibile. Ciò ripristinerebbe il loro account in buono stato.
Per preservare questa capacità, devi assicurarti che gli eventi siano immutabili. Una volta che un evento è stato registrato, non dovresti mai modificarne le proprietà. Se un evento necessita di modifiche, devi registrare un altro evento per rendere effettive le modifiche.
L'immutabilità del sistema significa che puoi usarlo in sicurezza per viaggiare indietro nel tempo e ricostruire stati storici. Se si desidera riprodurre il database com'era due anni fa, è possibile eliminare tutti gli eventi registrati nel frattempo.
Un altro vantaggio dell'event sourcing è una maggiore visibilità sullo stato del sistema. Se un utente segnala un bug, puoi clonare il database, eliminare nuovi eventi e ripetere i passaggi da un punto noto. L'analisi degli eventi registrati può aiutarti a individuare i bug nella tua base di codice.
Sourcing di eventi nel mondo reale
L'approvvigionamento di eventi ha la reputazione di essere complesso, ingombrante e troppo complicato. Storicamente, l'event sourcing è stato legato ad applicazioni con severi requisiti di controllo e una comprovata necessità di riprodurre gli eventi.
Questo non deve essere il caso però. Se sei uno sviluppatore esperto, probabilmente in passato hai implementato qualcosa di simile all'approvvigionamento di eventi. Qualsiasi sistema che tenga traccia degli eventi, come i tentativi di accesso degli utenti, le visite alle pagine del sito Web o le fasi di elaborazione degli ordini, gravita naturalmente verso un approccio basato sugli eventi, anche se non ne stai implementando uno intenzionalmente.
Un'implementazione deliberata dell'origine degli eventi nel codice può essere ingombrante. Gli sviluppatori di solito presumono che i dati recuperati dai database siano una rappresentazione accurata dello stato attuale di un oggetto. Con l'event sourcing, i dati che recuperi hanno poco valore da soli. Devi riprodurre gli eventi nella tua base di codice per creare una rappresentazione dello stato.
L'origine degli eventi può dar luogo a costi generali delle prestazioni. Nell'esempio precedente, la creazione di una rappresentazione completa dell'oggetto ordine ora richiede il recupero di molti più dati. Nel modello tradizionale, un'unica selezione fornisce tutti i dati associati all'ordine.
Ci sono anche altri aspetti negativi. Nel caso in cui qualcosa vada storto, l'origine degli eventi può essere più difficile da risolvere. Non è possibile scrivere una patch rapida o correggere manualmente il database. Dovrai trasferire con garbo gli schemi degli eventi assicurandoti che la cronologia storica rimanga intatta.
Combinazione di Event Sourcing e CQRS
L'origine degli eventi è comunemente combinata con CQRS (Command Query Responsibility Segregation). Questo modello sostiene la separazione dei comandi (che scrivono dati) dalle query (che leggono dati).
L'uso dell'origine eventi richiede un certo grado di CQRS. I dati vengono scritti nel database tramite eventi. Il modello di scrittura è quindi estremamente semplice: è un log di sola aggiunta unidirezionale degli eventi che si verificano. Gli eventi sono una manifestazione dei comandi descritti da CQRS.
Il modello di recupero dei dati è completamente indipendente. È possibile utilizzare il sistema di query più appropriato per recuperare gli eventi e inserirli nello stato corrente dell'applicazione. I comandi (eventi) portano il sistema in un nuovo stato; le query espongono gli aspetti di stato richiesti dall'applicazione.
In altre parole, i modelli di lettura e scrittura non hanno alcuna consapevolezza di come funzionano gli altri. Le entità nella tua base di codice (come un ordine) sono memorizzate come una sequenza di eventi cronologici. Gli eventi archiviati non possono essere reidratati nell'entità a cui appartengono senza sapere come viene derivato lo stato di tale entità. Tale conoscenza è implementata all'interno del modello di lettura (query), che riporta i dati nella tua applicazione.
Questa caratteristica consente di semplificare il livello di persistenza a un singolo inserimento di record per ciascuna operazione. I dati archiviati non devono rappresentare con precisione alcuna entità particolare poiché il modello di query li manipolerà in seguito. Ciò contrasta con un database relazionale tradizionale in cui i campi della tabella spesso si allineano strettamente con le proprietà degli oggetti nella base di codice.
L'output del modello di query è noto come proiezione. Una proiezione è una rappresentazione dei dati utilizzando una prospettiva diversa rispetto al relativo sistema di archiviazione. Quando si utilizza l'origine eventi, i dati vengono archiviati come un flusso di eventi ma proiettati in una rappresentazione dello stato corrente dell'applicazione. Tale rappresentazione è senza stato, immutabile e idempotente: la creazione della rappresentazione non modifica in alcun modo i dati dell'evento sottostante.
Una singola proiezione potrebbe dover interagire con diversi tipi di evento. Le proiezioni esaminano i dati in forma aggregata, in un modo che abbia senso per le funzioni dell'applicazione. Nel nostro esempio precedente, una proiezione Order potrebbe generare un oggetto contenente le proprietà CreatedDate
, ApprovedDate
e ShippedData
esaminando gli eventi associati all'oggetto dell'ordine .
Conclusione
L'approvvigionamento di eventi è un'architettura software specializzata con supporto innato per la responsabilità, la cronologia e la ricreazione dello stato. Il modello può semplificare notevolmente l'implementazione di applicazioni in cui queste qualità sono desiderabili.
L'origine degli eventi può essere utile anche in altri scenari, sebbene si possano riscontrare rendimenti decrescenti. L'adozione dell'event sourcing richiede agli sviluppatori di implementare il codice per determinare lo stato corrente, aggiungendo costi generali che non si verificano nei sistemi che memorizzano solo lo stato corrente nel proprio database.
L'origine degli eventi si combina al meglio con altre tecniche di architettura software come CQRS, il modello dell'osservatore e l'eventuale coerenza. Non è necessario utilizzare l'event sourcing nell'intera app: spesso, i singoli sottomoduli trarranno vantaggio dall'event sourcing mentre la maggior parte del tuo progetto si attiene a un database relazionale tradizionale.