Ich habe in letzter Zeit viel über immer gültige Domänenentitäten gelesen. Ich bin zu der Überzeugung gelangt, dass ich Folgendes tun muss, um sicherzustellen, dass die Entitäten immer gültig sind:
1) Entfernen Sie die primitive Besessenheit und fügen Sie Validierungs- / Domänenregeln in die Wertobjektkonstruktoren ein, wie hier erläutert: https://enterprisecraftsmanship.com/2016/09/13/validation-and-ddd/ . 2) Fügen Sie Validierungs- / Domänenregeln in den Konstruktor von Entitäten oder die Eigenschaftssetzer ein, wie hier erläutert: http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/ .
Ich schaue mir dann jedoch einige Open Source-Projekte wie dieses an: https://github.com/gregoryyoung/mr . Soweit ich weiß, ist der Autor dieses Projekts ein Verfechter des immer gültigen Domänenmodells, und dennoch schaue ich hier in die InventoryItem-Klasse: https://github.com/gregoryyoung/mr/blob/master/SimpleCQRS/Domain.cs . Ich stelle fest, dass ich dazu in der Lage bin:
InventoryItem inventoryItem = new InventoryItem();
oder dieses:
InventoryItem inventoryItem2 = new InventoryItem(Guid.Empty,null);
In meinen Augen bedeutet dies, dass die Entität in einem ungültigen Zustand initialisiert wird. Dies scheint auch bei allen anderen Open Source-Projekten der Fall zu sein, die ich mir kürzlich angesehen habe, z. B. diesem: https://github.com/dcomartin/DDD-CQRS-ES-Example/blob/master/src/Domain /Customer.cs .
Mir ist klar, dass dieses Open Source-Projekt eine Kontextvalidierung enthält ( https://martinfowler.com/bliki/ContextualValidation.html ). Mir ist auch klar, dass ORMs einen leeren Standardkonstruktor benötigen, wenn sie dem Domänenmodell zugeordnet sind.
Befindet sich ein Domänenobjekt in einem gültigen Zustand, wenn es mit Standardwerten unter Verwendung eines Konstruktors für Nullargumente initialisiert / mit leeren / Nullwerten initialisiert wurde?
quelle
Antworten:
Bevor ich mich mit den Überlegungen befasse, die bei der Objekterstellung anfallen, wollen wir zunächst die Motivation hinter Ihrer Frage ansprechen: den Ausdruck "Immer gültige Domänenentitäten". Dieser Satz ist bestenfalls irreführend und macht im Zusammenhang mit DDD wenig Sinn. Dies sollte aus zwei verwandten Gründen offensichtlich sein:
Das erste ist, dass es Ihren Fokus implizit vom Verhalten des Systems weg verlagert und Sie stattdessen auffordert, die Validierung nur in Bezug auf den Status zu berücksichtigen. Oberflächlich betrachtet mag dies sinnvoll erscheinen (die Validierung erfolgt natürlich in Bezug auf den Zustand!), Aber Sie müssen sich daran erinnern, dass das Grundprinzip von DDD darin besteht, dass ein System gemäß dem Verhalten modelliert wird . Die Motivation dafür ist ganz einfach, dass der Kontext oder der Geschäftsprozess selbst oft eine wichtige Überlegung ist, wenn festgestellt wird, ob ein Teil des Staates gültig ist oder nicht. Das Modellieren eines Systems auf diese Weise kann seine Komplexität erheblich reduzieren.
Dies bringt uns zum zweiten Grund, der sich auf die praktischen Anforderungen bezieht, die ein solches System mit sich bringen würde. Um ein System von "Immer gültigen Domänenentitäten" zu erstellen, müsste jede einzelne Statuspermutation gemäß den Geschäftsprozessen modelliert werden, in denen der Status verwendet wird. Ein einfaches Beispiel kann die Einschränkungen veranschaulichen:
Regeln:
Customer
muss über 18 sein, um sich zu registrierenCustomer
muss unter 25 sein, um einen Rabatt bei der Registrierung zu erhaltenCustomer
muss über 25 sein, um eine Reservierung vorzunehmenDas erste, was Sie beachten sollten, ist, dass alle diese Regeln (wie fast alle Regeln) für einige Geschäftsprozesse gelten. Sie existieren nicht im luftleeren Raum. Diese Regeln würden am
customer.Register()
und validiertcustomer.Reserve()
. Dies führt zu einem viel aussagekräftigeren und deklarativeren Paradigma, da klar ist, wo Regeln ausgeführt werden.Wenn wir diese Regeln so modellieren möchten, dass sie zu einem System von "Immer gültigen Domänenentitäten" führen, müssten wir unsere
Customer
inRegistrar
undReserver
Entitäten partitionieren . Und obwohl dies für dieses Beispiel nicht so schlecht erscheint, werden Sie mit zunehmender Komplexität und Fülle von Regeln Explosionsklassen wie diese erhalten, die den Zustand "innerhalb" eines bestimmten Kontexts oder Prozesses darstellen. Dies ist einfach unnötig und führt unweigerlich zu Problemen, wenn zwei solcher Objekte von demselben Statusabschnitt abhängig sind.Darüber hinaus ist so etwas wie
Customer c = new Customer()
ein schlechter Ort, um eine Ausnahme auszulösen, da unklar ist, welche Geschäftsregeln gelten könnten. Das bringt uns zu unserer abschließenden Diskussion.Ich werde nur herauskommen und so weit gehen zu sagen, dass es keine Validierung von Geschäftsregeln geben sollte, die in Konstruktoren stattfinden. Die Objektkonstruktion hat überhaupt nichts mit Ihrer Geschäftsdomäne zu tun. Aus diesem Grund sollten zusätzlich zu den oben genannten Gründen in Bezug auf Kontext und Kohärenz alle Geschäftsregeln in den Methodenkörpern einer Entität durchgesetzt werden (wahrscheinlich sind die Methoden nach dem richtigen Geschäftsprozess benannt ?).
Darüber hinaus ist das "Neuerstellen" eines Objekts nicht dasselbe wie das Erstellen einer neuen Entität in Ihrer Domäne. Objekte kommen nicht aus dem Nichts. Wenn es Geschäftsregeln gibt, wie eine neue Entität in Ihr System gelangen kann, sollte sie in Ihrer Domäne modelliert werden . Hier ist eine weitere Diskussion zu diesem Thema durch einen wahren Meister http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
quelle
Es kann sein.
Es gibt nichts gegen die Regeln zum Erstellen eines gültigen Domänenobjekts mit einem Standardkonstruktor.
Es gibt nichts gegen die Regeln zum Erstellen eines gültigen Domänenobjekts mit leeren Werten.
Es gibt nichts gegen die Regeln zum Erstellen eines gültigen Domänenobjekts, dem optionale Elemente fehlen.
Es gibt nichts gegen die Regeln zum Erstellen eines gültigen Domänenobjekts mit Nullen.
Wo Probleme auftreten: Erstellen von Domänenobjekten, die ihrer eigenen semantischen Algebra nicht entsprechen.
Die korrekte Implementierung hängt von einer korrekten Interpretation der Semantik ab.
Es ist normal, ein Domänenkonzept verzeihend darzustellen und schrittweise zusätzliche Einschränkungen anzuwenden. Dies ist einer der Bereiche, in denen Implementierungssprachen, die das Hinzufügen von Typen (z. B. F #) vereinfachen, Vorteile gegenüber ungeschickteren Sprachen wie Java haben.
Wenn wir beispielsweise eine Domäne haben, die sich um Zahlen kümmert, und innerhalb dieser Domäne einige Funktionen vorhanden sind, die für Primzahlen oder Mersenne-Primzahlen spezifisch sind, besteht ein natürlicher Mechanismus darin, drei verschiedene Typen zu erstellen . Machen Sie deutlich, dass auf die Eingaben in verschiedenen Teilen Ihrer Lösung unterschiedliche Einschränkungen angewendet werden müssen.
Bei dieser Art von Design würde die "Validierung", dass die Zahl wirklich ein Prime ist, innerhalb des Prime-Konstruktors selbst existieren. In einem Kontext, in dem wir die zusätzliche Einschränkung benötigen, dass der Parameter eine Mersenne- Primzahl ist, verwenden wir eine
Prime
->MersennePrime
-Konvertierung, um sicherzustellen, dass das Wissen über die Mersenne-Einschränkung eine Berechtigung hat ("Wiederholen Sie sich nicht")."Machen Sie das implizite, explizite"
quelle