Warum sollte ich meine Domain-Entitäten von meiner Präsentationsschicht isolieren?

85

Ein Teil des domänengesteuerten Designs, über den es nicht viele Details zu geben scheint, ist, wie und warum Sie Ihr Domänenmodell von Ihrer Benutzeroberfläche isolieren sollten. Ich versuche meine Kollegen davon zu überzeugen, dass dies eine gute Praxis ist, aber ich scheine nicht viel Fortschritte zu machen ...

Sie verwenden Domänenentitäten, wo immer sie möchten, in den Präsentations- und Schnittstellenebenen. Wenn ich ihnen argumentiere, dass sie Anzeigemodelle oder DTOs verwenden sollten, um die Domänenschicht von der Schnittstellenschicht zu isolieren, kontern sie, dass sie den Geschäftswert nicht sehen, wenn sie so etwas tun, weil Sie jetzt ein UI-Objekt pflegen müssen sowie das ursprüngliche Domain-Objekt.

Ich suche nach konkreten Gründen, die ich verwenden kann, um dies zu belegen. Speziell:

  1. Warum sollten wir in unserer Präsentationsschicht keine Domänenobjekte verwenden?
    (Wenn die Antwort offensichtlich ist: "Entkopplung", erklären Sie bitte, warum dies in diesem Zusammenhang wichtig ist.)
  2. Sollten wir zusätzliche Objekte oder Konstrukte verwenden, um unsere Domänenobjekte von der Schnittstelle zu isolieren?
Mark Rogers
quelle
Diese Frage sollte im Wiki sein.
Syed Tayyab Ali
@ m4bwav - Es sollte ein Wiki sein, da es so formuliert ist, dass es eher zur Diskussion als zu einer einzigen richtigen Antwort einlädt.
Rob Allen
1
@ m4bwav: Ich denke, Ihre Frage war eher eine Meinung als eine echte Frage ... Ich habe versucht, dies zu korrigieren (Sie möchten sie möglicherweise weiter bearbeiten), aber seien Sie sich bewusst, dass dies ohne angemessene Sorgfalt erscheinen könnte trolling.
Shog9
5
Ok, Backup, ich stelle eine berechtigte Frage, wie würde das jemanden beleidigen? An wen wende ich mich?
Mark Rogers
@ m4bwav: Du zielst auf deinen Strohmann. Die "große Anzahl von Personen", mit denen Sie dies in Ihrer Frage diskutieren.
Shog9

Antworten:

48

Der Grund liegt ganz einfach in der Implementierung und Drift. Ja, Ihre Präsentationsschicht muss über Ihre Geschäftsobjekte Bescheid wissen, um sie richtig darstellen zu können. Ja, anfangs sieht es so aus, als ob sich die Implementierung der beiden Objekttypen stark überschneidet. Das Problem ist, dass im Laufe der Zeit auf beiden Seiten Dinge hinzugefügt werden. Die Präsentation ändert sich, und die Anforderungen der Präsentationsschicht umfassen Dinge, die völlig unabhängig von Ihrer Geschäftsschicht sind (z. B. Farbe). In der Zwischenzeit ändern sich Ihre Domänenobjekte im Laufe der Zeit. Wenn Sie keine angemessene Entkopplung von Ihrer Schnittstelle haben, besteht die Gefahr, dass Sie Ihre Schnittstellenschicht durch scheinbar harmlose Änderungen an Ihren Geschäftsobjekten vermasseln.

Persönlich glaube ich, dass der beste Weg, Dinge anzugehen, das streng erzwungene Schnittstellenparadigma ist; Das heißt, Ihre Geschäftsobjektschicht stellt eine Schnittstelle bereit, mit der nur auf diese Weise kommuniziert werden kann. Es werden keine Implementierungsdetails (dh Domänenobjekte) über die Schnittstelle angezeigt. Ja, dies bedeutet, dass Sie Ihre Domänenobjekte an zwei Standorten implementieren müssen. Ihre Schnittstellenschicht und in Ihrer BO-Schicht. Diese Neuimplementierung mag zwar zunächst als zusätzliche Arbeit erscheinen, hilft jedoch dabei, die Entkopplung durchzusetzen, die irgendwann in der Zukunft Tonnen von Arbeit einsparen wird.

Paul Sonier
quelle
2
Was meinen Sie mit "Implementieren Ihrer Domänenobjekte an zwei Standorten"?
Jlembke
10
Das kommt mir einfach albern vor. Warum die zusätzliche Arbeit jetzt erledigen, die möglicherweise in Zukunft Arbeit spart? 9 mal von 10 müssen Sie nie die Änderung vornehmen, die "TONNEN" Arbeit spart.
Beep Beep
13
@LuckyLindy: 99 von 100 (eigentlich mehr) Mal muss ich nicht angeschnallt sein, um mich nicht zu verletzen. In dem einen Fall, in dem ich es wirklich brauche, wird es mich (wahrscheinlich) davon abhalten, getötet oder schwer verletzt zu werden. Eine Unze Vorbeugung ist ein Pfund Heilung wert. Ich vermute, Ihre Meinung dazu wird sich ändern, wenn Sie mehr Erfahrung haben.
Paul Sonier
19

Ich habe selbst damit gekämpft. Es gibt Fälle, in denen die Verwendung eines DTO in der Gegenwart sinnvoll ist. Angenommen, ich möchte eine Dropdown-Liste der Unternehmen in meinem System anzeigen und benötige deren ID, um den Wert zu binden.

Anstatt ein CompanyObject zu laden, das möglicherweise Verweise auf Abonnements enthält oder wer weiß was noch, könnte ich ein DTO mit dem Namen und der ID zurücksenden. Dies ist meiner Meinung nach eine gute Verwendung.

Nehmen Sie nun ein anderes Beispiel. Ich habe ein Objekt, das eine Schätzung darstellt. Diese Schätzung kann aus Arbeit, Ausrüstung usw. bestehen. Es kann viele vom Benutzer definierte Berechnungen enthalten, die alle diese Elemente aufnehmen und zusammenfassen (jede Schätzung kann bei verschiedenen Typen unterschiedlich sein von Berechnungen). Warum sollte ich dieses Objekt zweimal modellieren müssen? Warum kann ich meine Benutzeroberfläche nicht einfach über die Berechnungen aufzählen und anzeigen lassen?

Im Allgemeinen verwende ich keine DTOs, um meine Domänenschicht von meiner Benutzeroberfläche zu isolieren. Ich verwende sie, um meine Domänenschicht von einer Grenze zu isolieren, die außerhalb meiner Kontrolle liegt. Die Idee, dass jemand Navigationsinformationen in sein Geschäftsobjekt einfügt, ist lächerlich. Verunreinigen Sie Ihr Geschäftsobjekt nicht.

Die Idee, dass jemand sein Geschäftsobjekt validieren würde? Nun, ich sage, das ist eine gute Sache. Ihre Benutzeroberfläche sollte nicht allein für die Validierung Ihrer Geschäftsobjekte verantwortlich sein. Ihre Geschäftsschicht MUSS ihre eigene Validierung durchführen.

Warum sollten Sie UI-Generierungscode in ein Geschäftsobjekt einfügen? In meinem Fall habe ich separate Objekte, die den UI-Code separat von der UI generieren. Ich habe sperate Objekte, die meine Geschäftsobjekte in XML rendern. Die Idee, dass Sie Ihre Ebenen trennen müssen, um diese Art von Kontamination zu verhindern, ist mir so fremd, denn warum sollten Sie überhaupt HTML-Generierungscode in ein Geschäftsobjekt einfügen ...

Bearbeiten Wie ich ein bisschen mehr denke, gibt es Fälle, in denen UI-Informationen in die Domänenschicht gehören könnten. Dies könnte die so genannte Domänenschicht trüben, aber ich habe an einer mandantenfähigen Anwendung gearbeitet, die sich sowohl im Erscheinungsbild der Benutzeroberfläche als auch im funktionalen Workflow sehr unterschiedlich verhält. Abhängig von verschiedenen Faktoren. In diesem Fall hatten wir ein Domänenmodell, das die Mandanten und ihre Konfiguration darstellte. Ihre Konfiguration enthielt zufällig Informationen zur Benutzeroberfläche (z. B. Beschriftungen für generische Felder).

Wenn ich meine Objekte so gestalten musste, dass sie dauerhaft sind, sollte ich sie dann auch duplizieren müssen? Denken Sie daran, wenn Sie jetzt ein neues Feld hinzufügen möchten, haben Sie zwei Stellen, an denen Sie es hinzufügen können. Vielleicht wirft dies eine andere Frage auf, wenn Sie DDD verwenden. Sind alle Objekte Objekte der persistierten Entitäten? Ich weiß in meinem Beispiel, dass sie es waren.

JoshBerke
quelle
Würden die für verschiedene Mieter unterschiedlichen Bezeichnungen nicht für jeden Mieter eine andere allgegenwärtige Sprache anzeigen? Ich denke, es muss das Konzept eines Metamodells geben, bei dem eine Domäne unter Mietern mit einer Übersetzungsschicht für ihre Interpretation des Metamodells geteilt wird.
Kell
16

Sie tun dies aus demselben Grund, aus dem Sie SQL von Ihren ASP / JSP-Seiten fernhalten.

Wenn Sie nur ein Domänenobjekt für die Verwendung in der Präsentations- UND Domänenebene behalten, wird dieses eine Objekt bald monolithisch. Es beginnt, UI-Validierungscode, UI-Navigationscode und UI-Generierungscode einzuschließen. Dann fügen Sie bald alle Business-Layer-Methoden hinzu. Jetzt sind Ihre Geschäftsschicht und die Benutzeroberfläche alle durcheinander, und alle spielen auf der Ebene der Domänenentität herum.

Sie möchten dieses raffinierte UI-Widget in einer anderen App wiederverwenden? Nun, Sie müssen eine Datenbank mit diesem Namen, diesen beiden Schemata und diesen 18 Tabellen erstellen. Sie müssen auch Hibernate und Spring (oder die Frameworks Ihrer Wahl) konfigurieren, um die Geschäftsvalidierung durchzuführen. Oh, Sie müssen auch diese 85 anderen nicht verwandten Klassen einschließen, da auf sie in der Geschäftsschicht verwiesen wird, die sich zufällig in derselben Datei befindet.

digitaljoel
quelle
13

Ich bin nicht einverstanden.

Ich denke, der beste Weg ist, mit Domänenobjekten in Ihrer Präsentationsebene zu beginnen, bis es Sinn macht, etwas anderes zu tun.

Entgegen der landläufigen Meinung können "Domänenobjekte" und "Wertobjekte" in der Präsentationsebene problemlos nebeneinander existieren. Und dies ist der beste Weg, dies zu tun - Sie profitieren von beiden Welten, einer reduzierten Duplizierung (und einem Boilerplate-Code) mit den Domänenobjekten. und die Anpassung und konzeptionelle Vereinfachung der Verwendung von Wertobjekten über Anforderungen hinweg.

Daniel Alexiuc
quelle
Vielen Dank für Ihre Eingabe, ich sehe, woher Sie kommen. Ich sage zwar nicht, dass dies keine der unendlichen Möglichkeiten ist, ein erfolgreiches Projekt zu erstellen, aber es scheint dem "Domain-Driven Design" -Stil zu widersprechen, der für größere und komplexere Projekte gilt, die schwieriger zu warten sind auf Dauer.
Mark Rogers
Nein, das ist falsch und genau deshalb sind so viele Websites anfällig für SQL-Injection.
Remi
7

Die Antwort hängt vom Umfang Ihrer Bewerbung ab.


Einfache CRUD-Anwendung (Erstellen, Lesen, Aktualisieren, Löschen)

Für grundlegende Rohölanwendungen haben Sie keine Funktionalität. Das Hinzufügen von DTO zu Entitäten wäre Zeitverschwendung. Dies würde die Komplexität erhöhen, ohne die Skalierbarkeit zu erhöhen.

Geben Sie hier die Bildbeschreibung ein


Mäßig komplizierte Nicht-CRUD-Anwendung

In dieser Anwendungsgröße haben Sie nur wenige Entitäten, denen ein echter Lebenszyklus und eine damit verbundene Geschäftslogik zugeordnet sind.

Das Hinzufügen von DTOs zu diesem Fall ist aus mehreren Gründen eine gute Idee:

  • Die Präsentationsschicht kann nur eine Teilmenge der Felder sehen, über die die Entität verfügt. Sie kapseln Entitäten
  • Keine Kopplung zwischen Backend und Frontent
  • Wenn Sie Geschäftsmethoden innerhalb von Entitäten haben, jedoch nicht in DTO, bedeutet das Hinzufügen von DTOs, dass externer Code den Status Ihrer Entität nicht ruinieren kann.

Geben Sie hier die Bildbeschreibung ein


Komplizierte Unternehmensanwendung

Eine einzelne Entität benötigt möglicherweise mehrere Darstellungsweisen. Jeder von ihnen benötigt unterschiedliche Felder. In diesem Fall treten dieselben Probleme wie im vorherigen Beispiel auf und Sie müssen die Anzahl der für jeden Client sichtbaren Felder steuern. Wenn Sie für jeden Client ein separates DTO haben, können Sie auswählen, was sichtbar sein soll.

Geben Sie hier die Bildbeschreibung ein

Marcin Szymczak
quelle
4

Wir verwenden das gleiche Modell auf dem Server und auf der Benutzeroberfläche. Und es ist ein Schmerz. Wir müssen es eines Tages umgestalten.

Die Probleme liegen hauptsächlich darin, dass das Domänenmodell in kleinere Teile geschnitten werden muss, um es serialisieren zu können, ohne dass auf die gesamte Datenbank verwiesen wird. Dies erschwert die Verwendung auf dem Server. Wichtige Links fehlen. Einige Typen sind auch nicht serialisierbar und können nicht an den Client gesendet werden. Zum Beispiel 'Typ' oder eine generische Klasse. Sie müssen nicht generisch sein und Type muss als Zeichenfolge übertragen werden. Dies erzeugt zusätzliche Eigenschaften für die Serialisierung, sie sind redundant und verwirrend.

Ein weiteres Problem ist, dass die Entitäten auf der Benutzeroberfläche nicht wirklich passen. Wir verwenden Datenbindung und viele Entitäten haben viele redundante Eigenschaften nur für UI-Zwecke. Darüber hinaus enthält das Entitätsmodell viele "BrowsableAttribute" und andere. Das ist wirklich schlimm.

Am Ende denke ich, dass es nur eine Frage ist, welcher Weg einfacher ist. Es kann Projekte geben, bei denen es gut funktioniert und bei denen kein weiteres DTO-Modell geschrieben werden muss.

Stefan Steinegger
quelle
2
Wenn Sie die Datenbindung verwenden möchten, führen Sie eine Linq-Abfrage aus und binden Sie sie an einen anonymen Typ. Auf diese Weise können Sie die Hierarchie reduzieren und ändern. Sie können damit auch das Filtern und Sortieren sehr gut implementieren.
JoshBerke
@ Josh: Danke für den Rat. Dies könnte teilweise funktionieren. Ich bin selbst kein GUI-Programmierer und beschäftige mich nicht viel mit GUI-Konzepten. Das Problem tritt in Fällen auf, in denen die Daten manipuliert und an den Server zurückgesendet werden.
Stefan Steinegger
3

Es geht größtenteils um Abhängigkeiten. Die Kernfunktionsstruktur der Organisation hat ihre eigenen funktionalen Anforderungen, und die Benutzeroberfläche sollte es den Mitarbeitern ermöglichen, den Kern zu ändern und anzuzeigen. Der Kern selbst sollte jedoch nicht erforderlich sein, um die Benutzeroberfläche aufzunehmen. (Wenn es passieren muss, ist dies normalerweise ein Hinweis darauf, dass der Kern nicht eigenschaftsgestaltet ist.)

Mein Buchhaltungssystem hat eine Struktur und Inhalte (und Daten), die den Betrieb meines Unternehmens modellieren sollen. Diese Struktur ist real und existiert unabhängig davon, welche Buchhaltungssoftware ich verwende. (Ein bestimmtes Softwarepaket enthält zwangsläufig Struktur und Inhalt für sich selbst, aber ein Teil der Herausforderung besteht darin, diesen Aufwand zu minimieren.)

Grundsätzlich hat eine Person einen Job zu erledigen. Das DDD sollte dem Ablauf und Inhalt des Jobs entsprechen. Bei DDD geht es darum, alle zu erledigenden Aufgaben so vollständig und unabhängig wie möglich zu machen. Dann erleichtert die Benutzeroberfläche hoffentlich die Erledigung der Aufgabe so transparent wie möglich und so produktiv wie möglich.

Bei Schnittstellen handelt es sich um die Eingaben und Ansichten, die für den ordnungsgemäß modellierten und unveränderlichen Funktionskern bereitgestellt werden.

dkretz
quelle
3

Verdammt, ich schwöre, das sagte Beharrlichkeit.

Wie auch immer, es ist ein weiteres Beispiel für dasselbe: Das Gesetz von Parnas besagt, dass ein Modul ein Geheimnis bewahren sollte, und das Geheimnis ist eine Anforderung, die sich ändern kann. (Bob Martin hat eine Regel, die eine andere Version davon ist.) In einem System wie diesem kann sich die Darstellung unabhängig von der Domäne ändern . Wie zum Beispiel ein Unternehmen, das die Preise in Euro beibehält und in den Unternehmensbüros Französisch verwendet, aber die Preise in Dollar mit Text in Mandarin darstellen möchte. Die Domain ist dieselbe; Die Präsentation kann sich ändern. Um die Sprödigkeit des Systems zu minimieren, dh die Anzahl der Dinge, die geändert werden müssen, um eine Änderung der Anforderungen zu implementieren, trennen Sie die Bedenken.

Charlie Martin
quelle
2

Ihre Präsentation verweist möglicherweise auf Ihre Domain-Ebene, es sollte jedoch keine direkte Bindung von Ihrer Benutzeroberfläche an Ihre Domain-Objekte bestehen. Domänenobjekte sind nicht für die Verwendung auf der Benutzeroberfläche vorgesehen, da sie bei ordnungsgemäßem Design häufig auf Verhaltensweisen und nicht auf Datendarstellungen basieren. Zwischen der Benutzeroberfläche und der Domäne sollte eine Zuordnungsebene vorhanden sein. MVVM oder MVP ist hierfür ein gutes Muster. Wenn Sie versuchen, Ihre Benutzeroberfläche direkt an die Domain zu binden, werden Sie wahrscheinlich selbst Kopfschmerzen bekommen. Sie haben zwei verschiedene Zwecke.

jlembke
quelle
1

Möglicherweise konzipieren Sie die UI-Ebene nicht breit genug. Denken Sie an mehrere Antwortformen (Webseiten, Sprachantwort, gedruckte Briefe usw.) und an mehrere Sprachen (Englisch, Französisch usw.).

Angenommen, die Sprachmaschine für das Telefonanrufsystem läuft auf einem völlig anderen Computertyp (z. B. Mac) als der Computer, auf dem die Website ausgeführt wird (z. B. Windows).

Natürlich ist es leicht, in die Falle zu tappen. "Nun, in meiner Firma kümmern wir uns nur um Englisch, betreiben unsere Website unter LAMP (Linux, Apache, MySQL und PHP) und jeder verwendet dieselbe Version von Firefox." Aber was ist mit in 5 oder 10 Jahren?

JonnyBoats
quelle
1

Mit Hilfe von Tools wie ' Value Injecter ' und dem Konzept von 'Mappers' in der Präsentationsebene beim Arbeiten mit Ansichten ist es viel einfacher, jeden Code zu verstehen. Wenn Sie ein wenig Code haben, werden Sie die Vorteile nicht sofort erkennen, aber wenn Ihr Projekt immer mehr wächst, werden Sie sehr glücklich sein, wenn Sie mit den Ansichten arbeiten, um nicht in die Logik der Dienste eingehen zu müssen. Repositorys, um das Ansichtsmodell zu verstehen. View Model ist eine weitere Wache in der riesigen Welt der Antikorruptionsschicht und in einem langfristigen Projekt Gold wert.

Der einzige Grund, warum ich keinen Vorteil bei der Verwendung des Ansichtsmodells sehe, ist, dass Ihr Projekt klein und einfach genug ist, um Ansichten direkt an jede Eigenschaft Ihres Modells zu binden. Wenn jedoch in Zukunft die Anforderungsänderung und einige Steuerelemente in den Ansichten nicht an das Modell gebunden werden und Sie kein Ansichtsmodellkonzept haben, werden Sie an vielen Stellen Patches hinzufügen und einen Legacy-Code erhalten Sie werden es nicht zu schätzen wissen. Sicher, Sie können einige Umgestaltungen vornehmen, um Ihr Ansichtsmodell in ein Ansichtsansichtsmodell umzuwandeln und dem YAGNI-Prinzip zu folgen, ohne Code hinzuzufügen, wenn Sie ihn nicht benötigen, aber für mich ist es viel mehr eine bewährte Methode, die ich befolgen muss, um a hinzuzufügen Präsentationsschicht, die nur Ansichtsmodellobjekte verfügbar macht.

Samuel
quelle
1

Hier ist ein reales Beispiel dafür, warum es für mich empfehlenswert ist, Domänenentitäten von der Ansicht zu trennen.

Vor einigen Monaten habe ich eine einfache Benutzeroberfläche erstellt, um die Werte von Stickstoff, Phosphor und Kalium in einer Bodenprobe anhand einer Reihe von drei Messgeräten anzuzeigen. Jedes Messgerät hatte einen roten, grünen und roten Bereich, dh Sie konnten entweder zu wenig oder zu viel von jeder Komponente haben, aber in der Mitte befand sich ein sicherer Grünwert.

Ohne viel nachzudenken modellierte ich meine Geschäftslogik, um Daten für diese 3 chemischen Komponenten und ein separates Datenblatt bereitzustellen, das Daten über die akzeptierten Werte in jedem der 3 Fälle enthielt (einschließlich der verwendeten Maßeinheit, dh Mol oder Prozentsatz). Ich habe dann meine Benutzeroberfläche so modelliert, dass sie ein ganz anderes Modell verwendet. Dieses Modell befasste sich mit Beschriftungen, Werten, Grenzwerten und Farben.

Das bedeutete, als ich später 12 Komponenten zeigen musste, habe ich die zusätzlichen Daten einfach in 12 neue Modelle mit Messgeräteansicht abgebildet und sie wurden auf dem Bildschirm angezeigt. Es bedeutete auch, dass ich die Messgerätesteuerung problemlos wiederverwenden und andere Datensätze anzeigen lassen konnte.

Wenn ich diese Messgeräte direkt in meine Domänenentitäten eingekoppelt hätte, hätte ich keine der oben genannten Flexibilität und zukünftige Änderungen würden Kopfschmerzen bereiten. Ich bin beim Modellieren von Kalendern in der Benutzeroberfläche auf sehr ähnliche Probleme gestoßen. Wenn ein Kalendertermin bei mehr als 10 Teilnehmern rot werden muss, sollte die Geschäftslogik, um dies zu handhaben, in der Geschäftsschicht verbleiben und der gesamte Kalender in der Benutzeroberfläche muss wissen, dass er angewiesen wurde rot werden, es sollte nicht wissen müssen warum.

Adrian Thompson Phillips
quelle
-1

Der einzig sinnvolle Grund für das Hinzufügen einer zusätzlichen Zuordnung zwischen verallgemeinerter und domänenspezifischer Semantik besteht darin, dass Sie über einen vorhandenen Code (und Tools) verfügen (Zugriff darauf), der auf einer verallgemeinerten (aber abbildbaren) Semantik basiert, die sich von Ihrer Domänensemantik unterscheidet.

Domänengesteuerte Designs funktionieren am besten in Verbindung mit einem orthogonalen Satz funktionaler Domänenframeworks (wie ORM, GUI, Workflow usw.). Denken Sie immer daran, dass nur in den Nachbarschaften der äußeren Schicht die Domänensemantik verfügbar gemacht werden muss. In der Regel ist dies das Front-End (GUI) und das persistente Back-End (RDBM, ORM). Alle effektiv gestalteten dazwischenliegenden Schichten können und sollten domäneninvariant sein.

Alphazero
quelle
Absatz 1: Erstellen Sie keine unnötige Abstraktion (z. B. wiederverwendbare Komponenten), es sei denn, Sie teilen sie tatsächlich für verschiedene Apps. Abs. 2: Ich frage mich, wie generische GUIs in so vielen verschiedenen Domänen funktionieren. Bemerkung: Diese Branche ist so kaputt, dass sie nicht einmal mehr lustig ist ...
Alphazero