Ausnahmen in DDD

11

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?

yadiv
quelle
1
Es ist in Ordnung, wenn Domain-Services (Handler) Ausnahmen auslösen.
Andy

Antworten:

20

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 äußerst wichtiges Konzept, da es Ihr System "kohärenter" macht. Das Verschieben von Regeln "nach oben" ist im Allgemeinen ein Zeichen für ein anämisches Modell. Dabei handelt es sich bei den Objekten nur um Datenbeutel, und den Regeln werden die zu erzwingenden Daten hinzugefügt.

Ein anämisches Modell kann für Entwickler, die gerade erst mit DDD beginnen, sehr sinnvoll sein. Sie erstellen ein UserModell und ein Modell EmailMustBeUnqiueRule, das mit den erforderlichen Informationen zur Validierung der E-Mail versehen wird. 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 Rulesordentlich verpackten und gekapselten Modulen haben , aber sie sind völlig kontextfrei, bis zu dem Punkt, an dem sie nicht mehr geändert werden können, da nicht klar ist, wann / wo sie erzwungen werden. Ist das sinnvoll? Es mag selbstverständlich sein, dass ein EmailMustBeUnqiueRuleWille auf die Schaffung eines angewendet wird User, aber was ist mit UserIsInGoodStandingRule? Langsam aber sicher erfolgt die Kornularisierung der Extraktion derRulesWenn Sie nicht in ihrem Kontext stehen, haben Sie ein 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/ CommandHandlerthrow the Exceptionist, dass Ihre Geschäftslogik beginnt ("nach oben") aus Ihrer Domain herauszulaufen. Warum muss Ihre Service/ CommandHandlermüssen eine E-Mail eindeutig sein? Die Anwendungsdienstschicht wird im Allgemeinen eher zur Koordination als zur Implementierung verwendet. Der Grund dafür kann einfach veranschaulicht werden, wenn wir ChangeEmailIhrem System eine Methode / einen Befehl hinzufügen . Jetzt müssten BEIDE Methoden / Befehlshandler Ihre eindeutige Prüfung einschließen. Hier könnte ein Entwickler versucht sein, eine zu "extrahieren" EmailMustBeUniqueRule. 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 UserObjekten erzwungen werden muss . Gibt es in Ihrer Domain ein Konzept, das eine "Sammlung von UserObjekten" 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, in Ihrem Repository. Dies ist besonders praktisch, da Sie Repositoryauch die zusätzliche Infrastruktur "kennen", 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 addMethode einfügen. Das macht doch Sinn, oder? Konzeptionell ist es diese Methode, Userdie Ihrem System wirklich etwas hinzufügt . Der Datenspeicher ist ein Implementierungsdetail.

King-Side-Rutsche
quelle
Könnte ich fragen, was du damit meinst Moving rules "upwards" is generally a sign of an anemic model? Aufwärts bedeutet für die Anwendungsschicht? oder zum Domain-Modell?
Anyname Donotcare
1
"Aufwärts" bedeutet in Richtung Ihrer Anwendungsschicht (denken Sie an eine geschichtete Architektur). Ich habe dies oben angesprochen, aber der Grund, warum Regeln nach oben verschoben werden, ist ein Zeichen für ein anämisches Modell, da es die Trennung von Daten und Verhalten auf eine Weise darstellt, die den gesamten Zweck eines Kerndomänenmodells zunichte macht. Wenn sich die Validierung einer E-Mail in einem separaten Modul als das Senden einer E-Mail befindet, Emailmuss Ihr Modell ein Beutel mit Daten sein, der vor dem Senden auf Invarianten überprüft wird. Siehe: softwareengineering.stackexchange.com/questions/372338/…
King-Side-Folie
2
Das Verschieben der Domänenlogik in das Repository ist falsch. Sie sollten die Domänenregel durch das aggregierte Stammverzeichnis in der Domänenschicht erzwingen.
Arash
1
Die @ Arash Set-Validierung ist schwierig und funktioniert mit DDD oft nicht gut. Sie müssen entweder überprüfen, ob ein Prozess funktioniert, bevor Sie diesen Prozess ausführen (a hinzufügen User), oder akzeptieren, dass diese bestimmte Art von Invariante nicht ordnungsgemäß in Ihrer Domäne eingekapselt wird. Ersteres stellt eine Trennung von Daten und Verhalten dar, die zu einem prozeduralen Paradigma führt. Das ist weniger als ideal. Die Validierung sollte existiert mit Daten, nicht um Daten. Welchen Vorteil hat es außerdem, einen Domain-Service zu erstellen, der nur dazu dient, diese eine Regel zu überprüfen? Angeblich ist dieser Dienst ...
King-Side-Slide
1
@Arash koppelt Ihre Repository, Userund diese Invariante und erreicht daher nicht die puristische Vision, auf die Sie sowieso drängen. Repository-Implementierungen sind Teil der Domäne und können daher bestimmte Verantwortlichkeiten erhalten.
King-Side-Slide
0

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.

der Noob
quelle