Gestione degli eventi, riproduzione e controllo delle versioni

6

Sto progettando un sistema che utilizza Event Sourcing, CQRS e microservices. Sono portato a capire che questo non è uno schema insolito. Una caratteristica chiave del servizio deve essere la capacità di reidratare / ripristinare da un sistema di registrazione. Microservices produrrà comandi e query su un MQ (Kafka). Altri microservizi risponderanno (eventi). I comandi e le query verranno mantenuti su S3 per scopi di controllo e ripristino.

L'attuale processo di pensiero era che, ai fini del ripristino del sistema, potevamo estrarre il registro eventi da S3 e semplicemente reinserirlo in Kafka.

Tuttavia, ciò non riesce a riconoscere i cambiamenti nei consumatori dei produttori e nel tempo. Il controllo delle versioni a livello di comando / query sembra andare in qualche modo verso la risoluzione del problema, ma non riesco a comprendere i consumatori di versioni in modo tale da poterlo applicare quando un comando, durante un ripristino, viene ricevuto ed elaborato, è esattamente lo stesso [versione del] codice che sta eseguendo l'elaborazione poiché era la prima volta che il comando è stato ricevuto.

Ci sono dei pattern che posso usare per risolvere questo? Qualcuno è a conoscenza di altri sistemi che pubblicizzano questa funzione?

EDIT: aggiunta di un esempio.

Un "acquirente" invia una "domanda" a un "venditore" sul mio sito di aste. Il flusso appare come segue: UI -> Web App: POST /question {:text text :to seller-id :from user-id} Web App -> MQ: SEND {:command send-question :args [text seller-id user-id]} MQ -< Audit: <command + args appended to log in S3> MQ -< Questions service: - Record question in DB - Email seller 'You have a question'

Ora, a seguito di un nuovo requisito aziendale, aggiusto il consumatore del "Servizio domande", per mantenere un conteggio di tutte le domande non lette. Lo schema del DB è cambiato. Non abbiamo avuto la nozione di se una domanda fosse o meno letta dal venditore, fino ad ora. L'ultima riga diventa:

MQ -< Questions service: - Record question in DB - Email seller 'You have a question' - Increment 'unread questions count'

Due comandi sono problemi, uno prima della modifica, uno dopo la modifica. Il 'numero di domande non lette' è uguale a 1.

Il sistema si arresta in modo anomalo. Abbiamo ripristinato ripetendo i comandi tramite il nuovo codice. Alla fine del ripristino, il nostro 'conteggio di domande non lette' è uguale a 2. Anche se, in questo esempio forzato, il risultato non è una catastrofe, lo stato che è stato ripristinato è non ciò che precedentemente era .

    
posta acron 15.02.2016 - 18:50
fonte

2 risposte

13

In primo luogo, è importante comprendere ed essere in grado di sfruttare la differenza tra Comandi e Eventi.

Poiché questa domanda indica succintamente, Comandi sono cose che vorremmo accadere e Eventi sono cose che sono già accadute. Un comando non comporta necessariamente un evento significativo nel sistema, ma di solito lo fa. Ad esempio, un comando send message può essere rifiutato, nel qual caso non si verifica alcun evento (in genere un errore non sarebbe considerato un evento in questo senso, sebbene potremmo ancora scegliere di registrarlo in un log diagnostico). Ora, se viene accettato il comando send message , si verifica l'evento message sent e i dettagli dell'evento possono descrivere il mittente, il destinatario e il contenuto.

Quando parliamo dello stato del sistema, in realtà stiamo discutendo non un culmine di comandi, ma di eventi. Solo gli eventi possono causare un cambiamento di stato nel sistema. Per trarre da un esempio di vita, supponiamo di andare al supermercato Publix locale e acquistare un biglietto della lotteria della Florida. Il comando era "Acquista biglietto" e l'evento era "Biglietto emesso". Il mio prossimo comando è quindi la lotteria per disegnare i miei numeri per il PowerBall. La lotteria ignorerà il mio comando (ma non ne ho conoscenza) e l'evento "I numeri PowerBall scelti" si svolgono indipendentemente dai miei desideri. Se i miei numeri corrispondono, l'evento "Jackpot vinto" mi capita (e penso che il mio comando sia stato ascoltato). Altrimenti, mi rendo conto che il mio comando è stato ignorato.

Da una prospettiva storica, la lotteria è interessata solo a un sottoinsieme di eventi. La lotteria si preoccupa solo che (a) è stato emesso un biglietto, (b) i numeri sono stati scelti, e (c) il jackpot è stato vinto. Quelli sono gli oggetti di interesse. L'atto di acquistare il biglietto, di voler vincere, ecc. Sono tutti irrilevanti, come è quello che faccio con il mio biglietto dopo averlo perso. Mentre il mondo reale cambia per eventi mondani, abbiamo solo bisogno di registrare quegli eventi che sono significativi per il nostro sistema.

In teoria, con una tecnica di sourcing di eventi, un flusso di eventi può essere riprodotto dall'inizio del tempo per arrivare allo stato corrente. Questo si basa sul presupposto che le condizioni del sistema sottostante siano costanti e deterministiche. Tuttavia, queste ipotesi non sono valide in molti sistemi. I dati associati ad un evento, così come i tipi di eventi che ci interessano, possono cambiare con l'evolversi del nostro software per computer. Inoltre, può essere computazionalmente costoso ricalcolare lo stato corrente in risposta a ogni query. Per questo motivo, le istantanee dello stato del sistema vengono spesso prese per rappresentare punti noti nel tempo, a cui possono essere poi aggiunti gli eventi più recenti.

Sebbene sia ancora possibile riprodurre un flusso di eventi su più versioni, è probabile che la quantità di sforzo umano implicata in tale operazione sia proibitiva dal punto di vista dei costi. A meno che non ci sia un motivo giustificabile per progettare questa funzionalità nel sistema, è meglio costruire il tuo sistema per utilizzare le istantanee.

Esempio nella domanda

Nell'esempio fornito nella domanda, l'architettura non è realmente basata sugli eventi; è basato sui comandi. La riproduzione di comandi crea lo stato del sistema. Questo è un anti-pattern e dovrebbe essere corretto. Invece, gli eventi principali sono:

  • L'acquirente fa una domanda
  • Il venditore risponde alla domanda

Ognuno di questi eventi può essere "riprodotto" per dare lo stato corrente. Ad esempio, nell'atto di porre una domanda, il comportamento del sistema potrebbe essere quello di inviare per email al venditore e incrementare il contatore unanswered question . Questo comportamento può essere cambiato; tuttavia, il fatto che la domanda sia stata fatta non lo fa. Allo stesso modo, il sistema potrebbe ridurre il contatore unanswered question quando il venditore risponde. Questo comportamento è modificabile, ma il fatto che il venditore abbia risposto non lo è.

La maggior parte dei sistemi di sourcing di eventi calcola dinamicamente il conteggio delle domande senza risposta ripetendo il flusso di eventi specifico in risposta a una query.

    
risposta data 17.02.2016 - 14:33
fonte
3

Commands and queries will be persisted on S3 for purpose of auditing and restoring.

Per l'auditing, sicuro. Per ripristino ? È strano, e probabilmente ti causerà mal di testa.

Se stai per essere un evento di sourcing, vuoi essere reidratato dallo stato degli eventi (cose che sono successe nel passato) non dai comandi. Questo ti consente di risparmiare dalla maggior parte dei problemi associati alle modifiche all'implementazione dei comandi: devi solo gestire le modifiche allo stato persistente.

Il controllo delle versioni è ancora un problema. In particolare, si desidera assicurarsi che gli eventi persistenti siano il più flessibili possibile (rappresentazioni DTO, piuttosto che serializzazioni dirette dei concetti nel proprio dominio). Durante la lettura degli eventi dal negozio, hai la possibilità di aggiornarli secondo necessità prima di applicarli allo stato di reidratazione.

    
risposta data 15.02.2016 - 19:24
fonte