Evans führt in seinem Buch "Domain Driven Design" in Kapitel 6 "Aggregate" das Konzept der Aggregate ein. Er definiert ferner Regeln, um dieses Konzept in eine Implementierung umzusetzen (Evans 2009, S. 128-129):
Die Root-ENTITY kann Verweise auf die internen ENTITIES an andere Objekte übergeben, diese Objekte können sie jedoch nur vorübergehend verwenden, und sie halten möglicherweise nicht an der Referenz fest.
Nachdem er andere Regeln ausgearbeitet hat, fasst er sie in diesem Absatz zusammen:
Gruppieren Sie die Entitäten und Wertobjekte zu Aggregaten und definieren Sie jeweils Grenzen. Wählen Sie eine Entität als Stamm jedes Aggregats aus und steuern Sie den gesamten Zugriff auf die Objekte innerhalb der Grenze über den Stamm. Zulassen, dass externe Objekte nur Verweise auf das Stammverzeichnis enthalten. Vorübergehende Verweise auf interne Mitglieder können nur zur Verwendung innerhalb einer einzelnen Operation ausgegeben werden. Da der Root den Zugriff steuert, kann er nicht durch Änderungen an den Interna blind gemacht werden. Diese Anordnung macht es praktisch, alle Invarianten für Objekte im Aggregat und für das Aggregat als Ganzes bei jeder Zustandsänderung zu erzwingen.
Was bedeutet vorübergehende Nutzung genau?
Mein Kollege versteht, dass nur das aggregierte Stammverzeichnis eine öffentliche Schnittstelle für die Clients bereitstellt. Clients haben keine Möglichkeit, eine Operation für eine andere Entität als den aggregierten Stamm aufzurufen.
Mein Verständnis der zitierten Sätze ist anders. Ich verstehe, dass es tatsächlich explizit erlaubt, dass Clients Operationen an internen Entitäten aufrufen. Allerdings erst, nachdem man sie von der Wurzel bekommen hat.
Lassen Sie uns ein konkretes Beispiel geben:
Nehmen wir an, a Cart
besteht aus vielen Items
. Jeder Item
hat eine Quantity
. Das Modell sollte den Anwendungsfall "Erhöhen Sie die Menge eines bestimmten Artikels" unterstützen. Es konnten keine Invarianten verletzt werden, die etwas außerhalb des Gegenstands betreffen.
Verstößt ein Modell gegen die oben genannten Regeln, wenn ein Client dies durch einen Anruf tun kann, cart.item(itemId).increaseQuantity()
oder sollte es einem Client nur gestattet sein, einen anzurufen cart.increaseItemQuantity(itemId)
? Was wäre der Vorteil des letzteren?
quelle
cart.increaseItemQuantity(itemId)
, wenn auch aus keinem anderen Grund, als wenn es sich weniger um einen Verstoß gegen das Demeter-Gesetz handelt. Durch Anrufencart.increaseItemQuantity(itemId)
können Sie beispielsweise die Gesamtbeträge des Warenkorbs aktualisieren.Antworten:
Solange
Item
es nicht existieren kann, ohneCart
auch anwesend zu sein, gibt es keinen Unterschied zwischen den beiden Optionen. In beiden Fällen können Invarianten beibehalten werden.Wenn die Methode aktiviert ist
Item
,Item
kann sie den übergeordneten Warenkorb "benachrichtigen", um die Invariante zu überprüfen, wenn sie ihren eigenen Status ändern muss. Dies macht die Dinge etwas komplizierter, da dann eine zyklische Abhängigkeit zwischenItem
und bestehtCart
(was meiner Meinung nach aufgrund der Annahme im ersten Satz und der Tatsache, dass IMO so oder so existieren muss, kein Problem darstellt).Cart
Wenn die Methode aktiviert ist, ist sie einfacher, da keineItem
Referenz erforderlich istCart
. Aber es macht es kompliziert, weil die Methode jetzt nicht nur nach Invarianten sucht und den Zustand ändert. Es muss aber auch sichergestellt werden, dass der Artikel (oder seine ID) dafür gültig istCart
. Im anderen Fall wird dies bereits durch eine Methode behandelt, die einen bestimmten Artikel aus dem Warenkorb abfragt.tl; dr; Beide Optionen haben klare Vor- und Nachteile und keine scheint offensichtlich besser oder schlechter zu sein als andere.
quelle