Sollte ich ein DTO sowohl auf der Client- als auch auf der Serverseite einer Domänenentität zuordnen?

StackOverflow https://stackoverflow.com/questions/2201150

Frage

Ich habe ein umfangreiches Domänenmodell, in dem die meisten Klassen über ein bestimmtes Verhalten und einige Eigenschaften verfügen, die entweder berechnet werden oder die Eigenschaften von Mitgliedsobjekten offenlegen (das heißt, dass die Werte dieser Eigenschaften niemals beibehalten werden).

Mein Client kommuniziert nur über WCF mit dem Server.

Daher habe ich für jede Domänenentität ein entsprechendes DTO – eine einfache Darstellung, die nur Daten enthält – sowie eine Mapper-Klasse, die implementiert DtoMapper<DTO,Entity> und kann eine Entität über ein statisches Gateway in ihr DTO-Äquivalent oder umgekehrt konvertieren:

var employee = Map<Employee>.from_dto<EmployeeDto>();

Auf der Serverseite dieser Anwendung geht es hauptsächlich um Persistenz, wobei meine DTOs vom WCF-Dienst eingehen, deserialisiert werden und sie dann von einem beliebigen ORM in der Datenbank gespeichert werden oder eine Abfrageanforderung von WCF eingeht und der ORM diese Abfrage ausführt Die DB und gibt Objekte zurück, die von WCF serialisiert und zurückgesendet werden sollen.

Angesichts dieses Szenarios Ist es sinnvoll, meinen Persistenzspeicher den Domänenentitäten zuzuordnen, oder sollte ich ihn einfach direkt den DTOs zuordnen?

Wenn ich Domänenentitäten verwende, wäre der Ablauf

  1. Client fordert Objekt an
  2. WCF überträgt die Anfrage an den Server
  3. ORM fragt die Datenbank ab und gibt Domänenentitäten zurück
  4. Domänenentitäten, die vom Mapper in DTOs umgewandelt werden
  5. WCF serialisiert DTO und kehrt zum Client zurück
  6. Client deserialisiert DTO
  7. DTO wird vom Mapper in eine Domänenentität umgewandelt
  8. Ansichtsmodelle erstellt, usw.

Ähnliches auf der Rückfahrt

Wenn ich direkt DTO zuordne, kann ich eine Zuordnung pro Objekt und Anfrage eliminieren.Was verliere ich dadurch?

Das Einzige, was mir in den Sinn kommt, ist eine weitere Möglichkeit zur Validierung vor dem Einfügen/Aktualisieren, da ich keine Garantie dafür habe, dass das DTO jemals einer Validierung unterzogen wurde oder überhaupt als Domäneneinheit existierte, bevor es über die Leitung gesendet wurde, und ich vermute, dass es eine Chance dafür gibt Bei Auswahl validieren (wenn ein anderer Prozess möglicherweise ungültige Werte in die Datenbank eingegeben hat).Gibt es andere Gründe?Reichen diese Gründe aus, um die zusätzlichen Mapping-Schritte zu rechtfertigen?

bearbeiten:

Ich habe oben „willkürliches ORM“ gesagt, und ich möchte, dass die Dinge so ORM- und Persistenz-unabhängig wie möglich sind, aber wenn Sie etwas Besonderes hinzufügen möchten, das spezifisch für NHibernate ist, tun Sie dies auf jeden Fall.

War es hilfreich?

Lösung

Ich persönlich würde empfehlen, Ihre Zuordnung auf der Serverseite zu halten. Sie haben wahrscheinlich eine Menge Arbeit den Aufbau Ihres Design auf den Punkt, es ist auf jetzt getan; nicht werfen, dass weg.

Überlegen Sie, was ein Web-Service ist. Es ist nicht nur eine Abstraktion über Ihre ORM; es ist ein Vertrag . Es ist eine öffentliche API für Ihre Kunden, sowohl intern als auch extern.

Eine öffentliche API sollte wenig, wenn aus irgendeinem Grund zu ändern. Fast jede Änderung an eine API, abgesehen vom Hinzufügen neue Typen und Methoden, ist eine unterbrechende Änderung. Aber Ihre Domain-Modell wird nicht so streng sein. Sie müssen es von Zeit zu Zeit ändern, wie Sie neue Funktionen hinzuzufügen oder Fehler in dem ursprünglichen Entwurf zu entdecken. Sie wollen in der Lage sein, um sicherzustellen, dass Änderungen an Ihrem internen Modells verursachen keine Änderungen durch den Dienst des Vertrags kaskadieren.

Es ist eigentlich eine gängige Praxis (I Leser nicht mit dem Begriff „best practice“ beleidigen wird) spezifische Request und Response Klassen für jede Nachricht aus einem ähnlichen Grund zu schaffen; wird es viel einfacher, die Fähigkeit bestehender Dienste und Methoden zu erweitern, ohne sie zu brechen Veränderungen sein.

Kunden wahrscheinlich nicht tun wollen genau das gleiche Modell, das Sie intern in den Dienst nutzen. Wenn Sie Ihr einziger Kunde sind, dann vielleicht scheint dies transparent, aber wenn Sie externe Kunden haben und haben gerade, wie weit ihre Auslegung des Systems kann oft gesehen, dann werden Sie den Wert nicht so dass Ihr perfektes Modell verstehen lecken aus den Grenzen des Service-API.


Und manchmal, es ist nicht einmal möglich Ihr Modell zurück über die API zu senden. Es gibt viele Gründe, warum dies geschehen kann:

  • Cycles im Objektgraphen. Vollkommen in Ordnung in OOP; katastrophal in Serialisierung. Sie am Ende zu schmerzhafte Dauer Entscheidungen über die „Richtung“ der Graph machen muß serialisiert werden. Auf der anderen Seite, wenn Sie einen DTO verwenden, können Sie in serialisiert welche Richtung auch immer Sie wollen, was auch immer die Aufgabe in der Hand paßt.

  • Der Versuch, bestimmte Arten von Vererbungsmechanismen über SOAP / REST verwenden kann bestenfalls eine Flickschusterei sein. Der im alten Stil XML Serializer unterstützt zumindest xs:choice; DataContract nicht der Fall, und ich werde nicht Rationale Haarspalterei vorbei, aber es genügt zu sagen, dass Sie wahrscheinlich einige Polymorphismus in Ihrem reichen Domain-Modell haben und es ist verdammt nahe unmöglich, dass auf Kanal durch den Webservice.

  • Fauler / verzögertes Laden, die Sie wahrscheinlich Gebrauch machen, wenn Sie ein ORM verwenden. Es ist peinlich genug dafür, dass es richtig serialisiert wird - zum Beispiel mit Linq to SQL Entitäten, nicht WCF nicht einmal den faulen loader auslösen, wird es nur null in das Feld setzen, wenn Sie es manuell laden - aber das Problem wird noch schlimmer für Daten kommen zurück in etwas so einfach wie ein List<T> auto-Eigenschaft, die im Konstruktor initialisiert wird -. häufig genug in einem Domänenmodell - funktioniert einfach nicht in WCF, weil es nicht Ihr Konstruktor nicht aufrufen. Stattdessen haben Sie eine [OnDeserializing] initializer Methode hinzufügen, und Sie wirklich nicht Ihr Domain-Modell mit diesem Müll zu vollstopfen wollen.

  • Ich habe auch bemerkt nur die Bemerkung, die Sie NHibernate verwenden. Man bedenke, dass Schnittstellen wie IList<T> können nicht alle über einen Web-Service serialisiert werden! Wenn Sie POCO Klassen mit NHibernate verwenden, da die meisten von uns tun, dann wird dies einfach nicht funktionieren, period.


Es wird auch wahrscheinlich viele Fälle, in denen Sie Ihr internes Domänenmodell einfach nicht die Bedürfnisse des Kunden entspricht, und es macht keinen Sinn, Ihr Domain-Modell zu ändern, um diese Bedürfnisse anzupassen. Als Beispiel hierfür ist etwas so einfaches wie eine Rechnung nehmen lassen. Es muss zeigen:

  • Informationen über das Konto (Kontonummer, Name, etc.)
  • Rechnungsspezifische Daten (Rechnungsnummer, Datum, Fälligkeit, etc.)
  • A / R-Level-Informationen (vorherige Gleichgewicht, späte Gebühren, neue Balance)
  • Produkt oder Service-Informationen für alles auf der Rechnung;
  • Etc.

Dies ist wahrscheinlich paßt fein in einem Domänenmodell. Was aber, wenn der Kunde will einen Bericht auszuführen, die 1200 dieser Rechnungen zeigt? Irgendeine Art von Abstimmreport?

Dieses saugt für die Serialisierung. Jetzt sind Senden Sie 1200 Rechnungen mit den gleichen Daten serialisiert über werden und immer wieder - gleiche Konten, die gleichen Produkte, gleichen A / R. Intern Ihre Anwendung wird die Verfolgung aller Verbindungen; es kennt die Rechnung # 35 und # 45 Rechnung für die gleichen Kunden sind und somit eine Customer Referenz teilen; alle diese Informationen verloren, auf der Serialisierung und Sie am Ende eine lächerliche Menge redundanter Daten zu senden.

Was Sie wirklich wollen, ist es, einen benutzerdefinierten Bericht zu senden, der folgendes beinhaltet:

  • enthalten Alle Konten in dem Bericht, und ihre A / R;
  • Alle Produkte im Bericht enthalten sind;
  • Alle Rechnungen, mit Produkt- und Konto-IDs nur.

Sie müssen zusätzliche „Normalisierung“ führen auf die abgehenden Daten, bevor Sie sie an den Client senden, wenn Sie die massive Redundanz zu vermeiden. Dieser stark begünstigt den DTO-Ansatz; es macht keinen Sinn, diese Struktur in Ihrem Domain-Modell zu haben, weil Sie Ihre Domain-Modell bereits Pflege von Entlassungen nimmt, auf seine eigene Weise.

Ich hoffe, dass diejenigen genug Beispiele und genug Gründe sind Sie zu überzeugen, Ihre Zuordnungen von Domain zu halten <-> Service-Vertrag erhalten. Sie haben absolut das Richtige bisher getan, Sie einen großen Entwurf, und es wäre eine Schande, alle für etwas, das Bemühen zu negieren, die später zu großen Kopfschmerzen führen kann.

Andere Tipps

Sie müssen die DTOs in der Client-Seite abzubilden sowieso, also, für Symmetrie, es ist besser, die inverse Abbildung in der Server-Seite zu machen. So können Sie isolieren Ihre Conversions in gut getrennte Abstraktionsschichten.

Abstraktionsschichten sind nicht nur gut für Validierungen, aber Ihren Code von Änderungen unter / über sie zu isolieren, und machen Sie Ihren Code mehr prüfbar und mit weniger Wiederholungen.

Auch wenn Sie einen großen Performance-Engpass in der zusätzlichen Umwandlung bemerken, denken Sie daran: frühe Optimierung ist die Wurzel allen Übels ist. :)

Sie sollten Ihre Domain-Entitäten auf jeden Fall von Ihren DTOs trennen, da es sich dabei um unterschiedliche Anliegen handelt.DTOs sind in der Regel heriale, selbstbeschreibende Modelle, bei denen Ihre Domänenentitäten andererseits Ihre Geschäftslogik kapseln und mit denen viel Verhalten verbunden ist.

Allerdings bin ich mir nicht sicher, wo sich die zusätzliche Zuordnung befindet?Sie rufen die Daten mithilfe Ihres ORM (auch Domänenentitäten genannt) ab und ordnen diese Objekte Ihren DTOs zu, sodass es dort nur eine Zuordnung gibt?Übrigens, wenn Sie noch nicht so etwas verwenden Automapper um das mühsame Mapping für Sie zu erledigen.

Dieselben DTOs werden dann auf dem Client deserialisiert und von dort aus können Sie sie direkt Ihren UIViewModels zuordnen.Das Gesamtbild sieht also etwa so aus:

  • Der Client fordert eine Entität anhand der ID vom WCF-Dienst an
  • Der WCF-Dienst ruft die Entität aus dem Repository/ORM ab
  • Verwendet einen AutoMapper, um die Entität dem DTO zuzuordnen
  • Der Kunde erhält DTO
  • Verwendet einen AutoMapper zur Zuordnung zum UI-ViewModel
  • UIViewModel ist an die GUI gebunden

Wenn Sie sagen, dass Ihre Server-Seite app „meist“ über Beharrlichkeit ist, glaube ich, dass dies der Schlüssel ist, um darüber nachzudenken. Gibt es wirklich ein serverseitige Domain-Modell, das eine gewisse Intelligenz um die Daten benötigt, die es empfängt oder nicht Ihr WCF-Dienst rein fungieren als Gateway zwischen Ihrem Domain-Modell und dem Datenspeicher?

Auch prüfen, ob Ihre DTO für die Client-Domäne ausgelegt ist.
Ist dies die einzige Client-Domäne, den Zugriff auf diesen Datenspeicher über Ihren Service benötigt?
Sind die serverseitige DTOs flexible oder grobkörnig genug, um eine andere Anwendungsdomäne zu dienen?
Wenn nicht, dann ist es wohl der Mühe wert, die externe Schnittstelle Implementierungen abstrahiert zu halten.

  

(DB-> ORM-> EmployeeEntity-> Client1DTOAssembler-> Client1EmployeeDTO).

Wir haben eine ähnliche Anwendung, bei der ein WCF-Dienst in erster Linie als Tor zum persistenten Datenspeicher fungiert.

In unserem Fall unser Client und Server wieder verwenden nicht die Assembly mit „DTOs.“ Dies gibt uns die Möglichkeit, einfach den Code zu den Teilklassen durch den Dienstverweis erzeugt hinzuzufügen, so dass wir oft in der Lage einen DTO zu verwenden, um auf der Client-Seite, wie sie ist und als Domain-Objekt zu behandeln. Andere Zeiten können wir clientseitige geschützten Domänenobjekte, die als Fassaden zu einem Bündel von persistenten Objekten dienen wir von dem WCF-Dienst bekamen.

Wenn denken Sie über das Verhalten und die berechneten Eigenschaften, die Ihre Domain-Objekte haben, wie viel Überlappung ist es, wirklich zwischen dem Client und Server? In unserem Fall haben wir festgestellt, dass die Aufteilung der Zuständigkeiten zwischen Client und Server bedeutete, dass es sehr klein war, wenn überhaupt, Code, der sowohl für die auf den heutigen Bedarf (und genau das gleiche) sein, Client und Server.

Um Ihre Fragen direkt zu beantworten, wenn Ihr Ziel ist es vollständig zu bleiben Persistenz Agnostiker, ich würde Karte sicherlich Ihre persistenten Speicher auf Ihre Domain-Objekte und dann auf DTOs abzubilden. Es gibt zu viele Persistenz-Implementierungen, die in Ihre Objekte und komplizieren, sie als WCF DTOs bluten können.

Auf der Client-Seite kann es nicht erforderlich sein, eine zusätzliche Zuordnung zu tun, wenn Sie nur Ihre DTOs dekorieren oder erweitern und das ist eine ziemlich einfache Lösung.

Ihre Architektur scheint ziemlich gut durchdacht. Mein Bauch-Gefühl ist, wenn Sie bereits entschieden haben, um die Objekte zu DTO reduzieren sie durch WCF zu schicken, und Sie haben derzeit keine Notwendigkeit für zusätzliche Objektfunktionalität auf der Server-Seite, warum nicht die Dinge einfach und Karte hält Ihre Persistenzspeicher direkt an die DTOs.

Was verlieren Sie? Ich glaube nicht, dass Sie wirklich etwas zu verlieren. Sie sind Architektur sauber und einfach ist. Wenn Sie in Zukunft entscheiden, dass es ein neues Bedürfnis nach mehr Funktionen auf der Server-Seite ist, kann man immer wieder Faktor an diesem Punkt Ihre Domäne Entitäten dort neu zu erstellen.

Ich mag es einfach zu halten und wieder Faktor als später benötigt, versuchen Sie die verfrühte Optimierung Sache zu vermeiden, etc.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top