In CQRS / ES, un comando può creare un altro comando?

8

In CQRS / ES, un comando viene inviato dal client al server e instradato al gestore comandi appropriato. Quel gestore comandi carica un aggregato dal suo repository, chiama un metodo su di esso e lo salva nel repository. Gli eventi sono generati. Un gestore di eventi / saga / gestore di processi può ascoltare questi eventi per emettere comandi.

Quindi, i comandi (input) producono eventi (output), che possono quindi reimmettere nel sistema più comandi (input). Ora, è pratica comune che un comando non emetta alcun evento, ma piuttosto che accoda un altro comando? Tale approccio potrebbe essere utilizzato per forzare l'esecuzione in un processo esterno.

EDIT:

Il caso d'uso specifico che ho in mente è l'elaborazione dei dettagli di pagamento. Il client invia un comando PayInvoice , il cui payload include i dati della carta di credito dell'utente. Il PayInvoiceHandler passa un comando MakeInvoicePayment a un processo separato, che è responsabile dell'interazione con il gateway di pagamento. Se il pagamento ha esito positivo, viene generato un evento InvoicePaid . Se per qualche motivo il sistema si arresta in modo anomalo dopo la permanenza del comando PayInvoice ma prima che il comando MakeInvoicePayment venga mantenuto, possiamo tenerlo tracciato manualmente (nessun pagamento sarà stato eseguito). Se il sistema si blocca dopo che il comando MakeInvoicePayment è persistuto ma prima che l'evento InvoicePaid venga mantenuto, potremmo avere una situazione in cui la carta di credito dell'utente viene addebitata ma la fattura non è contrassegnata come pagata. In tal caso, la situazione dovrebbe essere esaminata manualmente e la fattura verrà contrassegnata manualmente come pagata.

    
posta magnus 25.05.2016 - 22:31
fonte

3 risposte

9

In retrospettiva, penso di aver complicato il problema.

In generale, i comandi devono generare un'eccezione o generare uno o più eventi.

Se potessi riepilogare l'architettura di Event Sourcing, sarebbe come segue:

  • I comandi sono input che rappresentano istruzioni per fare qualcosa.
  • Gli eventi sono risultati che rappresentano fatti storici di ciò che è stato fatto.
  • I gestori di eventi possono ascoltare eventi al fine di emettere comandi, aiutando a coordinare le diverse parti del sistema.

Avere un comando che crea un altro comando causa ambiguità nel significato generale di comandi ed eventi: se un comando "innesca" un altro comando, ciò implica che un comando è "un fatto storico di ciò che è stato fatto". Ciò contraddice l'intento di questi due tipi di messaggi e potrebbe diventare un percorso sdrucciolevole, in quanto altri sviluppatori potrebbero attivare eventi dagli eventi, che potrebbero portare a dati corrotti ed eventuali incoerenze .

Riguardo allo specifico scenario che ho posto, il fattore complicante era che non volevo che il thread principale interagisse con il gateway di pagamento, in quanto (essendo un processo persistente, a thread singolo), questo non avrebbe consentito altri comandi essere processato. La soluzione semplice qui è generare un altro thread / processo per gestire il pagamento.

Per illustrare, il client invia un comando PayInvoice . Il PayInvoiceHandler avvia un nuovo processo e gli passa i dettagli del pagamento. Il nuovo processo comunica con il gateway di pagamento. Se il pagamento è andato a buon fine, chiama invoice.markAsPaid() con il numero di ricevuta (che produce l'evento InvoicePaid ). Se il pagamento non ha avuto esito positivo, chiama invoice.paymentFailed() con un codice di riferimento per ulteriori indagini (che produce l'evento InvoicePaymentFailed ). Quindi, nonostante sia coinvolto un processo / thread separato, il pattern rimane Command -> Event .

Riguardo al fallimento, ci sono tre scenari, il sistema potrebbe bloccarsi dopo che il comando PayInvoice è stato mantenuto ma prima che gli eventi InvoicePaid o InvoicePaymentFailed siano persistenti. In questo caso, non sappiamo se è stata addebitata la carta di credito dell'utente. In tal caso, l'utente noterà l'addebito sulla sua carta di credito e presenterà un reclamo, nel qual caso un membro del personale può esaminare il problema e contrassegnare manualmente la fattura come pagata.

    
risposta data 26.05.2016 - 01:06
fonte
2

Otterrai un sistema che è architettonicamente più strettamente accoppiato se emetti solo eventi da un comando. In altre parole, un comando non dovrebbe aver bisogno di sapere quali altri comandi esterni emettere; questo dovrebbe essere la responsabilità della parte esterna (chi dovrebbe iscriversi all'evento e potrebbe essere, come hai detto, un manager della saga con responsabilità di coordinamento, o solo un altro modulo che ha dipendenza da quegli eventi).

    
risposta data 25.05.2016 - 23:19
fonte
1

Visualizzazione consigliata: Udi Dahan su Messaggistica affidabile - non è esattamente quello che stai descrivendo, ma strettamente correlato.

Now, is it common practice for a command to not emit any events, but rather to enqueue another command?

Non ho visto nessuno raccomandare quella pratica.

Risposta breve: se non si salva alcun stato, non è possibile ripristinare il comando enqueued se si blocca dopo aver riconosciuto di averlo ricevuto.

Se decidi che è necessario salvare lo stato, non è chiaro che ci sia un grande vantaggio nel pianificare il secondo comando dal primo, piuttosto che usare un gestore di eventi.

    
risposta data 25.05.2016 - 23:05
fonte