Sto lavorando a una piccola applicazione di esempio per apprendere i concetti di CQRS e di sourcing degli eventi. Ho un Basket
aggregato e un Product
aggregato 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 AddItem
e crea un ItemAdded
evento Basket
sull'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:
- Devo insistere sul gestore processi? Sembra di sì, ma non ne sono sicuro
- 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
- Come faccio a sapere a che basket
ProductReserved
sono destinati gli eventi? Va bene avere un ancheBasketId
su quelli o sono informazioni che perdono? - Come posso mantenere una relazione tra eventi, come faccio a sapere quale
ItemAdded
prodotto ha prodotto qualeProductReserved
evento? Passo unEventId
? Sembra strano ... - Dovrei implementare
Basket
come 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.