Iniziamo con una breve revisione dello spazio del problema: uno dei principi fondamentali del DDD è quello di posizionare le regole aziendali il più vicino possibile ai luoghi in cui devono essere applicate. Questo è un concetto importante estremamente perché rende il sistema più "coeso". Lo spostamento delle regole "verso l'alto" è generalmente un segno di un modello anemico; dove gli oggetti sono solo sacchi di dati e le regole vengono iniettate con quei dati da applicare.
Un modello anemico può avere molto senso per gli sviluppatori che iniziano appena con DDD. Crei un modello User
e un EmailMustBeUnqiueRule
che viene iniettato con le informazioni necessarie per convalidare l'e-mail. Semplice. Elegante. Il problema è che questo "tipo" di pensiero è fondamentalmente di natura procedurale. Non DDD. Quello che finisce per accadere è che ti rimane un modulo con dozzine di Rules
accuratamente incapsulate e incapsulate, ma sono completamente prive di contesto al punto in cui non possono più essere modificate perché non è chiaro quando / dove vengono applicate. Ha senso? potrebbe essere ovvio che verrà applicata una EmailMustBeUnqiueRule
alla creazione di un User
, ma per quanto riguarda UserIsInGoodStandingRule
?. Lentamente ma inesorabilmente, la granularizzazione dell'estrazione del Rules
dal loro contesto ti lascia con un sistema che è difficile da capire (e quindi non può essere modificato). Le regole dovrebbero essere incapsulate solo quando il crunch / esecuzione effettivo è così dettagliato che il tuo modello inizia a perdere attenzione.
Ora passiamo alla tua domanda specifica: il problema di avere Service
/ CommandHandler
di generare Exception
è che la logica aziendale sta iniziando a perdere ("verso l'alto") dal tuo dominio. Perché il tuo Service
/ CommandHandler
deve sapere che un'email deve essere unica? Il livello del servizio applicativo è generalmente utilizzato per il coordinamento piuttosto che per l'implementazione. La ragione di ciò può essere illustrata semplicemente se aggiungiamo un metodo / comando ChangeEmail
al tuo sistema. Ora ENTRAMBI i metodi / i gestori di comandi dovrebbero includere il controllo univoco. È qui che uno sviluppatore può essere tentato di "estrarre" un EmailMustBeUniqueRule
. Come spiegato sopra, non vogliamo seguire questa strada.
Qualche altro scricchiolio della conoscenza può portarci a una risposta più DDD. L'unicità di un messaggio di posta elettronica è un invariante che deve essere applicato a un insieme di oggetti User
. Esiste un concetto nel tuo dominio che rappresenta un "insieme di oggetti User
"? Penso che tu possa probabilmente vedere dove sto andando qui.
Per questo caso particolare (e molti altri ancora che coinvolgono gli invarianti attraverso le collezioni) il posto migliore per implementare questa logica sarà nel tuo Repository
. Ciò è particolarmente utile perché anche il tuo Repository
"conosce" l'infrastruttura aggiuntiva necessaria per eseguire questo tipo di convalida (l'archivio dati). Nel tuo caso, inserisco questo controllo nel metodo add
. Questo ha senso, giusto? Concettualmente, è questo metodo che aggiunge veramente un User
al tuo sistema. L'archivio dati è un dettaglio di implementazione.