Come gestire gli errori post-validazione nel comando (DDD + CQRS)


18

Ad esempio, quando si invia un modulo di registrazione, è necessario verificare che Domain Model( WriteModelin CQRS) sia in uno stato valido (esempio, sintassi dell'indirizzo e-mail, età, ecc.).

Quindi si crea un Commande lo si invia a Command Bus.

Comprendo che i comandi non dovrebbero restituire nulla.

Quindi come gestisci un errore oltre Command Bus? (Ad esempio, un utente si è registrato 1 secondo prima con lo stesso username/email).

Come si fa a sapere che il comando non è riuscito e come si conosce l'errore?


2
Non è necessario un bus eventi. Perché tutti pensano che tu abbia bisogno di un bus eventi durante l'implementazione di CQRS, non lo so. CQRS significa semplicemente separare completamente le scritture e le letture, formulare le preoccupazioni separate. Hai CQRS fintanto che lo fai. I tuoi comandi non devono nemmeno essere asincroni. Se i comandi sincroni con segnalazione errori funzionano per te, fallo.
Andy,

1
I comandi non devono restituire nulla in caso di successo . L'idea è partita da CQS , il che implicava che il comando poteva ancora generare un'eccezione in caso di errore. Quello che stai descrivendo è un comando unidirezionale. Vedi questa risposta in relazione a ciò.
Kasey Speakman

Risposte:


4

Comprendo che i comandi non dovrebbero restituire nulla.

Questa è una visione, ma non è interamente incastonata nella pietra. Considera le scritture (PUT, POST, DELETE) in HTTP: tutti questi messaggi sono comandi, nel senso che sono messaggi con la richiesta che lo stato della risorsa cambi, e tuttavia tutti restituiscono risposte.

Quindi, come gestite un errore oltre Command Bus? (Ad esempio, un utente si è registrato 1 secondo prima con lo stesso nome utente / e-mail).

Come si fa a sapere che il comando non è riuscito e come si conosce l'errore?

Quindi, nel caso in cui si stia comunicando direttamente con il gestore dei comandi, un messaggio restituito è un modo perfettamente ragionevole per riconoscere che il comando è stato ricevuto ed elaborato.

Se stai usando un middleware, come un bus, che ti impedisce di comunicare direttamente con la destinazione, ti suggerirei di guardare a schemi di messaggistica asincroni: come si fa a far sì che il gestore dei comandi rispedisca un messaggio al chiamante?

Un'idea è quella di iscriversi all'esito del comando; questo prende in prestito alcune delle idee nei modelli di integrazione aziendale di Hohpe. L'idea di base è che, poiché il client ha familiarità con il messaggio di comando che è stato inviato, è ben posizionato per iscriversi a tutti i nuovi messaggi pubblicati come conseguenza del messaggio di comando. Il gestore dei comandi, dopo aver salvato i dati nel registro, pubblica gli eventi annunciando che la modifica ha avuto esito positivo e il client si iscrive a tali eventi, riconoscendo gli eventi corretti considerando la coincidenza di vari identificatori nel messaggio (ID causalità, ID di correlazione e così via).

Approcci alternativi sono un po 'più diretti. Uno sarebbe quello di includere nel messaggio un callback, che può essere invocato dal gestore dei comandi dopo che il messaggio è stato gestito con successo.

Un'alternativa molto simile è quella di riservare spazio nel messaggio di comando affinché il gestore comandi scriva il riconoscimento - poiché il client ha già il messaggio di comando in questione, il circuito è già completo. Pensa " promessa " o " futuro realizzabile". Il messaggio indica al gestore comandi dove scrivere il riconoscimento; così facendo segnala al client (blocco del conto alla rovescia) che il riconoscimento è disponibile.

E, naturalmente, hai la possibilità aggiuntiva di rimuovere il middleware che sembra ostacolare semplicemente la cosa giusta.

Ad esempio, un utente si è registrato 1 secondo prima con lo stesso nome utente / e-mail

Se gestisci la registrazione dell'utente in modo idempotente, non sarebbe necessariamente un errore: ripetere i messaggi fino a quando non viene osservata una risposta è un modo comune per garantire almeno una volta la consegna.


2

Ad esempio, quando si invia un modulo di registrazione, è necessario verificare nel modello di dominio (WriteModel in CQRS) che si trova in uno stato valido (esempio, sintassi dell'indirizzo e-mail, età, ecc.)

Esistono molti tipi di convalida. La convalida quando si verifica la sintassi dell'indirizzo e-mail e il formato dell'età è un tipo di convalida che un comando può eseguire. Questo non è davvero un problema di dominio. Potrebbe sembrare che alcuni esperti di dominio ti diano queste specifiche ma dovresti fare questo tipo di validazione nella creazione del comando. In effetti, l'idea generale è quella di eseguire la convalida il più presto possibile perché dopo che un comando è stato creato e inviato a un BUS è più complicato intraprendere azioni.

Quindi, come gestite un errore oltre Command Bus? (Ad esempio, un utente si è registrato 1 secondo prima con lo stesso nome utente / e-mail).

Questo tipo di convalida è molto discusso nella comunità CQRS, dall'inizio del CQRS. Dove farlo? È molto dibattuto. Personalmente utilizzo il seguente approccio: Prima di inviare il comando al BUS, segnare il nome utente / e-mail come preso in modo centralizzato (cioè un vincolo di indice univoco su nome utente / e-mail). Dopodiché invio il comando. Lo svantaggio è che, se il comando non riesce, per un breve periodo di tempo tale nome utente viene utilizzato e non utilizzato; questo è accettabile per la mia attività, probabilmente per la tua attività.

Come si fa a sapere che il comando non è riuscito e come si conosce l'errore?

Se un comando asincrono viene rifiutato dall'aggregato a causa di un invariante di dominio, è necessario adottare alcune misure emettendo un comando compensativo che in qualche modo informa l'emittente del comando (ovvero inviare un'email di spiegazione del fallimento del comando).

Il problema con un'e-mail duplicata è che non puoi inviare un'e-mail perché quell'indirizzo e-mail appartiene a un'altra persona, ecco perché lo controllo prima di inviare il comando al bus.


1

La convalida deve essere eseguita in un decoratore. Quindi qualsiasi comando che necessita di validazione potrebbe essere decorato come tale.

Le convalide possono essere gestite con eccezioni se il tuo ritorno viene annullato con il tuo comando in modo da poter essere raccolto con chiamate di sincronizzazione o asincrone con il risultato dell'attività restituita.

Un'altra possibilità è pensare alla convalida come a un tipo di "query" che restituirebbe un risultato di convalida. Esegui la query di convalida e, se passato, esegui il comando. Questa sarebbe un'alternativa all'approccio del decoratore.


Mi piace l'approccio delle eccezioni perché è il modo più pulito. Ma le eccezioni non sono troppo pesanti per questo?
EresDev

1
HI @EresDev - Sì, sono pesanti. Ma non so quanto siano "validi" i tuoi dati. Diciamo che su 1000 record 2 non sono validi. Le eccezioni potrebbero essere praticabili in quanto sono condizioni davvero eccezionali. Ora considera 1000 record con 200 non validi. In tale causa, si dovrebbero evitare le eccezioni sì perché il 20% dei dati non è valido. Quindi, lascio a voi decidere se scegliere o meno questo approccio in base alla validità dei vostri dati attuali. :)
Jon Raynor il

Aggiungerò a questo dicendo che se manteniamo l'esempio del modulo di registrazione, dove penso che il 50% o anche più utenti inseriscano prima i dati non validi, le eccezioni vanno ancora bene. Nel back-end dell'applicazione Web, si ottengono principalmente dati validi poiché anche i front-end eseguono la convalida. Ottieni solo se qualcosa ha perso la convalida del frontend per qualche rara possibilità o se qualcuno sta scherzando. Quindi, le eccezioni vanno molto bene in questo caso.
EresDev,
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.