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:
- 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.) - Sollten wir zusätzliche Objekte oder Konstrukte verwenden, um unsere Domänenobjekte von der Schnittstelle zu isolieren?
quelle
Antworten:
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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.
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:
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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?
quelle
Siehe auch den Abschnitt "Datenausbreitung zwischen Ebenen" im Folgenden, der meiner Meinung nach überzeugende Argumente enthält:
http://galaxy.andromda.org/docs/andromda-documentation/andromda-getting-started-java/java/index.html
quelle
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.
quelle
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.
quelle
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.
quelle