Eccezioni in DDD

9

Sto imparando DDD e sto pensando di lanciare delle eccezioni in determinate situazioni. Capisco che un oggetto non può entrare in uno stato negativo, quindi qui le eccezioni vanno bene, ma in molti esempi le eccezioni vengono lanciate anche per esempio se stiamo provando ad aggiungere un nuovo utente con l'e-mail esistente nel database.

public function doIt(UserData $userData)
{
    $user = $this->userRepository->byEmail($userData->email());
    if ($user) {
        throw new UserAlreadyExistsException();
    }

    $this->userRepository->add(
        new User(
            $userData->email(),
            $userData->password()
        )
    );
}

Quindi, se l'utente con questa email esiste, allora nel servizio applicativo possiamo rilevare un'eccezione, ma non dovremmo controllare il funzionamento dell'applicazione utilizzando il blocco try-catch.

Qual è il modo migliore per questo?

    
posta yadiv 02.03.2018 - 19:13
fonte

1 risposta

12

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.

    
risposta data 02.03.2018 - 23:14
fonte