it-swarm.com.de

Ausnahmen in DDD

Ich lerne DDD und denke darüber nach, in bestimmten Situationen Ausnahmen zu werfen. Ich verstehe, dass ein Objekt nicht in einen schlechten Zustand versetzt werden kann, daher sind hier die Ausnahmen in Ordnung, aber in vielen Beispielen werden auch Ausnahmen ausgelöst, wenn wir versuchen, einen neuen Benutzer mit vorhandener E-Mail in der Datenbank hinzuzufügen.

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()
        )
    );
}

Wenn also ein Benutzer mit dieser E-Mail vorhanden ist, können wir im Anwendungsdienst eine Ausnahme abfangen, aber wir sollten den Betrieb der Anwendung nicht mithilfe des Try-Catch-Blocks steuern.

Wie ist der beste Weg dafür?

11
yadiv

Beginnen wir mit einem kurzen Überblick über den Problembereich: Eines der Grundprinzipien von DDD besteht darin, die Geschäftsregeln so nah wie möglich an den Stellen zu platzieren, an denen sie durchgesetzt werden müssen. Dies ist ein extrem wichtiges Konzept, da es Ihr System "zusammenhängender" macht. Regeln "nach oben" zu bewegen ist allgemein ein Zeichen eines anämischen Modells; Dabei handelt es sich bei den Objekten nur um Datenbeutel, und den Regeln werden die zu erzwingenden Daten eingefügt.

Ein anämisches Modell kann für Entwickler, die gerade erst mit DDD beginnen, sehr sinnvoll sein. Sie erstellen ein User Modell und ein EmailMustBeUnqiueRule, denen die erforderlichen Informationen zur Validierung der E-Mail hinzugefügt werden. Einfach. Elegant. Das Problem ist, dass diese "Art" des Denkens grundsätzlich prozeduraler Natur ist. Nicht DDD. Was am Ende passiert, ist, dass Sie ein Modul mit Dutzenden von Rules haben, die ordentlich verpackt und gekapselt sind, aber völlig kontextfrei sind, bis zu dem Punkt, an dem sie nicht mehr geändert werden können, weil nicht klar ist, wann/wo sie sich befinden werden durchgesetzt. Ist das sinnvoll? Es kann selbstverständlich sein, dass ein EmailMustBeUnqiueRule bei der Erstellung eines User angewendet wird, aber was ist mit UserIsInGoodStandingRule?. Langsam aber sicher führt die Kornularisierung des Extrahierens von Rules aus ihrem Kontext zu einem System, das schwer zu verstehen ist (und daher nicht geändert werden kann). Regeln sollten nur gekapselt werden, wenn das eigentliche Knirschen/Ausführen so ausführlich ist, dass Ihr Modell den Fokus verliert.

Nun zu Ihrer spezifischen Frage: Das Problem mit dem Service/CommandHandler, das Exception zu werfen, ist, dass Ihre Geschäftslogik beginnt, aus Ihrem "(nach oben") zu lecken Domain. Warum muss Ihr Service/CommandHandler wissen, dass eine E-Mail eindeutig sein muss? Die Anwendungsdienstschicht wird im Allgemeinen eher zur Koordination als zur Implementierung verwendet. Der Grund dafür kann einfach veranschaulicht werden, wenn wir Ihrem System eine Methode/einen Befehl ChangeEmail hinzufügen. Jetzt müssten BEIDE Methoden/Befehlshandler Ihre eindeutige Prüfung einschließen. Hier könnte ein Entwickler versucht sein, ein EmailMustBeUniqueRule zu "extrahieren". Wie oben erklärt, wollen wir diesen Weg nicht gehen.

Einige zusätzliche Wissensverknappungen können uns zu einer DDD-Antwort führen. Die Eindeutigkeit einer E-Mail ist eine Invariante, die für eine Sammlung von User -Objekten erzwungen werden muss. Gibt es in Ihrer Domain ein Konzept, das eine "Sammlung von User Objekten" darstellt? Ich denke, Sie können wahrscheinlich sehen, wohin ich hier gehe.

Für diesen speziellen Fall (und viele weitere, bei denen Invarianten über Sammlungen hinweg erzwungen werden) ist der beste Ort, um diese Logik zu implementieren, Ihr Repository. Dies ist besonders praktisch, da Ihr Repository auch die zusätzliche Infrastruktur "kennt", die für die Ausführung dieser Art der Validierung erforderlich ist (den Datenspeicher). In Ihrem Fall würde ich diese Prüfung in die Methode add einfügen. Das macht doch Sinn, oder? Konzeptionell ist es diese Methode, die Ihrem System wirklich ein User hinzufügt. Der Datenspeicher ist ein Implementierungsdetail.

20
king-side-slide

Sie können in jeder Ebene Ihrer Anwendung eine Validierung festlegen. Wo Sie festlegen müssen, welche Regeln von Ihrer Anwendung abhängen.

Beispielsweise implementieren Entitäten Methoden, die Geschäftsregeln darstellen, während Anwendungsfälle Regeln implementieren, die für Ihre Anwendung spezifisch sind.

Wenn Sie einen E-Mail-Dienst wie Google Mail erstellen, könnte man argumentieren, dass die Regel "Benutzer sollten eine eindeutige E-Mail-Adresse haben" eine Geschäftsregel ist.

Wenn Sie einen Registrierungsprozess für ein neues CRM-System erstellen, wird diese Regel wahrscheinlich am besten in einem Anwendungsfall implementiert, da diese Regel wahrscheinlich auch Teil Ihres Anwendungsfalls ist. Darüber hinaus ist das Testen möglicherweise auch einfacher, da Sie die Repository-Aufrufe in Ihren Anwendungsfalltests problemlos stubben können.

Aus Gründen der zusätzlichen Sicherheit können Sie diese Regel auch in Ihrem Repository durchsetzen.

0
thenoob