Es ist schwierig, Best Practices für etwas so "Flexibles" oder Abstraktes wie ein DTO zu etablieren. Im Wesentlichen sind DTOs nur Objekte für die Datenübertragung. Abhängig vom Ziel oder dem Grund für die Übertragung möchten Sie möglicherweise unterschiedliche "Best Practices" anwenden.
Ich empfehle, Patterns of Enterprise Application Architecture von Martin Fowler zu lesen . Es gibt ein ganzes Kapitel über Muster, in dem DTOs einen wirklich detaillierten Abschnitt erhalten.
Ursprünglich waren sie für die Verwendung in teuren Fernaufrufen konzipiert, bei denen Sie wahrscheinlich viele Daten aus verschiedenen Teilen Ihrer Logik benötigen würden. DTOs würden die Datenübertragung in einem einzigen Anruf durchführen.
Laut dem Autor waren DTOs nicht für die Verwendung in lokalen Umgebungen vorgesehen, aber einige Leute fanden eine Verwendung für sie. Normalerweise werden sie verwendet, um Informationen von verschiedenen POCOs in einer einzigen Entität für GUIs, APIs oder verschiedene Ebenen zu sammeln.
Mit der Vererbung ähnelt die Wiederverwendung von Code eher einer Nebenwirkung der Vererbung als ihrem Hauptziel. Komposition hingegen wird mit der Wiederverwendung von Code als Hauptziel implementiert.
Einige Leute empfehlen die Verwendung von Komposition und Vererbung zusammen, wobei sie die Stärken beider nutzen und versuchen, ihre Schwächen zu mildern. Folgendes ist Teil meines mentalen Prozesses, wenn ich neue DTOs oder neue Klassen / Objekte auswähle oder erstelle:
- Ich verwende Vererbung mit DTOs innerhalb derselben Ebene oder desselben Kontexts. Ein DTO wird niemals von einem POCO erben, ein BLL-DTO wird niemals von einem DAL-DTO erben usw.
- Wenn ich versuche, ein Feld vor einem DTO zu verbergen, überarbeite ich es und verwende stattdessen möglicherweise die Komposition.
- Wenn ich nur sehr wenige verschiedene Felder von einem Basis-DTO benötige, füge ich sie einem universellen DTO hinzu. Universelle DTOs werden nur intern verwendet.
- Ein Basis-POCO / DTO wird so gut wie nie für eine Logik verwendet, so dass die Basis nur auf die Bedürfnisse ihrer Kinder reagiert. Wenn ich jemals die Basis verwenden muss, vermeide ich es, ein neues Feld hinzuzufügen, das seine Kinder niemals verwenden werden.
Einige von ihnen sind vielleicht nicht die "besten" Praktiken, sie funktionieren ziemlich gut für die Projekte, an denen ich gearbeitet habe, aber Sie müssen sich daran erinnern, dass keine Größe für alle passt. Beim universellen DTO sollten Sie vorsichtig sein, meine Methodensignaturen sehen folgendermaßen aus:
public void DoSomething(BaseDTO base) {
//Some code
}
Wenn eine der Methoden jemals ein eigenes DTO benötigt, übernehme ich die Vererbung und normalerweise muss ich nur den Parameter ändern, obwohl ich in bestimmten Fällen manchmal tiefer gehen muss.
Aus Ihren Kommentaren gehe hervor, dass Sie verschachtelte DTOs verwenden. Wenn Ihre verschachtelten DTOs nur aus einer Liste anderer DTOs bestehen, ist es meines Erachtens das Beste, die Liste auszupacken.
Abhängig von der Datenmenge, die Sie anzeigen oder bearbeiten müssen, ist es möglicherweise eine gute Idee, neue DTOs zu erstellen, die die Daten begrenzen. Wenn Ihr UserDTO beispielsweise viele Felder hat und Sie nur 1 oder 2 benötigen, ist es möglicherweise besser, ein DTO nur mit diesen Feldern zu haben. Das Definieren der Ebene, des Kontexts, der Verwendung und des Nutzens eines DTO wird beim Entwerfen sehr hilfreich sein.