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 Person
Entitä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 Age
Wert nicht mehr lokal speichern müssen, muss er aus der Person
Entitä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.Age
Eigenschaft verwenden, die jetzt nicht mehr vorhanden ist. Es stellt sich ein Problem: Alle Ansichten, die sich auf die Age
einer Person beziehen, müssen behoben werden .
Beispiel 2 - Ändern der zugrunde liegenden Datenstruktur - Mit DTO
Im alten System gibt es auch PersonDTO
Unternehmen mit den gleichen fünf Eigenschaften: Id, FirstName, LastName, Age, CityId
. Nach dem Abrufen von Person
konvertiert die Service-Schicht es in ein PersonDTO
und 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 Age
Wert nicht mehr lokal speichern müssen, muss er aus der Person
Entitä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.Age
und ü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 PersonDTO
Objekt erhalten. Und im neuen System erhält es immer noch ein volles PersonDTO
Objekt. 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 Age
besteht 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 Name
Eigenschaft haben, heißt das nicht, dass wir sie von einer gemeinsam genutzten EntityWithName
Basisklasse erben lassen sollten . Die verschiedenen Name
Eigenschaften haben keine sinnvolle Beziehung.
- Sollte sich eine der Eigenschaften ändern (z. B. wird ein Song in
Name
umbenannt Title
oder eine Person erhält ein FirstName
und 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 Person
Einheit, 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
PersonDTO
Objekt 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.
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.