Finden Sie die DDD-Gesamtwurzel
On Februar 1, 2021 by adminLassen Sie uns das Lieblingsspiel aller spielen und die Gesamtwurzel finden. Verwenden wir die kanonische Problemdomäne Kunde / Bestellung / Bestellpositionen / Produkt. Traditionell sind Kunde, Bestellung und Produkt die ARs, wobei Bestellpositionen Entitäten unter der Bestellung sind. Die Logik dahinter ist, dass Sie Kunden, Bestellungen und Produkte identifizieren müssen, aber eine Bestelllinie ohne eine Bestellung nicht existieren würde. In unserer Problemdomäne haben wir eine Geschäftsregel, die besagt, dass ein Kunde nur eine nicht zugestellte Bestellung haben kann zu einem Zeitpunkt.
Verschiebt dies die Bestellung unter den Stamm des Kundenaggregats? Ich denke, dass dies der Fall ist. Dadurch wird der Kunden-AR jedoch ziemlich groß und kann später zu Parallelitätsproblemen führen.
Oder was wäre, wenn wir eine Geschäftsregel hätten, die besagt, dass ein Kunde ein bestimmtes Produkt nur einmal in seinem Leben bestellen kann. Dies ist ein weiterer Beweis dafür, dass der Kunde die Bestellung besitzen muss.
Aber wenn es darum geht Zum Versand führen sie alle ihre Aktionen für die Bestellung aus, nicht für den Kunden. Es ist ziemlich dumm, den gesamten Kunden laden zu müssen, um eine einzelne Bestellung als geliefert zu markieren.
Dies ist Was ich vorschlage:
class Customer { public Guid Id {get;set;} public string Name { get; set; } public Address Address { get; set; } public IEnumerable<Order> Orders { get; set; } public void PlaceOrder(ThingsInTheOrder thingsInTheOrder) { // Make sure there aren"t any pending orders already. // Make sure they aren"t ordering a Widget if they"ve already ordered a Widget in the past. // Create an Order object and add it to the collection. Raise a domain event to trigger emails and other stuff } } class Order { public Guid Id { get; set; } public IEnumerable<OrderLine> OrderLines { get; set; } public ShippingData {get;set;} public void Ship(ShippedByPerson shippedByPerson, string trackingCode) { // Create a new ShippingData object and assign it from the data passed in. // Publish a domain event } }
Meine größte Sorge ist das Problem der Parallelität und die Tatsache, dass der Orden selbst charakteristisch ist s einer Aggregatwurzel.
Antwort
Was sind die Kriterien für die Definition eines Aggregats?
Kehren wir zu den Grundlagen des großen blauen Buches zurück:
Aggregat: Ein Cluster zugeordneter Objekte, die als Einheit zum Zweck von Datenänderungen behandelt werden . Externe Verweise sind auf ein Mitglied des AGGREGATE beschränkt, das als Root bezeichnet wird. Innerhalb der Grenzen des AGGREGATE gelten eine Reihe von Konsistenzregeln.
Ziel ist es, die Invarianten beizubehalten. Es geht aber auch darum, die lokale Identität richtig zu verwalten, dh Objekte zu identifizieren, die nicht nur eine Bedeutung haben.
Order
und Order line
gehören definitiv zu einem solchen Cluster. Beispiel:
- Wenn Sie eine
Order
löschen, müssen alle Zeilen gelöscht werden. - Zum Löschen einer Zeile müssen möglicherweise die folgenden Zeilen neu nummeriert werden.
- Zum Hinzufügen einer neuen Zeile muss der Zeilennummer anhand aller anderen Zeilen derselben Reihenfolge ermittelt werden.
li> Das Ändern einiger Bestellinformationen, wie z. B. der Währung, kann sich auf die Bedeutung des Preises in den Werbebuchungen auswirken (oder eine Neuberechnung der Preise erforderlich machen).
Hier also das vollständige Aggregat ist erforderlich, um Konsistenzregeln und Invarianten sicherzustellen.
Wann zu stoppen?
Nun beschreiben Sie einige Geschäftsregeln und argumentieren, dass Sie den Kunden berücksichtigen müssen, um sie sicherzustellen als Teil des Aggregats:
Wir haben ein Geschäft Die Regel besagt, dass ein Kunde immer nur eine nicht zugestellte Bestellung gleichzeitig haben kann.
Natürlich, warum nicht. Lassen Sie uns die Auswirkungen sehen: Auf die Bestellung wird immer über den Kunden zugegriffen. Ist dies das wirkliche Leben? Wenn Mitarbeiter die Kisten für die Lieferung der Bestellung füllen, müssen sie den Kunden-Barcode und den Bestell-Barcode lesen, um auf die Bestellung zugreifen zu können Tatsächlich ist die Identität eines Auftrags im Allgemeinen global und nicht lokal für einen Kunden, und diese relative Unabhängigkeit legt nahe, ihn außerhalb des Aggregats zu halten.
Darüber hinaus sehen diese Geschäftsregeln eher als Richtlinien aus: Es ist eine willkürliche Entscheidung des Unternehmens, seinen Prozess nach diesen Regeln durchzuführen. Wenn die Regeln nicht eingehalten werden, ist der Chef möglicherweise unglücklich, aber die Daten sind nicht wirklich inkonsistent. Darüber hinaus kann über Nacht “ pro Kunde jeweils eine nicht zugestellte Bestellung “ zu “ werden zehn nicht zugestellte Bestellungen pro Kunde “ oder sogar “ unabhängig vom Kunden, hundert nicht zugestellte Bestellungen pro Lager „, sodass das Aggregat möglicherweise nicht mehr gerechtfertigt ist.
Antwort
Kurzversion
Der Grund für DDD ist, dass Domänenobjekte Abstraktionen sind, die Ihre funktionalen Domänenanforderungen erfüllen sollten. Wenn Domänenobjekte diese Anforderungen nicht einfach erfüllen können, deutet dies darauf hin, dass Sie möglicherweise die falsche Abstraktion verwenden.
Benennung Domänenobjekte , die Entitätsnomen verwenden, können dazu führen, dass diese Objekte eng miteinander gekoppelt werden und aufgeblähte „Gott“ -Objekte werden, und sie können Probleme wie das in dieser Frage auftretende aufwerfen als „Wo ist der richtige Ort, um p ut die CreateOrder-Methode? „.
Um die Identifizierung des „richtigen“ Aggregatstamms zu vereinfachen, sollten Sie einen anderen Ansatz in Betracht ziehen, bei dem Domänenobjekte auf den funktionalen Geschäftsanforderungen auf hoher Ebene basieren – d. h.Wählen Sie Substantive aus, die auf funktionale Anforderungen und / oder Verhaltensweisen verweisen, die Benutzer des Systems ausführen müssen.
Langversion
DDD ist ein Ansatz für das OO-Design, der zu einem Diagramm der Domänenobjekte im Geschäft führen soll Schicht Ihres Systems – Domänenobjekte sind für die Erfüllung Ihrer Geschäftsanforderungen auf hoher Ebene verantwortlich und sollten sich im Idealfall auf die Datenschicht verlassen können, um beispielsweise die Leistung und Integrität des zugrunde liegenden persistenten Datenspeichers zu gewährleisten.
Eine andere Sichtweise könnten die Aufzählungspunkte in dieser Liste sein.
- Entitätsnomen schlagen normalerweise Datenattribute vor.
- Domänennomen sollten Verhalten vorschlagen
- DDD- und OO-Modellierung befassen sich mit Abstraktionen, die auf funktionalen Anforderungen und der Kerndomänen- / Geschäftslogik basieren.
- Die Business Logic Layer ist dafür verantwortlich, die Domänenanforderungen auf hoher Ebene zu erfüllen.
Eines der häufigsten Missverständnisse in Bezug auf DDD ist, dass Domänenobjekte auf einer bestimmten physischen Realität basieren sollten. Welt „Ding“ (dh ein Substantiv, auf das Sie in der realen Welt verweisen können, das mit allen Arten von Daten / Eigenschaften belegt ist), aber die Daten / Attribute dieser realen Dinge sind nicht unbedingt ein guter Ausgangspunkt, wenn Sie versuchen zu nageln funktionale Anforderungen.
Natürlich sollte Business Logic diese Daten verwenden , aber Domänenobjekte selbst sollten letztendlich Abstraktionen sein, die funktionale Domänenanforderungen und -verhalten darstellen.
Zum Beispiel; Substantive wie Order
oder Customer
implizieren kein Verhalten und sind daher im Allgemeinen nicht hilfreiche Abstraktionen für die Darstellung von Geschäftslogik und Domänenobjekten.
Berücksichtigen Sie bei der Suche nach Abstraktionen, die für die Darstellung von Business Logic nützlich sein können, typische Anforderungen, die ein System möglicherweise erfüllen muss:
- Als Verkäufer habe ich I. Ich möchte eine Bestellung für einen neuen Kunden erstellen, damit ich eine Rechnung für die zu verkaufenden Produkte mit ihren Preisen und Mengen erstellen kann.
- Als Kundendienstberater möchte ich eine ausstehende Bestellung stornieren, damit die Die Bestellung wird von einem Lagerbetreiber nicht ausgeführt.
- Als Kundendienstberater möchte ich eine Bestellposition zurücksenden, damit das Produkt in den Lagerbestand aufgenommen werden kann und die Zahlung über die ursprüngliche Zahlung des Kunden zurückerstattet wird Methode.
- Als Lagerbetreiber möchte ich alle Produkte in einer ausstehenden Bestellung und die Versandinformationen anzeigen, damit ich die Produkte auswählen und über den Kurier versenden kann.
- usw.
Modellieren von Domänenanforderungen mit einem DDD-Ansatz
Berücksichtigen Sie anhand der obigen Liste einige potenzielle Domänenobjekte ts für ein solches Auftragssystem:
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
Als Domänenobjekte stellen diese Abstraktionen dar, die Eigentümer verschiedener Verhaltensdomänenanforderungen sind; In der Tat weisen ihre Substantive stark auf die spezifischen funktionalen Anforderungen hin, die sie erfüllen.
(Möglicherweise ist dort auch eine zusätzliche Infrastruktur vorhanden, z. B. eine EventMediator
, die übergeben werden muss Benachrichtigungen für Beobachter, die wissen möchten, wann eine neue Bestellung erstellt wurde oder wann eine Bestellung versendet wurde usw.
Zum Beispiel muss SalesOrderCheckout
wahrscheinlich Daten über Kunden, Versand und Produkte verarbeiten, hat jedoch nichts mit dem Verhalten bei Versandaufträgen, dem Sortieren ausstehender Bestellungen oder der Ausgabe von Rückerstattungen zu tun.
Für SalesOrderCheckout
Zur Erfüllung seiner Domänenanforderungen gehört die Durchsetzung dieser Geschäftsregeln, z. B. die Verhinderung, dass Kunden zu viele Artikel bestellen, möglicherweise eine Validierung durchführen und möglicherweise Benachrichtigungen für andere Teile des Systems auslösen. All diese Aufgaben können ausgeführt werden, ohne dass Sie sich unbedingt darauf verlassen müssen eines der anderen Objekte.
DDD unter Verwendung von Entitätsnomen zur Darstellung von Domänenobjekten
Ther Es gibt eine Reihe potenzieller Gefahren bei der Behandlung einfacher Substantive wie Order
, Customer
und Product
als Domänenobjekte; Zu diesen Problemen gehören diejenigen, auf die Sie in der Frage anspielen:
- Wenn eine Methode eine
Order
behandelt, eineCustomer
und aProduct
, zu welchem Domänenobjekt gehört es? - Wo befindet sich die aggregierte Wurzel für diese 3 Objekte?
Wenn Sie Entitätsnomen zur Darstellung von Domänenobjekten auswählen, kann eine Reihe von Ereignissen auftreten:
-
Order
,Customer
undProduct
laufen Gefahr, zu“ Gott „-Objekten zu werden. - Es besteht die Gefahr, dass eine einzige
Manager
Gott-Objekt, um alles zusammenzubinden. - Diese Objekte laufen Gefahr, eng miteinander verbunden zu werden. Es kann schwierig sein, die Domänenanforderungen zu erfüllen, ohne
this
(oderself
) - Ein Risiko, „undichte“ Abstraktionen zu entwickeln – dhEs wird erwartet, dass Domänenobjekte Dutzende von
get
/set
-Methoden verfügbar machen, die die Kapselung schwächen (oder, wenn Sie dies nicht tun, einen anderen Programmierer wahrscheinlich später ..). - Ein Risiko, dass Domänenobjekte mit einer komplexen Mischung aus Geschäftsdaten (z. B. Benutzerdateneingabe über eine Benutzeroberfläche) und vorübergehendem Status (z. B. „Verlauf“ von Benutzeraktionen) aufgebläht werden wenn die Reihenfolge geändert wurde).
DDD-, OO-Design- und einfache Modelle
Ein häufiges Missverständnis in Bezug auf DDD- und OO-Design ist, dass „einfache“ Modelle irgendwie “ schlecht „oder ein“ Anti-Muster „. Martin Fowler schrieb einen Artikel, der das anämische Domänenmodell beschreibt – aber wie er im Artikel klarstellt, sollte DDD selbst dem Ansatz einer sauberen Trennung zwischen Schichten nicht „widersprechen“
„Es ist auch hervorzuheben, dass das Einfügen von Verhalten in die Domänenobjekte nicht widersprechen sollte der solide Ansatz, Layering zu verwenden, um zu trennen Bewerten Sie die Domänenlogik anhand von Dingen wie Persistenz und Präsentationsverantwortung. Die Logik, die sich in einem Domänenobjekt befinden sollte, ist die Domänenlogik – Validierungen, Berechnungen, Geschäftsregeln – wie auch immer Sie sie nennen möchten. „
Mit anderen Worten, die Verwendung einfacher Modelle zum Speichern von Geschäftsdaten, die zwischen anderen Ebenen übertragen werden (z. B. ein Auftragsmodell, das von einer Benutzeranwendung übergeben wird, wenn der Benutzer einen neuen Auftrag erstellen möchte), ist nicht dasselbe wie ein „anämisches Domänenmodell“. „einfache“ Datenmodelle sind häufig der beste Weg, um Daten zu verfolgen und Daten zwischen Ebenen zu übertragen (z. B. ein REST-Webdienst, ein Persistenzspeicher, eine Anwendung oder eine Benutzeroberfläche usw.).
Die Geschäftslogik kann die Daten verarbeiten Daten in diesen Modellen und können sie als Teil des Geschäftsstatus verfolgen – übernehmen jedoch nicht unbedingt das Eigentum an diesen Modellen.
Der aggregierte Stamm
Betrachten Sie noch einmal die Beispieldomänenobjekte – SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
es gibt immer noch keine offensichtliche aggregierte Wurzel, aber das tut es Es spielt eigentlich keine Rolle, da diese Domänenobjekte völlig unterschiedliche Verantwortlichkeiten haben, die sich nicht zu überschneiden scheinen.
Funktionell ist es nicht erforderlich, dass die SalesOrderCheckout
mit der PendingOrdersStream
spricht, da dies die Aufgabe der ersteren ist ist abgeschlossen, wenn der Datenbank eine neue Bestellung hinzugefügt wurde. Andererseits kann die PendingOrdersStream
neue Bestellungen aus der Datenbank abrufen. Diese Objekte müssen nicht mit jeder interagieren andere direkt (Vielleicht kann ein Ereignisvermittler Benachrichtigungen zwischen den beiden bereitstellen, aber ich würde erwarten, dass die Kopplung zwischen diesen Objekten sehr locker ist)
Vielleicht ist die aggregierte Wurzel ein IoC-Container, der einen oder mehrere von ihnen injiziert diese Domänenobjekte in einen UI-Controller, der auch andere Infrastrukturen wie EventMediator
und Repository
bereitstellt. Oder vielleicht ist es eine Art leichter Orchestrator-Service, der auf der Business-Ebene sitzt.
Der Aggregatstamm muss nicht unbedingt ein Domänenobjekt sein. Um die Trennung von Bedenken zwischen Domänenobjekten aufrechtzuerhalten, ist es im Allgemeinen eine gute Sache, wenn das Aggregat verwendet wird root ist ein separates Objekt ohne Geschäftslogik.
Kommentare
- Ich habe abgelehnt, weil Ihre Antwort Konzepte aus Entity Framework, einer Microsoft-spezifischen Technologie, mit Domain Driven Design aus einem Buch in Einklang bringt geschrieben von Eric Evans mit dem gleichen Namen. Ihre Antwort enthält einige Aussagen, die in direktem Widerspruch zum DDD-Buch stehen. In dieser Frage wird Entity Framework nicht erwähnt, sie ist jedoch speziell mit DDD gekennzeichnet. Es gibt ‚ auch keine Erwähnung der Persistenz in der Frage, so dass ich ‚ nicht sehe, wo Datenbanktabellen relevant sind.
- @RibaldEddie Vielen Dank, dass Sie sich die Zeit genommen haben, die Antwort und den Kommentar zu überprüfen. Ich stimme zu, dass die Erwähnung persistenter Daten ‚ nicht wirklich in der Antwort enthalten sein muss, also ‚ habe es umformuliert, um das zu entfernen. Der Schwerpunkt der Antwort könnte wie folgt zusammengefasst werden: “ Entitätsnomen sind aufgrund ihrer Tendenz zu ‚ häufig keine sehr guten Domänenobjektklassennamen Werden eng gekoppelte aufgeblähte Gottobjekte „, hoffentlich ist die Botschaft und Argumentation der WRT-Funktionsanforderungen / -verhalten jetzt klarer?
- Das DDD-Buch nicht ‚ habe dieses Konzept IIRC nicht. Es hat Entitäten, die lediglich Klassen sind, die, wenn sie instanziiert werden, eine persistente und eindeutige Identität haben, so dass zwei getrennte Instanzen zwei eindeutige und persistente Dinge implizieren, was im Gegensatz zu Wertobjekten steht, die nicht ‚ sind Sie haben keine Identität und bleiben nicht ‚ im Laufe der Zeit bestehen. In diesem Buch sind sowohl Entities- als auch Value-Objekte Domänenobjekte.
Antwort
in unserer Problemdomäne haben wir Eine Geschäftsregel besagt, dass ein Kunde immer nur eine nicht zugestellte Bestellung gleichzeitig haben kann.
Bevor Sie zu tief in dieses Kaninchenloch vordringen, sollten Sie Greg überprüfen Die Diskussion von Young über setzt Konsistenz und insbesondere:
Welche geschäftlichen Auswirkungen hat ein Fehler?
In vielen Fällen besteht die richtige Antwort nicht darin, dies zu verhindern Das Falsche passiert, aber stattdessen Ausnahmeberichte zu generieren , wenn möglicherweise ein Problem vorliegt.
Vorausgesetzt jedoch, dass mehrere nicht zugestellte Bestellungen vorliegen Eine erhebliche Belastung für Ihr Unternehmen …
Ja, wenn Sie sicherstellen möchten, dass nur eine nicht zugestellte Bestellung vorliegt, muss ein Aggregat vorhanden sein, das alle Bestellungen eines Kunden anzeigen kann .
Dieses Aggregat ist nicht unbedingt das Kundenaggregat .
Es kann sich um eine Bestellwarteschlange oder einen Bestellverlauf handeln, in dem alle Bestellungen für eine bestimmte Bestellung enthalten sind Kunden gehen in die gleiche Warteschlange. Nach Ihren Angaben werden nicht alle Profildaten des Kunden benötigt, sodass diese nicht Teil dieses Aggregats sein sollten.
Aber wenn es um den Versand geht, führen sie alle ihre Aktionen für die Bestellung aus, nicht für den Kunden.
Ja, wenn Sie tatsächlich mit Erfüllung und Erfüllung arbeiten Ziehen Sie Blätter, die Verlaufsansicht ist nicht besonders relevant.
Die Verlaufsansicht benötigt zum Erzwingen Ihrer Invariante nur die Auftrags-ID und ihren aktuellen Verarbeitungsstatus. Dies muss nicht unbedingt Teil desselben Aggregats wie die Bestellung sein. Denken Sie daran, dass es bei Aggregatgrenzen darum geht, Änderungen zu verwalten und keine Ansichten zu strukturieren.
Es kann also sein, dass Sie die Bestellung als Aggregat behandeln und den Auftragsverlauf als separates Aggregat und koordinieren Sie die Aktivität zwischen den beiden.
Antwort
Sie haben eingerichtet ein Beispiel für eine Strohperson. Es ist zu simpel und ich bezweifle, dass es ein reales System widerspiegelt. Ich würde diese Entitäten und ihr zugehöriges Verhalten nicht so modellieren, wie Sie es aus diesem Grund angegeben haben.
Ihre Klassen müssen das modellieren Status einer Bestellung in einer Weise, die sich in mehreren Aggregaten widerspiegelt. Wenn der Kunde das System beispielsweise in den Zustand versetzt, in dem die Bestellanforderung des Kunden verarbeitet werden muss, kann ich ein Domänenentitätsobjektaggregat mit dem Namen CustomerOrderRequest
oder oder auch nur CustomerOrder
oder eine andere Sprache, die das Unternehmen verwendet, und es könnte einen Zeiger sowohl auf den Kunden als auch auf die Auftragszeilen enthalten und dann eine Methode wie canCustomerCompleteOrder()
, das von der Serviceschicht aufgerufen wird.
Dieses Domänenobjekt würde die Geschäftslogik enthalten, um zu bestimmen, ob die Bestellung gültig ist oder nicht.
Wenn die Bestellung gültig und verarbeitet wäre, hätte ich eine Möglichkeit, dieses Objekt auf ein anderes Objekt zu übertragen, das die verarbeitete Bestellung darstellt.
Ich denke, das Problem mit Ihrem Verständnis ist, dass Sie ein Gerät verwenden stark vereinfachtes Beispiel für Aggregate. Ein PendingOrder
kann ein eigenes Aggregat sein, das von einem UndeliveredOrder
und erneut von einer oder eine CancelledOrder
oder was auch immer.
Kommentare
- Obwohl Ihr Versuch Wenn geschlechtsneutrale Sprache amüsant ist, würde ich bemerken, dass Frauen niemals auf Feldern stehen, um Krähen abzuschrecken.
- @RobertHarvey, dass ‚ eine seltsame Sache ist, auf die man sich konzentrieren sollte in meinem Beitrag. Vogelscheuchen und Bildnisse sind im Laufe der Geschichte regelmäßig in weiblicher Form aufgetreten.
- Sie hätten ‚ in Ihrem Beitrag nicht unterschieden, wenn Sie nicht ‚ halte es nicht für wichtig. Aus sprachlichen Gründen lautet der Begriff “ Strohmann; “ Vorbehalte gegen Sexismus werden mit ziemlicher Sicherheit von “ Was zum Teufel spricht er über den “ Faktor, der durch die Erfindung Ihres eigenen Begriffs entsteht.
- @RobertHarvey, wenn jemand weiß, was Stroh ist Mann bedeutet, ich ‚ bin sicher, dass sie herausfinden können, was Strohmensch bedeutet, wenn sie ‚ diesen Begriff nicht gehört haben. Können wir uns auf die Substanz meines Beitrags konzentrieren, bitte schreiben Sie Software?
Antwort
Vaughn Vernon erwähnt dies in seinem Buch „Implementieren von domänengesteuertem Design“ am Anfang von Kapitel 7 (Dienste):
„Oft ist der beste Hinweis, dass Sie einen Dienst im Domänenmodell erstellen sollten, wenn sich der Vorgang, den Sie ausführen müssen, anfühlt of place als Methode für ein Aggregat oder ein Wertobjekt „.
In diesem Fall könnte es also einen Domänendienst namens „CreateOrderService“ geben, der eine Kundeninstanz und die Liste der Artikel für die Bestellung verwendet.
class CreateOrderService { public Order CreateOrder(Customer customer, ThingsInTheOrder thingsInTheOrder) { // Get all the orders for the customer // Check if any of the things to be ordered exist in previous orders // If none have been previously ordered, create the order and return it // Otherwise return null } }
Kommentare
- Können Sie bitte näher erläutern, wie der Domänendienst bei der Behebung von Parallelitätsproblemen in der Frage helfen kann?
Schreibe einen Kommentar