Modello di origine eventi
Anziché archiviare solo lo stato corrente dei dati in un database relazionale, archiviare la serie completa di azioni eseguite su un oggetto in un archivio di sola accodamento. L'archivio svolge la funzione di sistema di registrazione e consente di materializzare gli oggetti del dominio. Questo approccio può migliorare le prestazioni, la scalabilità e il controllo nei sistemi complessi.
Importante
L'origine eventi è un modello complesso che permea l'intera architettura e introduce compromessi per ottenere prestazioni, scalabilità e controllabilità migliori. Quando il sistema diventa un sistema di origine eventi, tutte le decisioni di progettazione future sono vincolate dal fatto che si tratta di un sistema di origine eventi. È previsto un costo elevato per eseguire la migrazione da o verso un sistema di origine eventi. Questo modello è più adatto per i sistemi in cui le prestazioni e la scalabilità sono requisiti principali. La complessità che l'origine eventi aggiunge a un sistema non è giustificato per la maggior parte dei sistemi.
Contesto e problema
La maggior parte delle applicazioni usa i dati e l'approccio tipico consiste nell'archiviare lo stato più recente dei dati in un database relazionale, inserendo o aggiornando i dati in base alle esigenze. Ad esempio, nel modello di creazione, lettura, aggiornamento ed eliminazione (CRUD) tradizionale, un processo di dati tipico consiste nel leggere i dati dall'archivio, apportare alcune modifiche e aggiornare lo stato corrente dei dati con i nuovi valori, spesso usando transazioni che bloccano i dati.
L'approccio CRUD è semplice e veloce per la maggior parte degli scenari. Tuttavia, nei sistemi a carico elevato, questo approccio presenta alcune sfide:
Prestazioni: man mano che il sistema viene ridimensionato, le prestazioni peggiorano a causa di conflitti per le risorse e i problemi di blocco.
Scalabilità: i sistemi CRUD sono sincroni e i blocchi di operazioni sui dati sugli aggiornamenti. Ciò può causare colli di bottiglia e una latenza più elevata quando il sistema è sotto carico.
Controllabilità: i sistemi CRUD archiviano solo lo stato più recente dei dati. A meno che non sia presente un meccanismo di controllo che registra i dettagli di ogni operazione in un log separato, la cronologia viene persa.
Soluzione
Il modello di origine eventi definisce un approccio alla gestione delle operazioni sui dati determinato da una sequenza di eventi, ognuno dei quali registrato in un archivio di solo accodamento. Il codice dell'applicazione genera eventi che descrivono in modo imperativo l'azione eseguita sull'oggetto . Gli eventi vengono in genere inviati a una coda in cui un processo separato, un gestore eventi, rimane in ascolto della coda e rende persistenti gli eventi in un archivio eventi. Ogni evento rappresenta una modifica logica all'oggetto, ad esempio AddedItemToOrder
o OrderCanceled
.
Gli eventi vengono salvati in modo permanente in un archivio eventi che svolge la funzione di sistema di registrazione (origine dati autorevole) dello stato corrente dei dati. I gestori eventi aggiuntivi possono restare in ascolto degli eventi a cui sono interessati ed eseguire un'azione appropriata. I consumer, ad esempio, possono iniziare le attività che si applicano alle operazioni sugli eventi di altri sistemi oppure eseguire qualsiasi altra azione associata necessaria per completare l'operazione. Tenere presente che il codice dell'applicazione che genera gli eventi è separato dai sistemi che sottoscrivono gli eventi.
In qualsiasi momento, è possibile che le applicazioni leggano la cronologia degli eventi. È quindi possibile usare gli eventi per materializzare lo stato corrente di un'entità riproducendo e utilizzando tutti gli eventi correlati a tale entità. Questo processo può verificarsi su richiesta per materializzare un oggetto dominio durante la gestione di una richiesta.
Poiché è relativamente costoso leggere e riprodurre eventi, le applicazioni in genere implementano viste materializzate, proiezioni di sola lettura dell'archivio eventi ottimizzate per l'esecuzione di query. Ad esempio, un sistema può mantenere una visualizzazione materializzata di tutti gli ordini dei clienti usati per popolare l'interfaccia utente. Quando l'applicazione aggiunge nuovi ordini, aggiunge o rimuove gli elementi nell'ordine o aggiunge informazioni sulla spedizione, vengono generati eventi e un gestore aggiorna la visualizzazione materializzata.
La figura mostra una panoramica del modello, incluse alcune implementazioni tipiche con il modello, tra cui l'uso di una coda, un archivio di sola lettura, l'integrazione di eventi con applicazioni e sistemi esterni e la riproduzione di eventi per creare proiezioni dello stato corrente di entità specifiche.
Flusso di lavoro
Di seguito viene descritto un flusso di lavoro tipico per questo modello:
- Il livello presentazione chiama un oggetto responsabile della lettura da un archivio di sola lettura. I dati restituiti vengono usati per popolare l'interfaccia utente.
- Il livello presentazione chiama i gestori dei comandi per eseguire azioni come creare un carrello o aggiungere un elemento al carrello.
- Il gestore dei comandi chiama l'archivio eventi per ottenere gli eventi cronologici per l'entità. Ad esempio, può recuperare tutti gli eventi del carrello. Tali eventi vengono riprodotti nell'oggetto per materializzare lo stato corrente dell'entità, prima di qualsiasi azione eseguita.
- La logica di business viene eseguita e vengono generati eventi. Nella maggior parte delle implementazioni, gli eventi vengono inseriti in una coda o in un argomento per separare i producer di eventi e i consumer di eventi.
- I gestori eventi sono in ascolto degli eventi a cui sono interessati ed eseguono l'azione appropriata per tale gestore. Alcune azioni tipiche del gestore eventi sono:
- Scrittura degli eventi nell'archivio eventi
- Aggiornamento di un archivio di sola lettura ottimizzato per le query
- Integrazione con sistemi esterni
Vantaggi del modello
Il modello di origine eventi offre i vantaggi seguenti:
Gli eventi non sono modificabili e possono essere memorizzati con un'operazione di solo accodamento. L'interfaccia utente, il flusso di lavoro o il processo che ha avviato un evento può continuare e le attività che gestiscono gli eventi possono essere eseguite in background. Questo processo, combinato con il fatto che non è presente alcuna contesa durante l'elaborazione delle transazioni, può notevolmente migliorare le prestazioni e la scalabilità per le applicazioni, soprattutto per il livello di presentazione.
Gli eventi sono oggetti semplici che descrivono alcune azioni che si sono verificate, insieme ai dati associati necessari per descrivere l'azione rappresentata dall'evento. Gli eventi non aggiornano direttamente un archivio dati. Vengono semplicemente registrati per essere gestiti al momento opportuno. L'uso degli eventi può semplificare l'implementazione e la gestione.
Gli eventi, in genere, sono facilmente comprensibili per gli esperti del settore, mentre la mancata corrispondenza di impedenza object-relational può rendere difficile la comprensione delle tabelle di database complesse. Le tabelle sono costrutti artificiali che rappresentano lo stato corrente del sistema, non gli eventi che si sono verificati.
La funzione di origine eventi contribuisce a impedire conflitti generati da aggiornamenti simultanei in quanto evita la necessità di aggiornare direttamente gli oggetti nell'archivio dati. La progettazione del modello di dominio, tuttavia, non prevede ancora la protezione da richieste che possono determinare uno stato incoerente.
L'archiviazione di sola accodamento degli eventi fornisce un audit trail che può essere usato per monitorare le azioni eseguite su un archivio dati. Può rigenerare lo stato corrente come viste materializzate o proiezioni riproducendo gli eventi in qualsiasi momento e può facilitare il test e il debug del sistema. Inoltre, il requisito di usare eventi di compensazione per annullare le modifiche può fornire una cronologia delle modifiche invertite. Questa funzionalità non sarebbe il caso se il modello archivia lo stato corrente. L'elenco di eventi può essere usato anche per analizzare le prestazioni dell'applicazione e per rilevare le tendenze di comportamento degli utenti. In alternativa, può essere usato per ottenere altre informazioni aziendali utili.
I gestori dei comandi generano eventi e le attività eseguono operazioni in risposta a tali eventi. È questa separazione tra le attività e gli eventi a garantire flessibilità ed estensibilità. Le attività sono a conoscenza del tipo di evento e dei dati relativi all'evento, ma non dell'operazione che l'evento ha generato. Ogni evento, inoltre, può essere gestito da più attività. Questo aspetto semplifica l'integrazione con altri servizi e sistemi che restano in ascolto solo di nuovi eventi generati dall'archivio eventi. Gli eventi che danno origine a nuovi eventi, tuttavia, tendono a essere di livello molto basso e può essere necessario generare eventi di integrazione specifici.
L'origine eventi viene comunemente combinata con il modello CQRS eseguendo le attività di gestione dei dati in risposta agli eventi e materializzando le viste dagli eventi archiviati.
Considerazioni e problemi
Prima di decidere come implementare questo modello, considerare quanto segue:
Coerenza finale : il sistema sarà coerente solo alla fine quando si creano viste materializzate o si generano proiezioni di dati riproducendo eventi. Si verifica un ritardo tra un'applicazione che aggiunge eventi all'archivio eventi come risultato della gestione di una richiesta, degli eventi pubblicati e dei consumer degli eventi che li gestiscono. Durante questo intervallo di tempo è possibile che siano giunti nell'archivio nuovi eventi che descrivono ulteriori modifiche alle entità. I clienti devono essere d'accordo con il fatto che i dati sono alla fine coerenti e il sistema deve essere progettato per tenere conto della coerenza finale in questi scenari.
Nota
Per altre informazioni sulla coerenza finale, vedere Nozioni di base sulla coerenza dei dati.
Eventi di controllo delle versioni: l'archivio eventi è l'origine permanente delle informazioni e quindi i dati dell'evento non devono mai essere aggiornati. L'unico modo per aggiornare un'entità o annullare una modifica consiste nell'aggiungere un evento di compensazione all'archivio eventi. Se lo schema (anziché i dati) degli eventi persistenti deve cambiare, ad esempio durante una migrazione, può essere difficile combinare eventi esistenti nell'archivio con la nuova versione. L'applicazione dovrà supportare le modifiche alle strutture degli eventi. Questa operazione può essere eseguita in diversi modi.
- Assicurarsi che i gestori eventi supportino tutte le versioni degli eventi. Può trattarsi di una sfida per gestire e testare. Ciò richiede l'implementazione di un timbro di versione in ogni versione dello schema di eventi per mantenere sia i formati precedenti che i nuovi eventi.
- Implementare un gestore eventi per gestire versioni di eventi specifiche. Può trattarsi di una sfida di manutenzione che potrebbe essere necessario apportare modifiche alle correzioni di bug tra più gestori. Ciò richiede l'implementazione di un timbro di versione in ogni versione dello schema di eventi per mantenere sia i formati precedenti che i nuovi eventi.
- Aggiornare gli eventi cronologici al nuovo schema quando viene implementato un nuovo schema. Ciò interrompe l'immutabilità degli eventi.
Ordinamento eventi : le applicazioni a thread multipli e più istanze di applicazioni potrebbero archiviare eventi nell'archivio eventi. La coerenza degli eventi nell'archivio eventi è di fondamentale importanza, così come l'ordine degli eventi che interessano un'entità specifica (l'ordine in cui vengono apportate le modifiche influisce sullo stato corrente dell'entità). L'aggiunta di un timestamp a ogni evento contribuisce a evitare problemi. Un'altra procedura comune è quella di annotare ogni evento generato da una richiesta con un identificatore incrementale. Se due azioni tentano contemporaneamente di aggiungere eventi per una stessa entità, l'archivio eventi può rifiutare un evento che corrisponde a un identificatore di entità e a un identificatore di evento esistenti.
Esecuzione di query sugli eventi : non esiste un approccio standard o meccanismi esistenti, ad esempio query SQL, per la lettura degli eventi per ottenere informazioni. Un flusso di eventi è l'unico tipo di dati che è possibile estrarre usando un identificatore di eventi come criterio. L'ID evento è generalmente associato a entità individuali. Lo stato corrente di un'entità può essere determinato solo riproducendo tutti gli eventi associati all'entità sullo stato originale dell'entità.
Costo della ricreazione dello stato per le entità : la lunghezza di ogni flusso di eventi influisce sulla gestione e l'aggiornamento del sistema. Se i flussi sono di grandi dimensioni, valutare l'opportunità di creare snapshot a intervalli specifici, ad esempio dopo un determinato numero di eventi. È possibile ottenere lo stato corrente dell'entità mediante lo snapshot e la riproduzione di tutti gli eventi che si sono verificati a partire da quel punto nel tempo. Per altre informazioni sulla creazione di snapshot di dati, vedere Replica snapshot secondaria primaria.
Conflitti : anche se l'origine eventi riduce al minimo la possibilità di aggiornare i dati in conflitto, l'applicazione deve comunque essere in grado di gestire incoerenze che derivano dalla coerenza finale e dalla mancanza di transazioni. Ad esempio, un evento che indica una riduzione dell'inventario delle scorte potrebbe arrivare nell'archivio dati mentre viene effettuato un ordine per l'articolo. Questa situazione comporta un requisito per riconciliare le due operazioni, consigliando il cliente o creando un ordine indietro.
Esigenza di idempotenza : la pubblicazione degli eventi potrebbe essere almeno una volta e quindi i consumer degli eventi devono essere idempotenti. In questo modo, non è necessario riapplicare l'aggiornamento descritto in un evento se l'evento viene gestito più volte. Più istanze di un consumer possono gestire e aggregare la proprietà di un'entità, ad esempio il numero totale di ordini effettuati. Solo uno deve avere esito positivo nell'incremento dell'aggregazione, quando si verifica un evento inserito nell'ordine. Anche se questo risultato non è una caratteristica chiave dell'origine degli eventi, è la solita decisione di implementazione.
Logica circolare : tenere presente gli scenari in cui l'elaborazione di un evento comporta la creazione di uno o più nuovi eventi poiché ciò può causare un ciclo infinito.
Quando usare questo modello
Usare questo modello negli scenari seguenti:
Quando si vuole acquisire l'intento, lo scopo o il motivo insito nei dati. Ad esempio, le modifiche apportate a un'entità cliente possono essere acquisite come una serie di tipi di eventi specifici, ad esempio Moved home, Closed account o Defunto.
Quando è essenziale ridurre al minimo o evitare completamente la presenza di conflitti tra gli aggiornamenti ai dati.
Quando si desidera registrare gli eventi che si verificano, per riprodurre gli eventi per ripristinare lo stato di un sistema, per eseguire il rollback delle modifiche o per mantenere una cronologia e un log di controllo. Ad esempio, quando un'attività prevede più passaggi, potrebbe essere necessario eseguire azioni per ripristinare gli aggiornamenti e quindi riprodurre alcuni passaggi per riportare i dati in uno stato coerente.
Quando si usano eventi. Si tratta di una caratteristica naturale del funzionamento dell'applicazione e richiede poco lavoro di sviluppo o implementazione aggiuntivo.
Quando è necessario separare il processo di input o aggiornare i dati dalle attività necessarie per applicare queste azioni. Questa modifica potrebbe essere quella di migliorare le prestazioni dell'interfaccia utente o di distribuire eventi ad altri listener che eseguono azioni quando si verificano gli eventi. Ad esempio, è possibile integrare un sistema di retribuzione con un sito Web per l'invio di spese. Gli eventi generati dall'archivio eventi in risposta agli aggiornamenti dei dati effettuati nel sito Web vengono utilizzati sia dal sito Web che dal sistema di retribuzione.
Quando si vuole che la flessibilità sia in grado di modificare il formato dei modelli materializzati e dei dati delle entità se i requisiti cambiano o, se usati con CQRS, è necessario adattare un modello di lettura o le visualizzazioni che espongono i dati.
Quando viene usato con CQRS e la coerenza finale è accettabile durante l'aggiornamento di un modello di lettura o l'impatto sulle prestazioni delle entità e dei dati riattivati da un flusso di eventi è accettabile.
Questo modello può non essere utile nelle situazioni seguenti:
Applicazioni che non richiedono iper scalabilità o prestazioni.
Domini semplici o di piccole dimensioni, con poca o alcuna logica di business o sistemi non di dominio che per natura interagiscono bene con i tradizionali meccanismi di gestione dati CRUD.
Sistemi in cui sono necessari coerenza e aggiornamenti in tempo reale alle visualizzazioni dei dati.
Sistemi in cui è presente solo una bassa occorrenza di aggiornamenti in conflitto ai dati sottostanti. Ad esempio, sistemi che in prevalenza aggiungono dati anziché aggiornarli.
Progettazione del carico di lavoro
Un architetto deve valutare il modo in cui il modello di origine eventi può essere usato nella progettazione del carico di lavoro per soddisfare gli obiettivi e i principi trattati nei pilastri di Azure Well-Architected Framework. Ad esempio:
Concetto fondamentale | Come questo modello supporta gli obiettivi di pilastro |
---|---|
Le decisioni di progettazione dell'affidabilità consentono al carico di lavoro di diventare resilienti a malfunzionamenti e di assicurarsi che venga ripristinato in uno stato completamente funzionante dopo che si verifica un errore. | A causa dell'acquisizione di una cronologia delle modifiche in un processo aziendale complesso, può facilitare la ricostruzione dello stato se è necessario ripristinare gli archivi di stato. - PARTIZIONAMENTO dei dati RE:06 - RE:09 Ripristino di emergenza |
L'efficienza delle prestazioni consente al carico di lavoro di soddisfare in modo efficiente le richieste tramite ottimizzazioni in termini di scalabilità, dati, codice. | Questo modello, in genere combinato con CQRS, una progettazione di dominio appropriata e snapshot strategico, può migliorare le prestazioni del carico di lavoro a causa delle operazioni di sola accodamento atomiche e l'evitare il blocco del database per scritture e letture. - Prestazioni dei dati PE:08 |
Come per qualsiasi decisione di progettazione, prendere in considerazione eventuali compromessi rispetto agli obiettivi degli altri pilastri che potrebbero essere introdotti con questo modello.
Esempio
Un sistema di gestione conferenze deve tenere traccia del numero di prenotazioni completate per una conferenza. In questo modo è possibile verificare se sono ancora disponibili postazioni, quando un potenziale partecipante tenta di effettuare una prenotazione. Il sistema può archiviare il numero totale di prenotazioni per una conferenza in almeno due modi:
Il sistema può memorizzare le informazioni sul numero totale di prenotazioni come entità separata in un database in cui sono contenute le informazioni di prenotazione. Per ogni nuova prenotazione effettuata o annullata, il sistema può incrementare o diminuire il numero di conseguenza. Questo approccio è molto semplice in teoria, ma può generare problemi di scalabilità nel caso in cui un numero elevato di partecipanti tenti di effettuare una prenotazione in un breve intervallo di tempo, ad esempio nell'ultimo giorno prima della chiusura delle prenotazioni.
Il sistema può memorizzare le informazioni su ogni nuova prenotazione aggiunta o annullata come eventi contenuti in un archivio eventi. Può quindi calcolare il numero di posti disponibili riproducendo questi eventi. Questo approccio può essere più scalabile grazie alla non modificabilità degli eventi. Il sistema deve solo riuscire a leggere dati dall'archivio eventi e, se necessario, aggiungere nuovi dati. Le informazioni sugli eventi rappresentati da ogni nuova prenotazione aggiunta o annullata non vengono mai modificate.
Il grafico seguente illustra come il sottosistema di prenotazione dei posti del sistema di gestione delle conferenze possa essere implementato usando il modello di origine eventi.
Di seguito è illustrata la sequenza di azioni per la prenotazione di due posti:
L'interfaccia utente esegue un comando per prenotare i posti per due partecipanti. Il comando viene gestito da un gestore di comando separato. Una parte della logica che viene separata dall'interfaccia utente diventa responsabile della gestione delle richieste inviate sotto forma di comandi.
Un'entità contenente informazioni su tutte le prenotazioni per la conferenza viene costruita eseguendo una query sugli eventi che descrivono prenotazioni e annullamenti. Questa entità è denominata
SeatAvailability
e è contenuta all'interno di un modello di dominio che espone metodi per l'esecuzione di query e la modifica dei dati nell'entità.Alcune ottimizzazioni da considerare usano snapshot (in modo che non sia necessario eseguire query e riprodurre l'elenco completo degli eventi per ottenere lo stato corrente dell'entità) e mantenere una copia memorizzata nella cache dell'entità in memoria.
Il gestore di comando richiama un metodo esposto dal modello di dominio per effettuare le prenotazioni.
L'entità
SeatAvailability
genera un evento contenente il numero di postazioni riservate. La volta successiva che l'entità applica gli eventi, tutte le prenotazioni verranno usate per calcolare il numero di postazioni rimaste.Il sistema aggiunge il nuovo evento all'elenco degli eventi presenti nell'archivio eventi.
Se un utente annulla una prenotazione, il sistema segue un processo simile, ma in questo caso il gestore di comando esegue un comando che genera un evento di annullamento della prenotazione e lo aggiunge all'archivio eventi.
Oltre a fornire più ambito per la scalabilità, l'uso di un archivio eventi fornisce anche una cronologia completa, o audit trail, delle prenotazioni e degli annullamenti per una conferenza. Gli eventi nell'archivio di eventi costituiscono il record esatto. Non è necessario rendere persistenti le aggregazioni in altro modo perché il sistema può riprodurre facilmente gli eventi e ripristinare lo stato in qualsiasi momento.
Passaggi successivi
Nozioni di base sulla coerenza dei dati. Quando si usa l'origine eventi con un archivio di lettura o viste materializzate separate, i dati di lettura non saranno immediatamente coerenti. I dati saranno invece coerenti solo alla fine. Questo articolo riepiloga i problemi relativi alla gestione della coerenza sui dati distribuiti.
Linee guida di partizionamento di dati. I dati vengono spesso partizionati quando si usa l'origine eventi per migliorare la scalabilità, ridurre i conflitti e ottimizzare le prestazioni. Questo articolo descrive come dividere i dati in partizioni discrete e i problemi che possono verificarsi.
Blog di Martin Fowler:
Risorse correlate
Per l'implementazione di questo modello possono risultare utili i modelli e le informazioni aggiuntive seguenti:
Modello di separazione di responsabilità per query e comandi (CQRS, Command and Query Responsibility Segregation). L'archivio di scrittura che costituisce un'origine permanente di informazioni per l'implementazione di un modello CQRS è spesso basato su un'implementazione del modello di origine eventi. Questo modello descrive come isolare in un'applicazione le operazioni di lettura dei dati dalle operazioni di aggiornamento dei dati tramite l'uso di interfacce separate.
Modello di vista materializzata. L'archivio dati usato in un sistema basato sull'origine eventi in genere non è adatto per eseguire query efficienti. In questo caso, l'approccio comune è quello di generare viste dati prepopolate a intervalli regolari o in caso di variazione dei dati.
Modello di transazioni di compensazione. I dati esistenti in un archivio di origine eventi non vengono aggiornati. Vengono invece aggiunte nuove voci che passano lo stato delle entità ai nuovi valori. Per invertire una modifica, vengono usate voci di compensazione perché non è possibile invertire la modifica precedente. In questo modello viene descritto come annullare le conseguenze prodotte da un'operazione precedente.