Was ist die Verwendung von DTO anstelle von Entity?

17

Ich arbeite an einer RCP-Anwendung. Ich bin neu in dieser Anwendung.

Spring Beans werden zum Schreiben von Geschäftslogik zum Speichern / Abrufen von Entitäten verwendet.

Aber anstatt Entitäten direkt an den Client zu senden, konvertieren wir in DTOs und füllen den Client. Während des Speicherns konvertieren wir DTO erneut in Entity und speichern.

Was ist der Vorteil dieser Konvertierungen? Kann das jemand erklären?

Naveen Kocherla
quelle
What's the benefit of these conversions?Entkopplung des Persistenzdatenmodells von dem den Verbrauchern angebotenen Datenmodell (Repräsentation). Die Vorteile der Entkopplung wurden in SE ausführlich diskutiert. Ziel der DTOs ist es jedoch, in einer einzigen Antwort so viele Informationen zu sammeln, wie Clients für erforderlich halten, um Anrufe auf dem Server zu speichern. Was macht die Kommunikation Client-Server reibungsloser.
Laiv
Dein Beispiel ist schön. Wenn Sie der Kunde sind (Ansichten ...) ist es schmerzhaft zu ändern, aber das größte Problem ist, wenn das System bereits Integrationen von Drittanbietern hat, die nicht geändert werden können (Vertrag, Gebühren ...). Wenn Ihr System über eine Drittanbieterintegration verfügt, sollten Sie DTO verwenden.
Lucas Gonçalves

Antworten:

43

Wann immer ein Entwickler fragt, was der Sinn ist, dies zu tun, ist das, was er wirklich meint: "Ich sehe keinen Anwendungsfall, in dem dies einen Nutzen bringt." Lassen Sie mich zu diesem Zweck einige Beispiele zeigen.


Alle Beispiele basieren auf diesem einfachen Datenmodell:

Eine PersonEntität hat fünf Eigenschaften:Id, FirstName, LastName, Age, CityId

Und Sie können davon ausgehen, dass die Anwendung diese Daten auf vielfältige Weise verwendet (Berichte, Formulare, Popups, ...).

Die gesamte Anwendung ist bereits vorhanden. Alles, was ich erwähne, ist eine Änderung der vorhandenen Codebasis. Dies ist wichtig zu beachten.


Beispiel 1 - Ändern der zugrunde liegenden Datenstruktur - Ohne DTO

Die Anforderungen haben sich geändert. Das Alter der Person muss dynamisch aus der Datenbank der Regierung abgerufen werden (basierend auf Vor- und Nachnamen).

Da Sie den AgeWert nicht mehr lokal speichern müssen, muss er aus der PersonEntität entfernt werden. Hierbei ist es wichtig zu erkennen, dass die Entität die Datenbankdaten darstellt und nicht mehr. Wenn es nicht in der Datenbank ist, ist es nicht in der Entität.
Wenn Sie das Alter vom Webdienst der Regierung abrufen, wird es in einem anderen Objekt (oder int) gespeichert.

Aber Ihr Frontend zeigt immer noch ein Alter an. Alle Ansichten wurden so eingerichtet, dass sie die Person.AgeEigenschaft verwenden, die jetzt nicht mehr vorhanden ist. Es stellt sich ein Problem: Alle Ansichten, die sich auf die Ageeiner Person beziehen, müssen behoben werden .


Beispiel 2 - Ändern der zugrunde liegenden Datenstruktur - Mit DTO

Im alten System gibt es auch PersonDTOUnternehmen mit den gleichen fünf Eigenschaften: Id, FirstName, LastName, Age, CityId. Nach dem Abrufen von Personkonvertiert die Service-Schicht es in ein PersonDTOund gibt es dann zurück.

Aber jetzt haben sich die Anforderungen geändert. Das Alter der Person muss dynamisch aus der Datenbank der Regierung abgerufen werden (basierend auf Vor- und Nachnamen).

Da Sie den AgeWert nicht mehr lokal speichern müssen, muss er aus der PersonEntität entfernt werden. Hierbei ist es wichtig zu erkennen, dass die Entität die Datenbankdaten darstellt und nicht mehr. Wenn es nicht in der Datenbank ist, ist es nicht in der Entität.

Da Sie jedoch einen Vermittler haben PersonDTO, ist es wichtig zu sehen, dass diese Klasse die Eigenschaft behalten kann Age. Die Serviceebene ruft das ab Person, konvertiert es in ein PersonDTO, ruft dann auch das Alter der Person aus dem Webdienst der Regierung ab, speichert diesen Wert in PersonDTO.Ageund übergibt das Objekt.

Der wichtige Teil hierbei ist, dass jeder, der die Service-Schicht verwendet, keinen Unterschied zwischen dem alten und dem neuen System sieht . Dies beinhaltet Ihr Frontend. Im alten System hat es ein vollständiges PersonDTOObjekt erhalten. Und im neuen System erhält es immer noch ein volles PersonDTOObjekt. Die Ansichten müssen nicht aktualisiert werden .

Das ist, was wir meinen, wenn wir den Ausdruck Trennung von Bedenken verwenden : Es gibt zwei verschiedene Bedenken (Speichern der Daten in der Datenbank, Präsentieren von Daten für das Frontend) und sie benötigen jeweils einen anderen Datentyp. Auch wenn diese beiden Datentypen momentan dieselben Daten enthalten, kann sich dies in Zukunft ändern.
Im angegebenen Beispiel Agebesteht ein Unterschied zwischen den beiden Datentypen: Person(die Datenbankentität) benötigt kein Age, aber PersonDTO(der Frontend-Datentyp) benötigt es.
Durch das Trennen der Bedenken (= Erstellen separater Datentypen) von Anfang an ist die Codebasis viel widerstandsfähiger gegenüber Änderungen, die am Datenmodell vorgenommen wurden.

Sie könnten argumentieren, dass ein DTO-Objekt beim Hinzufügen einer neuen Spalte zur Datenbank doppelte Arbeit bedeutet, indem die Eigenschaft sowohl in der Entität als auch in der DTO hinzugefügt wird. Das ist technisch korrekt. Es erfordert ein wenig zusätzlichen Aufwand, zwei Klassen anstelle von einer zu verwalten.

Sie müssen jedoch den erforderlichen Aufwand vergleichen. Wenn eine oder mehrere neue Spalten hinzugefügt werden, dauert das Kopieren / Einfügen einiger Eigenschaften nicht allzu lange. Wenn sich das Datenmodell strukturell ändert, ist es erheblich aufwändiger, das Frontend zu ändern, möglicherweise in einer Weise, die nur zur Laufzeit (und nicht zur Kompilierungszeit) Fehler verursacht, und die Entwickler müssen nach Fehlern suchen.


Ich könnte Ihnen weitere Beispiele nennen, aber das Prinzip wird immer dasselbe sein.

Zusammenfassen

  • Separate Verantwortlichkeiten (Anliegen) müssen getrennt voneinander arbeiten. Sie sollten keine Ressourcen wie Datenklassen (zB Person) teilen
  • Nur weil eine Entität und ihr DTO dieselben Eigenschaften haben, bedeutet dies nicht, dass Sie sie in derselben Entität zusammenführen müssen. Schneiden Sie keine Ecken.
    • Nehmen wir als offensichtliches Beispiel an, unsere Datenbank enthält Länder, Lieder und Menschen. Alle diese Entitäten haben a Name. Nur weil sie alle eine NameEigenschaft haben, heißt das nicht, dass wir sie von einer gemeinsam genutzten EntityWithNameBasisklasse erben lassen sollten . Die verschiedenen NameEigenschaften haben keine sinnvolle Beziehung.
    • Sollte sich eine der Eigenschaften ändern (z. B. wird ein Song in Nameumbenannt Titleoder eine Person erhält ein FirstNameund LastName), müssen Sie mehr Aufwand betreiben, um die Vererbung rückgängig zu machen, die Sie überhaupt nicht benötigt haben .
    • Obwohl nicht so offensichtlich, ist Ihr Argument, dass Sie kein DTO benötigen, wenn Sie eine Entität haben, dasselbe. Sie schauen auf das Jetzt , bereiten sich aber nicht auf zukünftige Änderungen vor. WENN die Entität und das DTO genau gleich sind und WENN Sie garantieren können, dass das Datenmodell niemals geändert wird; dann hast du recht, dass du das DTO weglassen kannst. Die Sache ist jedoch, dass Sie niemals garantieren können, dass sich das Datenmodell niemals ändern wird.
  • Gute Praxis zahlt sich nicht immer sofort aus. Es könnte sich in Zukunft auszahlen, wenn Sie eine alte Anwendung erneut aufrufen müssen.
  • Der Hauptkiller bestehender Codebasen ist es, die Codequalität sinken zu lassen, was es immer schwieriger macht, die Codebasis aufrechtzuerhalten, bis es zu einem nutzlosen Durcheinander von Spaghetti-Code kommt, das nicht mehr zu halten ist.
  • Bewährte Methoden, wie das Implementieren einer Trennung von Bedenken und Problemen, zielen darauf ab, diesen schlüpfrigen Hang einer schlechten Wartung zu vermeiden, um die Codebasis so lange wie möglich wartbar zu halten.

Als Faustregel, um die Trennung von Bedenken zu berücksichtigen, stellen Sie es sich folgendermaßen vor:

Angenommen, jedes Anliegen (die Benutzeroberfläche, die Datenbank, die Logik) wird von einer anderen Person an einem anderen Ort bearbeitet. Sie können nur per E-Mail kommunizieren.

In einer gut getrennten Codebasis muss eine Änderung an einem bestimmten Unternehmen nur von einer Person vorgenommen werden:

  • Das Ändern der Benutzeroberfläche betrifft nur das UI dev.
  • Das Ändern der Datenspeichermethode betrifft nur die Datenbankentwicklung.
  • Das Ändern der Geschäftslogik betrifft nur den Geschäftsentwickler.

Wenn all diese Entwickler wurden unter Verwendung derselben PersonEinheit, und eine geringfügige Änderung wurde das Unternehmen gemacht, alle müßten in dem Prozess einbezogen werden.

Durch die Verwendung separater Datenklassen für jede Ebene tritt dieses Problem jedoch nicht so häufig auf:

  • Solange der Datenbankentwickler ein gültiges PersonDTOObjekt zurückgeben kann, ist es dem Geschäfts- und Benutzeroberflächenentwickler egal, wie die Daten gespeichert / abgerufen werden.
  • Solange der Geschäftsentwickler die Daten in der Datenbank speichert und dem Frontend die erforderlichen Daten zur Verfügung stellt, ist es den Datenbank- und Benutzeroberflächenentwicklern egal, ob er seine Geschäftsregeln überarbeitet.
  • Solange die Benutzeroberfläche basierend auf dem `PersonViewModel entworfen werden kann, kann der Benutzeroberflächen-Entwickler die Benutzeroberfläche nach Belieben erstellen. Die Datenbank- und Geschäftsentwickler kümmern sich nicht darum, wie es gemacht wird, da es sie nicht betrifft.

Die Schlüsselphrase hier ist, da es sie nicht betrifft . Die Umsetzung einer guten Trennung der Anliegen zielt darauf ab, die Beeinträchtigung anderer Parteien auf ein Mindestmaß zu beschränken (und daher die Einbeziehung dieser Parteien erforderlich zu machen).

Natürlich können einige wichtige Änderungen nicht verhindern, dass mehr als eine Person einbezogen wird, z. B. wenn der Datenbank eine völlig neue Entität hinzugefügt wird. Unterschätzen Sie jedoch nicht die Anzahl der geringfügigen Änderungen, die Sie während der Lebensdauer einer Anwendung vornehmen müssen. Hauptänderungen sind eine numerische Minderheit.

Flater
quelle
Umfassende Antwort, Danke. Ich habe Fragen; Schätzen Sie, wenn Sie antworten: 1 - Korrigieren Sie mich, wenn ich falsch liege. Business Object oder View Object dient zum Übertragen von Daten zwischen Presentation Layer und Business Layer, und Entity Object dient zum Übertragen von Daten zwischen Business Layer und Data Access Layer . und DTO kann als dummes BO verwendet werden. 2 - Angenommen, zwei Ansichten benötigen unterschiedliche Informationen einer Firma, dann brauchen wir zwei unterschiedliche companyDTOs?
Arash
1
@Arash (1) "DTO" ist wirklich eine Allround-Definition für jede Datenklasse, die für den Austausch zwischen zwei Ebenen verwendet wird. Ein Geschäftsobjekt und ein Ansichtsobjekt sind beide DTOs. (2) Das hängt sehr von vielen Dingen ab. Das Erstellen eines neuen Dto für jede Sammlung von Feldern, die Sie benötigen, ist eine umständliche Aufgabe. Es ist von Natur aus nichts Falsches daran, einfach ein vollständiges DTO des Unternehmens zurückzugeben (wo dies sinnvoll ist) und die Ansicht dann die Felder auswählen zu lassen, an denen es interessiert ist. Es geht darum, das Gleichgewicht zwischen der Implementierung einer angemessenen Trennung von Bedenken und der Vermeidung von Überplanung und sinnlosen Wiederholungen zu finden.
Flater
Das ergibt für mich einen Sinn. Danke vielmals. Flater.
Arash
Noch eine Frage. Sie sagten "Geschäftsobjekt und ein Ansichtsobjekt". Ich dachte, dass beide gleich sind. Bei der Suche wurde mir klar, dass Business Object eine allgemeine Bedeutung für den Vergleich mit View Object hat . Das Geschäftsobjekt sollte jedoch vom Anwendungsfall abgeleitet werden, und das Entitätsobjekt sollte vom Datenmodell abgeleitet werden. Sie unterscheiden sich also, oder? Könntest du es bitte ein bisschen erklären?
Arash
@Arash: Der Unterschied zwischen einem "Geschäftsobjekt" und einem "Ansichtsobjekt" liegt im Kontext . Für uns Menschen ist diese Unterscheidung wichtig, um die Dinge richtig zu verstehen. Aber der Compiler (und damit auch die Sprache selbst) sieht keinen technischen Unterschied zwischen ihnen. Wenn ich sage, dass sie gleich sind, meine ich das aus technischer Sicht. Beide sind nur eine Klasse mit Eigenschaften, die Daten enthalten und weitergegeben werden sollen. In dieser Hinsicht gibt es keinen Unterschied zwischen ihnen.
Flater