Modello di origine eventi

Prenotazioni

Anziché archiviare solo lo stato corrente dei dati in un dominio, usare un archivio di solo accodamento per registrare la serie completa di azioni eseguite sui dati. L'archivio svolge la funzione di sistema di registrazione e consente di materializzare gli oggetti del dominio. Contribuisce quindi a semplificare le attività in domini complessi, evitando la necessità di sincronizzare il modello dati e il dominio aziendale e migliorando le prestazioni, la scalabilità e la velocità di risposta. Può inoltre garantire coerenza ai dati transazionali e mantenere dati di cronologia e audit trail completi che consentono di eseguire azioni di compensazione.

Contesto e problema

La maggior parte delle applicazioni interagisce con i dati e, in genere, le applicazioni mantengono lo stato corrente dei dati lasciando agli utenti la facoltà di aggiornarli. Ad esempio, nel modello tradizionale di creazione, lettura, aggiornamento ed eliminazione (CRUD) 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 le transazioni che bloccano i dati.

L'approccio CRUD presenta tuttavia alcune limitazioni:

  • I sistemi CRUD eseguono operazioni di aggiornamento direttamente su un archivio dati. Queste operazioni possono rallentare le prestazioni e la velocità di risposta e possono limitare la scalabilità, a causa del sovraccarico di elaborazione richiesto.

  • In un dominio in collaborazione con molti utenti simultanei, i conflitti di aggiornamento sono più frequenti poiché le operazioni di aggiornamento vengono eseguite su un unico elemento di dati.

  • A meno che non sia presente un altro 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 invia una serie di eventi che descrivono in modo imperativo ogni azione eseguita sui dati nell'archivio eventi, in cui sono salvati in modo permanente. Ogni evento rappresenta un set di modifiche ai dati (ad esempio AddedItemToOrder).

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. L'archivio eventi pubblica in genere questi eventi in modo che i consumer vengano informati e, se necessario, possano gestirli. 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.

Gli usi tipici degli eventi pubblicati dall'archivio eventi prevedono la gestione di viste materializzate di entità come azioni nell'applicazione e per l'integrazione con sistemi esterni. Un sistema, ad esempio, può gestire una vista materializzata di tutti gli ordini clienti usata per popolare parti dell'interfaccia utente. L'applicazione aggiunge nuovi ordini, aggiunge o rimuove gli elementi nell'ordine e aggiunge informazioni sulla spedizione. Gli eventi che descrivono queste modifiche possono essere gestiti e usati per aggiornare la vista materializzata.

In qualsiasi momento, è possibile che le applicazioni leggino la cronologia degli eventi. È quindi possibile usarlo per materializzare lo stato corrente di un'entità riproducendo e consumando tutti gli eventi correlati a tale entità. Questo processo può verificarsi su richiesta per materializzare un oggetto di dominio durante la gestione di una richiesta. In alternativa, il processo si verifica tramite un'attività pianificata in modo che lo stato dell'entità possa essere archiviato come visualizzazione materializzata, per supportare il livello di presentazione.

La figura illustra una panoramica del modello, incluse alcune delle opzioni relative all'uso del flusso eventi, tra cui la creazione di una vista materializzata, l'integrazione di eventi con applicazioni e sistemi esterni e la riproduzione di eventi per poter creare proiezioni dello stato corrente di specifiche entità.

Panoramica ed esempio del modello di origine eventi

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ò migliorare notevolmente le prestazioni e la scalabilità per le applicazioni, soprattutto per il livello di presentazione o l'interfaccia utente.

  • Gli eventi sono oggetti semplici che descrivono alcune azioni che si sono verificate, insieme a tutti i 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 di 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 accodamento degli eventi fornisce un audit trail che può essere usato per monitorare le azioni eseguite in un archivio dati. Può rigenerare lo stato corrente come viste materializzate o proiezioni riproponendo gli eventi in qualsiasi momento e può essere utile per testare e eseguire 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 degli eventi può essere usato anche per analizzare le prestazioni dell'applicazione e rilevare le tendenze del comportamento degli utenti. In alternativa, può essere usato per ottenere altre informazioni aziendali utili.

  • L'archivio eventi genera eventi e le attività eseguono operazioni in risposta agli 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.

Il modello di origine eventi viene spesso combinato con il modello CQRS per l'esecuzione di attività di gestione dati in risposta agli eventi e per la materializzazione delle viste a partire da eventi archiviati.

Considerazioni e problemi

Prima di decidere come implementare questo modello, considerare quanto segue:

Il sistema acquisirà la coerenza finale solo durante la creazione di viste materializzate o la generazione di proiezioni di dati tramite la riproduzione di eventi. C'è 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à. 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.

L'archivio eventi è un'origine permanente di informazioni e i dati dell'evento, quindi, non dovrebbero essere mai aggiornati. L'unico modo per aggiornare un'entità in modo da annullare una modifica è quello di aggiungere un evento di compensazione all'archivio eventi. Se deve essere modificato il formato, anziché i dati, degli eventi persistenti, ad esempio durante una migrazione, potrebbe essere difficile combinare eventi esistenti nell'archivio con la nuova versione. È possibile che sia necessario scorrere tutti gli eventi per modificarli in modo da uniformarli al nuovo formato oppure aggiungere nuovi eventi che usano il nuovo formato. Valutare l'opportunità di usare uno stamp di versione in ogni versione dello schema eventi per mantenere sia il nuovo che il vecchio formato.

È possibile che nell'archivio eventi vengano archiviati eventi anche da parte di applicazioni multithread e da più istanze delle applicazioni. 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.

Non esiste un approccio standard o un meccanismo esistente, ad esempio query SQL, per leggere gli eventi in modo da 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à.

La lunghezza di ogni flusso di eventi influisce sulle modalità di gestione e di 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 subordinata primaria.

Anche se l'origine eventi riduce al minimo il rischio di conflitti tra gli aggiornamenti ai dati, l'applicazione deve comunque essere in grado di gestire eventuali incoerenze derivanti 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 inserito un ordine per tale elemento. Questa situazione comporta un requisito per riconciliare le due operazioni, consigliando il cliente o creando un ordine indietro.

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 mantenere e aggregare la proprietà di un'entità, ad esempio il numero totale di ordini inseriti. È necessario incrementare l'aggregazione solo quando si verifica un evento inserito nell'ordine. Anche se questo risultato non è una caratteristica fondamentale dell'origine degli eventi, è la consueta decisione di implementazione.

L'archiviazione eventi selezionata deve supportare il carico dell'evento generato dall'applicazione.
Tenere presente gli scenari in cui l'elaborazione di un evento comporta la creazione di uno o più nuovi eventi perché 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 Spostamento a casa, account chiuso 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 di nuovo 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 ripristinare i dati in uno stato coerente.

  • Quando si usano eventi. È una caratteristica naturale dell'operazione dell'applicazione e richiede un piccolo sforzo 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 agiscono quando si verificano gli eventi. Ad esempio, è possibile integrare un sistema di pagamento con un sito Web di invio spese. Gli eventi generati dall'archivio eventi in risposta agli aggiornamenti dei dati effettuati nel sito Web verranno utilizzati dal sito Web e dal sistema di pagamento.

  • 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, quando vengono 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 mentre un modello di lettura viene aggiornato o l'impatto sulle prestazioni delle entità e dei dati reidratati da un flusso di eventi è accettabile.

Questo modello può non essere utile nelle situazioni seguenti:

  • 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.

  • I sistemi in cui i percorsi di controllo, la cronologia e le funzionalità per eseguire il rollback e le azioni di riproduzione non sono necessari.

  • I sistemi in cui è presente solo un'occorrenza bassa degli aggiornamenti in conflitto ai dati sottostanti. Ad esempio, sistemi che in prevalenza aggiungono dati anziché aggiornarli.

Esempio

Un sistema di gestione conferenze deve tenere traccia del numero di prenotazioni completate per una conferenza. In questo modo può verificare se ci sono posti ancora disponibili, 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.

Uso del modello di origine eventi per acquisire informazioni sulle prenotazioni in un sistema di gestione delle conferenze

Di seguito è illustrata la sequenza di azioni per la prenotazione di due posti:

  1. 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.

  2. Viene creata un'aggregazione contenente informazioni su tutte le prenotazioni per la conferenza tramite l'esecuzione di query sugli eventi che descrivono le nuove prenotazioni e gli annullamenti. L'aggregazione prende il nome di SeatAvailability ed è contenuta in un modello di dominio che espone i metodi per l'esecuzione di query e la modifica dei dati inclusi nell'aggregazione.

    Alcune ottimizzazioni da considerare usano snapshot (in modo che non sia necessario eseguire query e riprodurre l'elenco completo di eventi per ottenere lo stato corrente dell'aggregazione) e mantenere una copia memorizzata nella cache dell'aggregazione in memoria.

  3. Il gestore di comando richiama un metodo esposto dal modello di dominio per effettuare le prenotazioni.

  4. L'aggregazione SeatAvailability registra un evento contenente il numero di posti prenotati. La volta successiva in cui l'aggregazione applica un evento verranno usate tutte le prenotazioni per calcolare il numero di posti rimanenti.

  5. 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 un 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 qualsiasi altro modo perché il sistema può facilmente riprodurre gli eventi e ripristinare lo stato in qualsiasi momento.

Altre informazioni su questo esempio sono disponibili in Introducing Event Sourcing (Introduzione all'origine eventi).

Passaggi successivi

Per l'implementazione di questo modello possono risultare utili i modelli e le informazioni aggiuntive seguenti:

  • Modello di separazione delle responsabilità dei comandi e delle query (CQRS). 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 visualizzazione materializzato. 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 transazione 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, le voci di compensazione vengono usate perché non è possibile invertire la modifica precedente. In questo modello viene descritto come annullare le conseguenze prodotte da un'operazione precedente.