Frage Verwalten von DTOs und Mapping im großen .NET-Projekt


Mein Team und ich erstellen große .NET WinForms-Anwendungen. Die Anwendung verwendet verschiedene "Dienste", um Daten von und zu unserer Datenbank zu erhalten. Jeder "Dienst" lebt in seiner eigenen Lösung und behandelt eine bestimmte Art von Daten. So verwaltet beispielsweise unser "ContactsService" das Abrufen / Speichern von Kontakten in unserer Datenbank.

In der Regel haben wir DTOs für jeden Dienst erstellt. Also haben wir vielleicht ein "ContactDTO", das einfache String-Eigenschaften für jedes Stück Daten auf dem Kontakt hat. Jetzt haben wir auch eine Business-Schicht "Contact" -Klasse, die genau die gleichen Eigenschaften und vielleicht ein paar zusätzliche Methoden mit etwas Geschäftslogik hat. Darüber hinaus hat der "ContactsService" eine eigene Contact-Klasse, die von der ContactDTO hydratisiert wird.

Es ist zu einem großen Problem geworden, all unsere DTOs und Kartierungen zu verwalten. Derzeit sieht das Senden eines Kontakts zum Speichern in der Datenbank wie folgt aus:

  • Kartenkunde Kontakt zu ContactDTO
  • Karte ContactDTO zum Service Kontakt
  • Kontakt speichern
  • Kartenservice Kontakt zu ContactDTO
  • Karte KontaktDTO zum Kunden Kontakt

Das fühlt sich einfach beschissen an. Wenn wir unserer Client Contact-Klasse eine Eigenschaft hinzufügen, müssen wir die Eigenschaft und die Zuordnungen an 3-4 Stellen hinzufügen.

Was machen wir falsch und wie können wir unser Leben leichter machen? Wäre es einfacher, etwas wie Json.NET zu verwenden und JSON DTOs zu haben? Wir haben AutoMapper ausgecheckt, aber einige Teammitglieder fanden es zu kompliziert.


8
2017-07-06 18:53


Ursprung


Antworten:


Ich bin schon oft darauf gestoßen, aber in meinem Fall habe ich beschlossen, es zu akzeptieren, weil die Entscheidung, DTOs zu verwenden, eine absichtliche war - ich wollen Meine Service-, Client-, Proxy- und Vertrags-Assemblies müssen sauber getrennt sein.

Bei der Verwendung von WCF ist das Layout, das ich bevorzuge, ungefähr so ​​(unter Verwendung Ihres Kontaktbeispiels):

  • Client-Assembly
    • Kontakt (hat client-y Eigenschaften)
  • Geteilte Verträge
    • Kontakt (der vereinbarte gemeinsame DTO)
  • Proxy-Assembly
    • Verwendet den Kontakt aus der freigegebenen Contracts-Assembly
  • Service Montage
    • Kontakt (hat Service-y-Geschäftslogikmerkmale, z. B. kann dies der Typ sein, der von einem ORM-Layer wie Entity Framework bereitgestellt wird)

Ich teile jetzt die Verträge zwischen dem Service und dem Kunden. Wenn ich sie unabhängig voneinander versionieren muss, kann ich einfach eine Kopie der freigegebenen Contracts-Assembly erstellen und die Proxy-Assembly neu ausrichten, um die Kopie zu verwenden, und dann die beiden Contracts-Assemblys unabhängig voneinander ändern. Aber in den meisten Fällen, in denen ich gearbeitet habe, besitze ich sowohl den Kunden als auch den Service, daher ist es praktisch, die Contracts-Assembly zwischen den beiden zu teilen.

Das ist die einzige Optimierung, die ich mir vorstellen kann, wenn Sie die Architekturentscheidung treffen, Ihre Komponenten mit DTOs zu isolieren, zumindest ohne Code-Generierungs-Tools zu verwenden (ich kenne keine guten, aber ich musste sie nicht untersuchen) .

Bearbeiten: Wenn Sie WCF nicht verwenden, benötigen Sie offensichtlich nicht die Assembly "Proxy".


2
2017-07-06 23:14



AutoMapper ist nicht besonders kompliziert. Wenn Sie den Konventionen folgen, z.B. Contact.Address1 wird ContactAddress1 auf dem DTO Sie müssen nicht viel Code abgesehen von dem Aufruf von Mapper.Map schreiben. Alternativ können Sie die Codegenerierung verwenden, es ist jedoch immer noch schwierig, Änderungen zu verwalten.

Darf ich fragen, warum Sie eine KontaktDTO und einen Service-Kontakt benötigen? Kannst du nicht einfach den Service Contact herumreichen? Ich weiß, dass es nicht die beste Übung ist, aber es könnte dich vor RSI retten

EDIT: Missachten Sie meinen letzten Punkt - aus irgendeinem Grund dachte ich, dass Sie den Dienst Kontakt auf eine Datenbank-Entität wie NHibernate / Entity Framework zuordnen


0
2017-07-06 20:07



Einige Dinge, um den Prozess zu beschleunigen:

Ich schrieb einen Code-Generator, mit dem Sie die Tabelle (n) und Spalten aus der Datenbank auswählen und ein C # -DTO generieren lassen können. Dies spart viel unnötiges Tippen und kann DTOs viel schneller generieren. Ein wenig Zeit im Voraus, aber wenn Sie einen Tisch mit 20 Spalten haben, die Sie zuordnen müssen, hilft es.

Ich schrieb auch Code-Generatoren, um die DTOs gespeicherten Prozeduren für Speicher-, Lösch- und Abfrageoperationen zuzuordnen. Wieder eine Zeitersparnis, weil viele von ihnen sehr ähnlich sind. Wenn Sie viel langwierigen "Grunt" -Code geschrieben haben, denken Sie an einen Code-Generator, um es zu tun.

Verwenden Sie ein Entitätsframework oder einen ORM-Mapper für das Back-End. Dies kann viel Zeit sparen, aber Sie müssen in das ORM-Wissen investieren und lernen, wie es funktioniert.

Erstellen Sie einen allgemeinen Satz von DTOs, die von Client zu Datenbank verwendet werden. Dies ist möglicherweise in einigen Situationen nicht praktikabel, wenn Sie Proxies oder kleinere DTOs für den Client benötigen. Sie können jedoch versuchen, einige allgemeine DTOs zu erhalten, die vom Client an den Server zurückübertragen werden.

Entfernen Sie einige der Ebenen. Ich weiß, dass das ein bisschen ein Anti-Muster klingt, aber in einigen Fällen muss die Zwiebel nicht so dick sein.


0
2017-07-06 23:29