DDD-Ansatz für grundlegende CRUD-Operationen in einer komplexen domänenzentrierten Anwendung

9

Mein Unternehmen schreibt unsere Webanwendung von Grund auf neu. Es ist eine große Anwendung auf Unternehmensebene mit einer komplexen Domäne in der Finanzbranche.

Wir verwenden ein ORM (Entity Framework) für die Persistenz.

Im Wesentlichen konzentriert sich die Hälfte unserer Anwendung darauf, Rohdaten vom Benutzer zu sammeln, zu speichern, und die andere Hälfte der Anwendung, die den größten Teil unserer tatsächlichen Domänenlogik enthält, verwendet diese Rohdaten, um unser Domänenbild zu erstellen, das sich stark von den ursprünglichen unterscheidet Roheingaben und Übergabe an eine Calc-Engine, Ausführen von Calcs und Ausspucken von Ergebnissen, die dem Benutzer dann angezeigt werden.

Bei einem DDD-Ansatz unter Verwendung von Schichten scheinen CRUD-Operationen die Domänenschicht zu durchlaufen. aber zumindest in unserem Fall scheint dies keinen Sinn zu ergeben.

Wenn ein Benutzer zum Bearbeitungsbildschirm wechselt, um beispielsweise ein Anlagekonto zu ändern, sind die Felder auf dem Bildschirm die genauen Felder, die in der Datenbank gespeichert sind, nicht die später für Berechnungen verwendete Domänendarstellung. Warum sollte ich die Domänendarstellung des Anlagekontos laden, wenn der Bearbeitungsbildschirm die Datenbankdarstellung (Roheingaben) benötigt?

Nachdem der Benutzer auf dem Bildschirm des Anlagekontos auf "Fertig" geklickt hat und ein POST an den Controller gesendet wurde, verfügt der Controller nun über eine ziemlich genaue Datenbankdarstellung des zu speichernden Anlagekontos. Aber aus irgendeinem Grund sollte ich die Domänendarstellung laden, um Änderungen vorzunehmen, anstatt nur das Modell des Controllers direkt dem Datenbankmodell zuzuordnen (Entity Framework-Modell).

Im Wesentlichen ordne ich ein Datenmodell dem Domänenmodell zu, damit es dann wieder dem Datenmodell zugeordnet werden kann, um zu bestehen. Wie macht das Sinn?

wired_in
quelle

Antworten:

9

Stellen Sie sich vor, Sie implementieren Ihre Kontoerstellungsseite, indem Sie den Formularbeitrag direkt einem EF-Objekt zuordnen, das dann in der Datenbank gespeichert wird.

Nehmen wir weiter an, dass die Datenbank verschiedene Einschränkungen aufweist, die verhindern, dass völlig falsche Daten eingegeben werden. Konten haben immer Kunden usw.

Alles scheint in Ordnung zu sein. Aber dann macht das Geschäft eine neue Regel.

  • An einem Donnerstag erstellte Konten erhalten einen Bonuszinssatz von 2%. (Angenommen, der Zinssatz ist eines der Kontofelder.)

Jetzt müssen Sie diese Logik irgendwo platzieren und haben kein Domänenobjekt, in das Sie sie einfügen können.

DDD geht davon aus, dass Sie diese Art von Regeln immer haben werden, und das tun Sie wahrscheinlich auch. Das Erstellen eines Kontos muss verschiedene Überprüfungen, ein Überwachungsprotokoll usw. enthalten. Es muss nicht nur "eine Zeile in die Datenbank schreiben" sein.

Planen Sie Ihre Domain unter der Annahme, dass keine Persistenz- oder MVC-Controller mit zusätzlicher Logik vorhanden sind. Stellen Sie sicher, dass Sie alle Anforderungen erfassen und alle im Domain-Modell enthalten sind.

Ewan
quelle
3
Das ist ein guter Weg, um es auszudrücken. Ich hasse es, Geschäftsregeln zu finden, die mit DB-Details gemischt sind. +1
candied_orange
Gute Punkte, aber was ist, wenn diese Validierungsregeln nur während der Erstellung und Aktualisierung der Benutzereingaben gelten? Sobald wir die Benutzereingaben haben, ist das Modell, das beim Ausführen von Berechnungen erstellt wird, ein völlig anderes Modell. Sollten wir zwei Domain-Modelle für ein Anlagekonto haben? Eine für CRUD-Operationen von Roheingaben für den Benutzer und eine andere für den Fall, dass diese Eingaben zum Erstellen des in Berechnungen verwendeten Domänenmodells verwendet werden?
Wired_in
meine Fragen verwirren. Sie müssen ein vollständiges Beispiel geben. Wenn Sie über eine Domänenlogik verfügen, sollte diese in ein Domänenobjekt integriert werden. Das bedeutet nicht, dass Sie später kein weiteres Domain-Objekt aus dem ersten erstellen können
Ewan
Stellen Sie sich eine komplexe Berechnungsmaschine vor. Eine der Eingaben, die zum Ausführen von Berechnungen erforderlich sind, ist ein Anlagekonto, aber das gesamte Anlagekonto für die Berechnungsmaschine ist über einen bestimmten Zeitraum eine Einnahmequelle. Dieses Domänenmodell eines Anlagekontos unterscheidet sich vollständig von den Roheingaben, die der Benutzer für dieses Anlagekonto eingegeben hat. Wenn der Benutzer grundlegende Eingaben wie Name, aktueller Wert usw. eingibt, muss noch eine Validierungslogik vorhanden sein, die jedoch nichts mit dem von der Calc-Engine verwendeten Modell zu tun haben sollte. Gibt es hier also zwei Domain-Modelle für ein Anlagekonto?
Wired_in
..... oder vielleicht ist ein Investment-Account-Modell in der Domain für CRUD-Operationen übertrieben und es sollten nur einige Validator-Attribute verwendet werden oder so
wired_in
7

Wie macht das Sinn?

Kurze Antwort: nicht .

Längere Antwort: Die Schwergewichtsmuster für die Entwicklung eines Domänenmodells gelten nicht für die Teile Ihrer Lösung, die nur eine Datenbank sind.

Udi Dahan hatte eine interessante Beobachtung , die helfen könnte, dies zu klären

Dahan ist der Ansicht, dass ein Dienst sowohl über Funktionen als auch über Daten verfügen muss. Wenn es keine Daten hat, ist es nur eine Funktion. Wenn nur CRUD-Operationen an Daten ausgeführt werden, handelt es sich um eine Datenbank.

Der Zweck des Domänenmodells besteht schließlich darin, sicherzustellen, dass alle Aktualisierungen der Daten die aktuelle Geschäftsinvariante beibehalten. Oder anders ausgedrückt: Das Domänenmodell ist dafür verantwortlich, dass die Datenbank, die als Aufzeichnungssystem fungiert, korrekt ist.

Wenn Sie mit einem CRUD-System arbeiten, sind Sie normalerweise nicht das Aufzeichnungssystem für die Daten. Die reale Welt ist das Buch der Aufzeichnungen, und Ihre Datenbank ist nur eine lokal zwischengespeicherte Darstellung der realen Welt.

Zum Beispiel haben die meisten Informationen, die in einem Benutzerprofil angezeigt werden, wie eine E-Mail-Adresse oder eine von der Regierung herausgegebene Identifikationsnummer, eine Wahrheitsquelle, die außerhalb Ihres Unternehmens liegt - es ist der E - Mail-Administrator eines anderen, der E-Mail-Adressen zuweist und widerruft, nicht Ihre App. Es ist die Regierung, die SSNs zuweist, nicht Ihre App.

Daher werden Sie normalerweise keine Domain-Validierung für die Daten durchführen, die von außen zu Ihnen kommen. Möglicherweise sind Überprüfungen vorhanden, um sicherzustellen, dass die Daten gut geformt und ordnungsgemäß bereinigt sind . Aber es sind nicht Ihre Daten - Ihr Domain-Modell erhält kein Veto.

Bei einem DDD-Ansatz unter Verwendung von Schichten scheinen CRUD-Operationen die Domänenschicht zu durchlaufen. aber zumindest in unserem Fall scheint dies keinen Sinn zu ergeben.

Das ist richtig für den Fall, dass die Datenbank das Buch der Aufzeichnung ist .

Ouarzy drückte es so aus .

Bei der Arbeit mit viel Legacy-Code beobachte ich jedoch häufige Fehler, um festzustellen, was sich innerhalb der Domäne befindet und was sich außerhalb befindet.

Eine Anwendung kann nur dann als CRUD betrachtet werden, wenn das Datenmodell keine Geschäftslogik enthält. Selbst in diesem (seltenen) Fall ist Ihr Datenmodell nicht Ihr Domänenmodell. Dies bedeutet lediglich, dass wir keine Abstraktion benötigen, um diese zu verwalten, da keine Geschäftslogik beteiligt ist, und daher kein Domänenmodell haben.

Wir verwenden das Domänenmodell, um die Daten zu verwalten, die zur Domäne gehören. Die Daten von außerhalb der Domain werden bereits an einem anderen Ort verwaltet - wir speichern nur eine Kopie zwischen.

Greg Young verwendet Lagersysteme als primäres Beispiel für Lösungen, bei denen sich das Buch der Aufzeichnung an einem anderen Ort befindet (dh in der Lagerhalle). Die Implementierung, die er beschreibt, ist Ihrer sehr ähnlich - eine logische Datenbank zum Erfassen von Nachrichten, die vom Warehouse empfangen wurden, und eine separate logische Datenbank, in der die aus der Analyse dieser Nachrichten gezogenen Schlussfolgerungen zwischengespeichert werden.

Vielleicht haben wir hier zwei begrenzte Kontexte? Jeweils mit einem anderen Modell für eineinvestment account

Vielleicht. Ich würde es nur ungern als begrenzten Kontext kennzeichnen, da nicht klar ist, welches andere Gepäck damit einhergeht. Es kann sein, dass Sie zwei Kontexte haben, es kann ein Kontext mit subtilen Unterschieden in der allgegenwärtigen Sprache sein, die Sie noch nicht aufgegriffen haben.

Möglicher Lackmustest: Für wie viele Domain-Experten benötigen Sie zwei Domain-Experten, um dieses Spektrum abzudecken, oder nur einen, der auf unterschiedliche Weise über die Komponenten spricht. Grundsätzlich können Sie möglicherweise erraten, wie viele begrenzte Kontexte Sie haben, indem Sie das Conway-Gesetz rückwärts anwenden.

Wenn Sie der Ansicht sind, dass begrenzte Kontexte mit Diensten ausgerichtet sind, ist dies möglicherweise einfacher: Sollten Sie in der Lage sein, diese beiden Funktionen unabhängig voneinander bereitzustellen? Ja schlägt zwei begrenzte Kontexte vor; aber wenn sie synchron gehalten werden müssen, dann ist es vielleicht nur eine.

VoiceOfUnreason
quelle
Nun, es gibt eine Validierungs- und Standardlogik, die jedoch nur beim Erstellen / Aktualisieren der Roheingaben für ein Anlagekonto gilt. Dann verwenden wir ein viel umfangreicheres Modell für das Anlagekonto, wenn wir es als Eingabe für eine Calc-Engine verwenden. Vielleicht haben wir hier zwei begrenzte Kontexte? Jeweils mit einem anderen Modell für ein Anlagekonto '
wired_in
Ich bin erst nach einigen Jahren darauf zurückgekommen, und Ihr Kommentar findet aus irgendeinem Grund mehr Resonanz als zuvor. Hier gibt es viele gute Sachen, aber können Sie mir eines klarstellen? Sie sagten: "Der Zweck des Domänenmodells besteht schließlich darin, sicherzustellen, dass alle Aktualisierungen der Daten die aktuelle Geschäftsinvariante beibehalten." Dies gilt für den Teil unserer App, der Informationen speichert / aktualisiert. Der andere Teil ist nur eine Berechnungsmaschine. Es nimmt eine Darstellung der Daten als Eingaben und spuckt Ergebnisse aus. Ist das dann nicht Teil des Domain-Modells? Da es gespeicherte Daten nicht beeinflusst?
wired_in
2

In Ihrer Domain sollten Sie nicht wissen müssen, dass die Datenbank überhaupt existiert.

In Ihrer Domain geht es um Geschäftsregeln. Das Zeug, das überleben muss, wenn das Unternehmen, das Ihre Datenbank erstellt hat, sein Geschäft aufgibt. Das heißt, wenn Sie möchten, dass Ihr Unternehmen überlebt. Es ist wirklich schön, wenn es diesen Regeln egal ist, dass Sie die Art und Weise geändert haben, wie Sie Daten beibehalten.

Datenbankdetails sind vorhanden und müssen behandelt werden. Sie sollten woanders wohnen. Setzen Sie sie über eine Grenze. Kontrollieren Sie sorgfältig, wie Sie über diese Grenze hinweg kommunizieren oder ob es sich nicht um eine Grenze handelt.

Onkel Bob hat folgendes zu sagen, in was Sie Ihre Daten eintragen sollen:

Typischerweise sind die Daten, die die Grenzen überschreiten, einfache Datenstrukturen. Wenn Sie möchten, können Sie grundlegende Strukturen oder einfache Datenübertragungsobjekte verwenden. Oder die Daten können einfach Argumente in Funktionsaufrufen sein. Oder Sie können es in eine Hashmap packen oder in ein Objekt konstruieren.

Wichtig ist, dass isolierte, einfache Datenstrukturen über die Grenzen hinweg weitergegeben werden. Wir wollen keine Entitäten oder Datenbankzeilen betrügen und übergeben. Wir möchten nicht, dass die Datenstrukturen irgendeine Abhängigkeit haben, die gegen die Abhängigkeitsregel verstößt.

[…] Wenn wir Daten über eine Grenze übergeben, haben sie immer die Form, die für den inneren Kreis am bequemsten ist.

Saubere Architektur

Er erklärt auch, wie Ihre äußeren Schichten Plugins zu Ihren inneren Schichten sein sollten, damit innere Schichten nicht einmal wissen, dass äußere Schichten existieren.

Sauberes Architektur-Spickzettel

Befolgen Sie so etwas und Sie haben einen guten Ort, um die Datenbank zu ignorieren, wo Sie sich Gedanken über Regeln zur Eingabevalidierung machen können, Regeln, die irgendwie beibehalten werden müssen, Regeln zum Ausführen von Berechnungen, Regeln zum Senden dieser Ergebnisse an eine beliebige Ausgabe. Es ist tatsächlich einfacher, diese Art von Code zu lesen.

Entweder das oder Sie entscheiden, dass Ihre Domain wirklich nur dazu dient, die Datenbank zu manipulieren. In diesem Fall ist Ihre Domänensprache SQL. Wenn dies in Ordnung ist, erwarten Sie jedoch nicht, dass Ihre Implementierung der Geschäftsregeln eine Änderung der Persistenz überlebt. Am Ende müssen Sie sie komplett neu schreiben.

candied_orange
quelle
Wir verwenden ein ORM (Entity Framework), daher ist unsere Datenbank bereits abstrahiert, aber die Datenmodelle (Entity Framework-Klassen) sind mit den Datenbanktabellen natürlich ziemlich 1 zu 1. Das Problem ist, dass in einigen Teilen unserer Anwendung der Benutzer im Wesentlichen nur das Datenmodell aktualisiert (der Bildschirm ist nur eine Liste von Textfeldern, wobei jedes Textfeld ein Feld in der Datenbank ist (Datenmodell).
Wired_in
Daher sehe ich keinen Grund, bei CRUD-Operationen nicht nur Darstellungen der Rohdaten (Datenmodell) zu verwenden. Wir haben eine komplexe Domänenrepräsentation, die für Berechnungen verwendet wird. Dies ist das, was ich als unser Domänenmodell betrachte, aber ich verstehe nicht, warum ich dieses Bild in den CRUD-Teil unserer Anwendung laden würde.
Wired_in
Definieren Sie, was Sie unter "Darstellungen der Rohdaten verwenden" verstehen. Daten werden eingegeben, Daten werden gemäß Domänenregeln validiert, Daten werden irgendwie beibehalten, Daten werden gegen berechnet, Ergebnisse werden an was auch immer ausgegeben. Vermisse ich etwas
candied_orange
Ich versuche zu sagen, dass die Rohdaten, die wir vom Benutzer für ein Anlagekonto erhalten, nicht so sind, wie wir dieses Anlagekonto in den Hauptteilen unserer Anwendung darstellen, wie wenn es für Berechnungen verwendet wird. Beispielsweise haben wir möglicherweise eine boolesche Eingabe, die wir in der Datenbank IsManagedAccount speichern. Der Benutzer liefert uns dies über ein Optionsfeld auf dem Bearbeitungsbildschirm. Die Darstellung von der Datenbank bis zum Bildschirm beträgt also 1 zu 1. Wenn wir unser Domänenmodell später in der Anwendung erstellen, verfügen wir möglicherweise über eine ManagedAccount-Klasse, also keine boolesche Eigenschaft. Die beiden Strukturen sind sehr unterschiedlich.
Wired_in
Wenn der Benutzer also nur die Roheingaben auf einem Bearbeitungsbildschirm bearbeitet, warum sollte ich dann das Domänenbild laden und dann eine Menge Komplexität hinzufügen, um die stark typisierte ManagedAccount-Klasse einer flachen Darstellung zuzuordnen, die nur eine einzelne Klasse mit einem IsManagedAccount ist Eigentum?
Wired_in
1

Anwendung der DDD-Theorie:

In dieser Domäne gibt es zwei begrenzte Kontexte:

  • Die Berechnungen des Anlagekontos. Das mathematische Modell des Anlagekontos ist ein Element, möglicherweise ein Aggregat.
  • Kernfinanzierung. Das Kundeninvestitionskonto ist eines der Unternehmen.

Jeder begrenzte Kontext kann ein anderes architektonisches Design haben.

Beispiel:

Das Kundeninvestitionskonto ist eine Entität (möglicherweise ein Aggregat, abhängig von der Domäne), und die Persistenz der Daten erfolgt über das Repository der Entität (RDB oder ein anderer DB-Typ wie eine OO-Datenbank).

Es gibt keinen DDD-Ansatz für CRUD-Operationen. Wenn ein DB-Feld an die Daten eines Objekts gebunden wird, werden die Entwurfsprinzipien verletzt.

Derek
quelle