Ich beschäftige mich mit Domain Driven Design und einige der Konzepte, denen ich begegne, sind auf den ersten Blick sehr sinnvoll, aber wenn ich mehr über sie nachdenke, muss ich mich fragen, ob das wirklich eine gute Idee ist.
Das Konzept der Aggregate macht zum Beispiel Sinn. Sie erstellen kleine Eigentumsdomänen, damit Sie sich nicht mit dem gesamten Domänenmodell befassen müssen.
Wenn ich im Zusammenhang mit einer Web-App darüber nachdenke, stoßen wir häufig auf die Datenbank, um kleine Teilmengen von Daten zurückzugewinnen. Zum Beispiel kann eine Seite nur die Anzahl der Bestellungen mit Links auflisten, auf die Sie klicken können, um die Bestellung zu öffnen und ihre Bestell-IDs anzuzeigen.
Wenn ich recht Verständnis Aggregates bin, würde ich in der Regel die Repository - Muster verwenden , um eine OrderAggregate zurück, die die Mitglieder enthalten würde GetAll
, GetByID
, Delete
, und Save
. OK das hört sich gut an. Aber...
Wenn ich GetAll aufrufe, um alle meine Bestellungen aufzulisten, scheint es mir, dass für dieses Muster die gesamte Liste der gesammelten Informationen zurückgegeben werden muss, Bestellungen, Bestellpositionen usw. abgeschlossen werden müssen. Wenn ich nur eine kleine Teilmenge dieser Informationen benötige (nur Header-Informationen).
Vermisse ich etwas? Oder gibt es eine Optimierungsstufe, die Sie hier verwenden würden? Ich kann mir nicht vorstellen, dass sich irgendjemand dafür aussprechen würde, ganze Informationsaggregate zurückzugeben, wenn Sie sie nicht benötigen.
Sicherlich könnte man Methoden auf Ihrem Repository wie erstellen GetOrderHeaders
, aber das scheint den Zweck der Verwendung eines Musters wie Repository an erster Stelle zu zunichte zu machen.
Kann das jemand für mich klären?
BEARBEITEN:
Nach viel mehr Recherche denke ich, dass die Trennung hier darin besteht, dass sich ein reines Repository-Muster von dem unterscheidet, was die meisten Leute von einem Repository halten.
Fowler definiert ein Repository als einen Datenspeicher, der Auflistungssemantik verwendet und im Allgemeinen im Arbeitsspeicher aufbewahrt wird. Dies bedeutet, dass ein komplettes Objektdiagramm erstellt wird.
Evans ändert das Repository so, dass es Aggregate Roots enthält, und daher wird das Repository amputiert, um nur die Objekte in einem Aggregat zu unterstützen.
Die meisten Leute scheinen Repositorys als verherrlichte Datenzugriffsobjekte zu betrachten, bei denen Sie nur Methoden erstellen, um die gewünschten Daten abzurufen. Dies scheint nicht die Absicht zu sein, die in Fowlers Patterns of Enterprise Application Architecture beschrieben wird.
Wieder andere betrachten ein Repository als eine einfache Abstraktion, die in erster Linie dazu dient, das Testen und Verspotten zu vereinfachen oder die Persistenz vom Rest des Systems zu entkoppeln.
Ich denke, die Antwort ist, dass dies ein viel komplexeres Konzept ist, als ich zuerst dachte.
quelle
Antworten:
Verwenden Sie Ihr Domain-Modell und Ihre Aggregate nicht zum Abfragen.
Was Sie fragen, ist in der Tat eine Frage, die weit genug verbreitet ist, dass eine Reihe von Grundsätzen und Mustern festgelegt wurde, um genau das zu vermeiden. Es heißt CQRS .
quelle
Ich habe Probleme mit der optimalen Verwendung des Repository-Musters in einem domänengetriebenen Design und habe immer noch Probleme. Nachdem ich es jetzt zum ersten Mal benutzt habe, habe ich die folgenden Methoden entwickelt:
Ein Repository sollte einfach sein. Es ist nur für das Speichern und Abrufen von Domänenobjekten verantwortlich. Alle andere Logik sollte sich in anderen Objekten wie Fabriken und Domänendiensten befinden.
Ein Repository verhält sich wie eine Sammlung, als ob es sich um eine Sammlung aggregierter Stammdaten im Arbeitsspeicher handelt.
Ein Repository ist kein generisches DAO, jedes Repository verfügt über eine eindeutige und enge Schnittstelle. Ein Repository verfügt häufig über bestimmte Finder-Methoden, mit denen Sie die Sammlung anhand der Domäne durchsuchen können (z. B. alle offenen Bestellungen für Benutzer X). Das Repository selbst kann mit Hilfe eines generischen DAO implementiert werden.
Im Idealfall geben die Finder-Methoden nur aggregierte Wurzeln zurück. Wenn dies zu ineffizient ist, kann es auch schreibgeschützte Wertobjekte zurückgeben, die genau das enthalten, was Sie benötigen (obwohl es ein Plus ist, wenn diese Wertobjekte auch in Bezug auf die Domäne ausgedrückt werden können). Als letzter Ausweg kann das Repository auch verwendet werden, um Teilmengen oder Sammlungen von Teilmengen einer aggregierten Wurzel zurückzugeben.
Entscheidungen wie diese hängen von den verwendeten Technologien ab, da Sie einen Weg finden müssen, um Ihr Domain-Modell mit den verwendeten Technologien am effizientesten auszudrücken.
quelle
Ich glaube nicht, dass Ihre GetOrderHeaders-Methode den Zweck des Repositorys überhaupt zunichte macht.
DDD ist (unter anderem) darum bemüht, sicherzustellen, dass Sie das, was Sie benötigen, über das aggregierte Stammverzeichnis erhalten (Sie hätten zum Beispiel kein OrderDetailsRepository), aber es schränkt Sie nicht in der Art und Weise ein, wie Sie es erwähnen.
Wenn ein OrderHeader ein Domain-Konzept ist, sollten Sie es als solches definieren und über die entsprechenden Repository-Methoden zum Abrufen verfügen. Stellen Sie einfach sicher, dass Sie dabei die richtige Gesamtwurzel durchlaufen.
quelle
Meine Verwendung von DDD wird möglicherweise nicht als "reines" DDD betrachtet, aber ich habe die folgenden praktischen Strategien für die Verwendung von DDD für einen DB-Datenspeicher angepasst.
** Sie müssen kein gesamtes Aggregat zurückbringen. Wenn Sie jedoch mehr möchten, müssen Sie den Root fragen, nicht einen anderen Dienst oder ein anderes Repository. Dies ist verzögertes Laden und kann entweder manuell mit verzögertem Laden durch arme Leute (Injizieren des entsprechenden Repositorys / Dienstes in das Stammverzeichnis) oder mit einem ORM durchgeführt werden, der dies unterstützt.
In Ihrem Beispiel würde ich wahrscheinlich einen Repository-Aufruf bereitstellen, der nur die Auftragskopfzeilen enthält, wenn ich die Details in einem separaten Aufruf laden möchte. Beachten Sie, dass wir mit einem "OrderHeader" tatsächlich ein zusätzliches Konzept in die Domain einführen.
quelle
Ihr Domain-Modell enthält Ihre Geschäftslogik in ihrer reinsten Form. Alle Beziehungen und Vorgänge, die den Geschäftsbetrieb unterstützen. Was Sie in Ihrer konzeptionellen Zuordnung vermissen, ist die Idee des Application Service Layers, den der Service Layer um das Domänenmodell umschließt und eine vereinfachte Ansicht der Geschäftsdomäne bietet (eine Projektion, wenn Sie so wollen), die es ermöglicht, das Domänenmodell nach Bedarf zu ändern ohne die Anwendungen über die Service-Schicht direkt zu beeinflussen.
Weitergehen. Die Idee des Aggregats ist, dass es ein Objekt gibt, die Aggregatwurzel, die für die Aufrechterhaltung der Konsistenz des Aggregats verantwortlich ist. In Ihrem Beispiel ist der Auftrag für die Manipulation seiner Auftragspositionen verantwortlich.
In Ihrem Beispiel würde die Serviceebene eine Operation wie GetOrdersForCustomer verfügbar machen, die nur das zurückgibt, was zum Anzeigen einer Zusammenfassungsliste der Bestellungen (wie Sie sie OrderHeaders nennen) erforderlich ist.
Schließlich ist das Repository-Muster nicht NUR eine Sammlung, sondern ermöglicht auch deklarative Abfragen. In C # können Sie LINQ als Abfrageobjekt verwenden , oder die meisten anderen O / RMs stellen auch eine Abfrageobjektspezifikation bereit.
Da Sie Abfragen für das Repository erstellen können, ist es auch sinnvoll, praktische Methoden bereitzustellen, die allgemeine Abfragen verarbeiten. Wenn Sie also nur die Überschriften Ihrer Bestellung benötigen, können Sie eine Abfrage erstellen, die nur die Überschriften zurückgibt, und diese mithilfe einer praktischen Methode in Ihren Repositorys verfügbar machen.
Hoffe das hilft Dinge zu klären.
quelle
Ich weiß, dass dies eine alte Frage ist, aber ich scheine zu einer anderen Antwort gekommen zu sein.
Wenn ich ein Repository erstelle, werden im Allgemeinen einige zwischengespeicherte Abfragen eingeschlossen.
Bewahren Sie diese Repositorys im RAM Ihres Servers auf. Sie übergeben nicht nur Objekte an die Datenbank!
Wenn ich in einer Webanwendung mit einer Seite bin, auf der Bestellungen aufgelistet sind, auf die Sie klicken können, um Details anzuzeigen, möchte ich wahrscheinlich, dass auf der Seite mit der Bestellauflistung Details zu den Bestellungen angezeigt werden (ID, Name, Betrag, Datum). um einem Benutzer bei der Entscheidung zu helfen, welchen er sich ansehen möchte.
Zu diesem Zeitpunkt haben Sie zwei Möglichkeiten.
Sie können die Datenbank abfragen und genau das zurückholen, was Sie für die Auflistung benötigen. Anschließend können Sie eine erneute Abfrage durchführen, um die einzelnen Details abzurufen, die auf der Detailseite angezeigt werden sollen.
Sie können eine Abfrage durchführen, die alle Informationen abruft und im Cache speichert. Auf der nächsten Seitenanforderung lesen Sie den RAM des Servers anstelle der Datenbank. Wenn der Benutzer auf die nächste Seite klickt oder diese auswählt, werden immer noch keine Fahrten in die Datenbank ausgeführt.
In Wirklichkeit ist es genau das, was Sie implementieren, und das Implementierungsdetail. Wenn mein größter Benutzer 10 Bestellungen hat, möchte ich wahrscheinlich Option 2 wählen. Wenn ich über 10.000 Bestellungen spreche, wird Option 1 benötigt. In beiden oben genannten Fällen und in vielen anderen Fällen möchte ich, dass das Repository diese Implementierungsdetails verbirgt.
Wenn ich zukünftig ein Ticket bekomme, um dem Benutzer zu sagen, wie viel sie im letzten Monat auf der Seite mit der Bestellauflistung für Bestellungen ( aggregierte Daten ) ausgegeben haben , würde ich lieber die Logik schreiben, um diese in SQL zu berechnen und eine weitere Hin- und Rückreise zu tätigen die DB oder möchten Sie sie lieber anhand der Daten berechnen, die sich bereits im RAM des Servers befinden?
Nach meiner Erfahrung bieten Domänenaggregate enorme Vorteile.
quelle