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 User
Modell 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 Rules
ordentlich 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 EmailMustBeUnqiueRule
Wille auf die Schaffung eines angewendet wird User
, aber was ist mit UserIsInGoodStandingRule
? Langsam aber sicher erfolgt die Kornularisierung der Extraktion derRules
Wenn 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
/ CommandHandler
throw the Exception
ist, dass Ihre Geschäftslogik beginnt ("nach oben") aus Ihrer Domain herauszulaufen. Warum muss Ihre Service
/ CommandHandler
mü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 ChangeEmail
Ihrem 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 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, in Ihrem Repository
. Dies ist besonders praktisch, da Sie Repository
auch 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 add
Methode einfügen. Das macht doch Sinn, oder? Konzeptionell ist es diese Methode, User
die Ihrem System wirklich etwas hinzufügt . Der Datenspeicher ist ein Implementierungsdetail.