Come implementare un manager di processo nella ricerca di eventi


14

Sto lavorando a una piccola applicazione di esempio per apprendere i concetti di CQRS e di sourcing degli eventi. Ho un Basketaggregato e un Productaggregato che dovrebbero funzionare in modo indipendente.

Ecco alcuni pseudo codici per mostrare l'implementazione

Basket { BasketId; OrderLines; Address; }

// basket events
BasketCreated { BasketId; }
ItemAdded { BasketId; ProductId; Quantity }
AddItemSucceeded { BasketId; ProductId; Quantity }
AddItemRevoked { BasketId; ProductId; Quantity }
ItemRemoved { BasketId; ProductId; Quantity }
CheckedOut { BasketId; Address }

Product { ProductId; Name; Price; }

// product events
ProductReserved { ProductId; Quantity }
ProductReservationFailed { ProductId; Quantity }
ProductReservationCancelled { ProductId; Quantity; }

I comandi sono abbastanza simili agli eventi, usando il nome imperativo e non il tempo passato.

In questo momento questi funzionano bene indipendentemente. Emetto un comando AddIteme crea un ItemAddedevento Basketsull'aggregato che fa quello che deve fare con lo stato del 'Basket'. Allo stesso modo, per prodotto il comando e gli eventi funzionano bene.

Vorrei ora combinare questo in un processo che sarebbe andato qualcosa del genere (in termini di comandi ed eventi che accadono):

Il gestore processi eseguirà le seguenti operazioni:

on BasketCreated: CreateShoppingProcess
on ItemAdded: ReserveProduct
on ProductReserved: SucceedAddingItem // does nothing, but needs to be there so that the basket knows it can check out
on ProductReservationFailed: RevokeAddItem
on RemoveItem: CancelProductReservation
on Checkout: CreateOrder // create an order and so on...

Le domande a cui non sono riuscito a trovare risposte definitive sono:

  1. Devo insistere sul gestore processi? Sembra di sì, ma non ne sono sicuro
  2. In tal caso, devo salvare gli eventi per il gestore processi. Tuttavia, gli eventi che sta ascoltando sono legati agli aggregati. Aggiungo l'id di processo a quelli? Ho eventi separati solo per il gestore processi? Come fare questo e mantenere il più asciutto possibile
  3. Come faccio a sapere a che basket ProductReservedsono destinati gli eventi? Va bene avere un anche BasketIdsu quelli o sono informazioni che perdono?
  4. Come posso mantenere una relazione tra eventi, come faccio a sapere quale ItemAdded prodotto ha prodotto quale ProductReservedevento? Passo un EventId? Sembra strano ...
  5. Dovrei implementare Basketcome gestore dei processi anziché un semplice aggregato?

Dopo alcune ulteriori ricerche sono arrivato a questo: una saga è qualcosa che mantiene i suoi eventi e ascolta gli eventi dall'esterno. Fondamentalmente, è un aggregato che può anche reagire agli eventi che accadono al di fuori del suo piccolo mondo.

Un Process Manager lavora con gli eventi dall'esterno e invia comandi. La sua storia può essere ricostruita dagli eventi accaduti sugli aggregati che condividono un identificatore comune come una correlazioneId.


Sembra che tu stia cercando di codificare nel tuo sistema un processo formale che parafrasa un caso d'uso informale esistente composto da una serie di comandi. In questo modo, sembra che tu stia creando un numero di comandi ed eventi ridondanti oltre a quelli esistenti. È questa la tua intenzione? Qual è la necessità aziendale dietro la formalizzazione delle cose come processo nel codice? Cosa nel tuo dominio ti richiede di identificare questo processo e ragionarlo come un concetto a tutti gli effetti?
guillaume31,

Questo è un progetto totalmente inventato in cui lo scopo è quello di imparare a far lavorare insieme due aggregati relativamente indipendenti. Quindi non c'è davvero alcuna "necessità aziendale" e sto cercando di evitare il più possibile la ridondanza di questi comandi ed eventi. Da qui la confusione con il gestore del processo., Perché sembra che non dovrebbe ripetere cose che gli aggregati stanno già gestendo. Tuttavia, devo mantenere una connessione tra questi due aggregati in qualche modo. Sembra che l'uso della causalità e della correlazione possa essere d'aiuto, ma devo provarlo.
Ivan Pintar,

Risposte:


14

Ripassa ciò che Rinat Abdullin ha scritto sull'evoluzione del processo aziendale . In particolare, nota la sua raccomandazione per lo sviluppo di un processo aziendale in un ambiente in rapido cambiamento: un responsabile di processo è "solo" un sostituto automatico per un essere umano che fissa uno schermo.

Il mio modello mentale di un gestore di processo è che si tratta di una proiezione proveniente da eventi che è possibile eseguire una query per un elenco di comandi in sospeso.

Devo insistere sul gestore processi? Sembra di sì, ma non ne sono sicuro

È un modello letto. È possibile ricostruire il gestore processi dalla cronologia degli eventi ogni volta che è necessario; oppure puoi trattarlo come un'istantanea che aggiorni.

In tal caso, devo salvare gli eventi per il gestore processi.

No: il gestore processi è un gestore . Non fa nulla di utile da solo; invece dice agli aggregati di fare il lavoro (cioè, apportare modifiche al registro).

Come faccio a sapere a quale carrello servono gli eventi ProductReserved? Va bene avere un BasketId anche su quelli, o sono informazioni che perdono

Sicuro. Nota: nella maggior parte dei domini dello shopping "reali", non dovrai insistere per prenotare l'inventario prima di elaborare un ordine; aggiunge contese inutili al business. È più probabile che la tua azienda voglia accettare l'ordine, quindi scusarsi nel raro caso in cui l'ordine non possa essere evaso nel tempo richiesto.

Come posso mantenere una relazione tra eventi, come faccio a sapere quale ItemAdded ha prodotto quale evento ProductReserved?

I messaggi hanno metadati - in particolare, puoi includere un causationIdentifier (in modo da poter identificare quali comandi hanno prodotto quali eventi) e un correlationIdentifier , in genere per tenere traccia della conversazione.

Ad esempio, il gestore processi scrive il proprio ID come correlationId nel comando; gli eventi prodotti da una copia dell'ID di correlazione del comando e il gestore dei processi sottoscrive tutti gli eventi che hanno una propria correlazioneId.

Devo implementare il Basket come gestore dei processi anziché come semplice aggregato?

La mia raccomandazione è no. Ma Udi Dahan ha un'opinione diversa che dovresti rivedere; il che significa che CQRS ha senso solo se i tuoi aggregati sono saghe : Udi ha usato la saga nel luogo in cui il gestore dei processi è diventato l'ortografia dominante.

i process manager dovrebbero recuperare gli aggregati?

Non proprio? I responsabili dei processi si occupano principalmente dell'orchestrazione, non dello stato del dominio. Un'istanza di un processo avrà "stato", sotto forma di una cronologia di tutti gli eventi che hanno osservato: la cosa corretta da fare in risposta all'evento Z dipende dal fatto che abbiamo visto o meno gli eventi X e Y Quindi potrebbe essere necessario essere in grado di memorizzare e caricare una rappresentazione di quello stato (che potrebbe essere qualcosa di piatto o potrebbe essere la storia degli eventi osservati).

(Dico "non proprio" perché l' aggregato è definito abbastanza vagamente da non essere del tutto errato affermare che l'elenco degli eventi osservati è un "aggregato". Le differenze sono più semantiche dell'implementazione: cariciamo lo stato del processo e decidiamo quali messaggi invia alle parti del sistema responsabili dello stato del dominio . Qui c'è un po 'di agitando la mano.)

Quindi il PM non ha bisogno di usare un tipo di gestione statale su un altro perché è responsabile solo di fare cose dal vivo e mai durante i replay?

Non del tutto: la gestione dello stato non è un doppiatore, ma è anche un inseguitore custode. In circostanze in cui il gestore del processo non dovrebbe emettere alcun segnale, si forniscono connessioni inerti al mondo. In altre parole, dispatch(command)è una no-op.


1
Dici: puoi ricostruire il gestore dei processi dalla cronologia degli eventi ogni volta che ne hai bisogno ... Ma per ricostruirlo, devo salvare gli eventi. O dovrei ricostruirlo dagli eventi negli aggregati? La parte con cui sto lottando è: con gli aggregati, gli eventi hanno l'id aggregato, ed è facile da ricostruire trovando tutti gli eventi con quell'ID aggregato. Ma come lo farei per il gestore dei processi? Dovrei farlo per il gestore dei processi? O il gestore dei processi dovrebbe cercare gli aggregati quando deve decidere qualcosa in base a un evento che è arrivato?
Ivan Pintar,

Quello che mi mancava era la nozione di causalità e correlazione nel reperimento di eventi. Una volta che l'ho esaminato, la tua risposta alla quarta domanda ha finalmente avuto un senso.
Ivan Pintar,

1
Mi piacerebbe una risposta al primo commento di @IvanPintar; i process manager dovrebbero recuperare gli aggregati? Dovrebbero costruire il proprio sulla base degli eventi che elabora? In tal caso, i gestori di eventi dovrebbero essere liberi da effetti collaterali, giusto?
Jeff,

@Jeff In un esempio che ho fatto il gestore dei processi aveva il proprio archivio che è stato aggiornato con ogni evento elaborato, una specie di modello letto. Questo è stato semplice ed è stato facile tenere traccia di ciò che è già stato elaborato. In un altro esempio, il gestore processi ha creato e archiviato i propri eventi, basati sugli eventi aggregati. Simile al precedente, ma lo stato era di provenienza evento. A seconda della complessità dello stato mantenuto dal gestore processi, potrebbe essere più semplice eseguire l'uno o l'altro. Ho trovato il primo approccio più semplice.
Ivan Pintar,

Interessante, quindi dipende più o meno dallo sviluppatore finché il gestore del processo risponde agli eventi e invia i comandi alla fine?
Jeff,

2

Quello che stai cercando ha uno schema chiamato "Saga", che è essenzialmente un gestore di processi.

Le Saga sono perfette anche per processi di lunga durata, perché possono mantenere lo stato tra comandi correlati.

Ecco un ottimo articolo su Sagas

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.