Verständnisfrage DTO Spring Boot

Diskutiere Verständnisfrage DTO Spring Boot im Data Tier Bereich.
NicoDeluxe

NicoDeluxe

Hallo zusammen,

hab eine Verständnisfrage. Irgendwie ergibt sich mir der Sinn eines DTO nicht. Ich könnte auch einen Constructor mit farbe, preis in Auto.java packen statt ein eigenes Objekt/Klasse dafür zu erstellen.

Ich habe 2 services, einen Datenservice welcher Daten aus der DB holt und bereit stellt und einen Shopservice, welcher Die Daten vom "Datenservice" holt, verarbeitet und an eine externe API schickt (zb Autoverkaufsportal oder sonst was)

Nun hab ich an einigen Stellen das Bedürfnis, nicht alle Daten eines Autos zu holen um Zeit und Last zu sparen. Zb fragt der Shopservice beim Datenservice nach einem Auto um dessen Preis und Farbe zu bekommen. Ich brauch an der Stelle nur die 2 Werte vom Auto und möchte nicht das gesamte Auto mit all seinen Relationen holen.

Dazu hab ich folgenden Dummycode

Code:
class Auto(){
Integer id;
String name;
String farbe;
Double preis;
Motor motor;
List<Bilder> bilder;
Hersteller hersteller;
Getriebe getriebe;
//... getter Setter
}
Code:
public class AutoUpdateDto {

String farbe;
Double preis;

public AutoUpdateDto(String farbe, Double preis) {
//this...
//this...
}
//getter setter

}
Code:
@Controller
class AutoController(){

@GetMapping("/getAutoUpdateDto/{id}")
    AutoUpdateDto getAutoUpdateDto(@PathVariable("id")int id) {
        return repository.getAutoUpdateDto(id);
    }
}
Code:
@Repository
public interface Repository..{
  @Query("Select new com.domain.dto.AutoUpdateDto(a.farbe,a.preis) from Auto a where a.id = :id")
    AutoUpdateDto getAutoUpdateDto(int id);
}
 
T

thecain

Was willst du dann mit dem unvollständigen Objekt machen? Die restlichen Felder null lassen? Wer stellt sicher, dass niemand die Felder dann braucht?
 
NicoDeluxe

NicoDeluxe

Die anderen Felder werden nicht ausgegeben
@JsonInclude(JsonInclude.Include.NON_NULL)

Ich brauch zb den Preis um ihn auf dem Automarktplatz zu updaten.

Ich stelle sicher, dass ich die anderen Daten nicht brauch, da nur ich den Service anspreche.
 
T

TM69

Hallo zusammen,

hab eine Verständnisfrage. Irgendwie ergibt sich mir der Sinn eines DTO nicht. Ich könnte auch einen Constructor mit farbe, preis in Auto.java packen statt ein eigenes Objekt/Klasse dafür zu erstellen.

Ich habe 2 services, einen Datenservice welcher Daten aus der DB holt und bereit stellt und einen Shopservice, welcher Die Daten vom "Datenservice" holt, verarbeitet und an eine externe API schickt (zb Autoverkaufsportal oder sonst was)

Nun hab ich an einigen Stellen das Bedürfnis, nicht alle Daten eines Autos zu holen um Zeit und Last zu sparen. Zb fragt der Shopservice beim Datenservice nach einem Auto um dessen Preis und Farbe zu bekommen. Ich brauch an der Stelle nur die 2 Werte vom Auto und möchte nicht das gesamte Auto mit all seinen Relationen holen.

Dazu hab ich folgenden Dummycode

Code:
class Auto(){
Integer id;
String name;
String farbe;
Double preis;
Motor motor;
List<Bilder> bilder;
Hersteller hersteller;
Getriebe getriebe;
//... getter Setter
}
Code:
public class AutoUpdateDto {

String farbe;
Double preis;

public AutoUpdateDto(String farbe, Double preis) {
//this...
//this...
}
//getter setter

}
Code:
@Controller
class AutoController(){

@GetMapping("/getAutoUpdateDto/{id}")
    AutoUpdateDto getAutoUpdateDto(@PathVariable("id")int id) {
        return repository.getAutoUpdateDto(id);
    }
}
Code:
@Repository
public interface Repository..{
  @Query("Select new com.domain.dto.AutoUpdateDto(a.farbe,a.preis) from Auto a where a.id = :id")
    AutoUpdateDto getAutoUpdateDto(int id);
}
DTOs werden dazu verwendet um die Übertragungsbreite zwischen UI und Service zu reduzieren. Es ist aber zu bedenken, dass die Konvertierung zwischen Domain Object und DTOs ein kostspieliger Prozess sein kann, was Onkel Bob hier erklärte: https://martinfowler.com/bliki/LocalDTO.html
 
T

thecain

Martin Fowler und Onkel Bob sind zwei Personen.


Ich stelle sicher, dass ich die anderen Daten nicht brauch, da nur ich den Service anspreche.
Das ist nicht sicherstellen.
Im Moment wird das sicher funktionieren, aber Design technisch kannst du das nicht sicherstellen. Deswegen wird üblicherweise ein DTO verwendet.
Schlussendlich kannst du das aber machen wie du willst. Vielleicht wirds dir nie Probleme machen.
Eventuell hast du aber plötzlich NullPointer, weil du das unvollständige Objekt verwendest.
 
L

LimDul

Die anderen Felder werden nicht ausgegeben
@JsonInclude(JsonInclude.Include.NON_NULL)

Ich brauch zb den Preis um ihn auf dem Automarktplatz zu updaten.

Ich stelle sicher, dass ich die anderen Daten nicht brauch, da nur ich den Service anspreche.
Das Problem ist nur - Dein Auto-Objekt hat nun aber zwei Verantwortlichkeiten:

a) Das fachliche Objekt in der Anwendung
b) Datenobjekt für den Service, wo viele Daten nicht gefüllt sind.

Das heißt, jede Methode, die ein Auto als Eingabe erwartet muss nun eigentlich genau prüfen, hat sie Auto a) oder ein Auto b) in der Hand? Du kannst damit generell aus rein architektonischer Sicht nicht davon ausgehen, dass bei einem Auto-Objekt mehr als Preis und Farbe gefüllt ist.

Klar gehst du aufgrund deines Codes aus, dass ein Auto-Objekt vom Typ b) nur in Richtung Service verwendet wird. Daher kann sowas funktionieren. Aber es verletzt halt das Single-Responsibility-Prinzip - deine Klasse ist nun für zwei Aufgaben zuständig
 
NicoDeluxe

NicoDeluxe

Hmmm und wie würde ich es richtigerweise lösen ohne dass ich alle Abhängigkeiten lade?
 
mrBrown

mrBrown

Es ist aber zu bedenken, dass die Konvertierung zwischen Domain Object und DTOs ein kostspieliger Prozess sein kann, was Onkel Bob hier erklärte: https://martinfowler.com/bliki/LocalDTO.html
Nur als Hinweis am Rande: der Text bezieht sich auf „Local DTOs“ und „Kosten“ meinen dort mehr als nur die reine Konvertierung.

Die reinen Konvertierungs-Kosten sind generell schon sehr gering, zusätzlich sind sie grad in diesem Fall, wo es um Rest-Schnittstellen geht, völlig vernachlässigbar, da alles andere um Größenordnungen langsamer ist. Bei rein lokalen DTOs sind die Kosten im Vergleich natürlich größer, in heutigen Systemen aber auch völlig irrelevant.

Die relevanten Kosten sind da eher der zusätzliche Code. Man muss das Model duplizieren (oder uU verdreifachen), man muss konvertieren, man schränkt die Schnittstellen ein, ....

Wenn es aber um Remote-Schnittstellen geht, grad auch wie in diesem Fall, wenn Enitäten nur teilweise benötigt werden, sind die Kosten es wert, bzw sogar geringer als der Verzicht auf DTOs. Den Performance-Nachteil durch die Konvertierung gleicht man dann auch zusätzlich wieder durch weniger zu übertragende Daten aus.
 
mrBrown

mrBrown

Und als drittes noch (ich verbreche grad mal bewusst gegen "keine Doppelposts", da es doch recht unterschiedliche Dinge sind):

Ich würde ich dieses Anwendungsfall nicht mal unbedingt von DTO sprechen. In diesem Anwendungsfall sieht es schon rein auf Domänen-Ebene nach einem eigenem Event aus, und nicht nach nur einer "Teil-Ansicht" der Entität, mindestens der Name spricht dafür (AutoUpdateDto). Es kann also durchaus Sinn machen, das auch auf Domain-Ebene auch explizit so zu modellieren.

Das hat dann natürlich noch andere Konsequenzen auf Architekturebene zur Folge, die sich aber uU durchaus positiv auswirken. Sowohl für den Service selbst, als auch alle abhängigen Services.
 
T

TM69

Im allgemeinen gebe ich dir voll und ganz Recht. Allerdings
Bei rein lokalen DTOs sind die Kosten im Vergleich natürlich größer, in heutigen Systemen aber auch völlig irrelevant.
würde ich persönlich dieses nicht so ganz stehen lassen, den es kommt immer auf die Anforderungen an.
 
mrBrown

mrBrown

Im allgemeinen gebe ich dir voll und ganz Recht. Allerdings

würde ich persönlich dieses nicht so ganz stehen lassen, den es kommt immer auf die Anforderungen an.
Na gut, es mag sicher 2, 3 Java-Programme geben, in denen Lokal DTOs eine Überlegung wert sind, aber dann aus Performance-Gründen ausscheiden ;)
 
L

LimDul

Warum das in dem Fall auch sinnvoll ist, kann man sogar an einem theoretischen Beispiel gut erklären. Angemommen, man würde das allgemeine Auto-Objekt nehmen, aber nur Preis und Farbe füllen und hätte folgenden Code:

Java:
public void sendeAnSchnittstelle(List<Auto> autos) {
  autos.forEach(this::sendeEinzelAutoAnSchnittstelle);
}
Dann kommt die Anforderung "Mit Plattform X gibt es einen Exklusivertrag, VWs dürfen daher nicht mehr über diese Schnittstelle gesendet werden". Dies setzt ein Entwickler um, der die Historie nicht kennt:

Java:
public void sendeAnSchnittstelle(List<Auto> autos) {
  autos.stream().filter(a->a.getMarke() != Marke.VW).forEach(this::sendeEinzelAutoAnSchnittstelle);
}
Er schreibt auch noch ein paar Unit-Testfälle für diese Methode, wo er mal die Marke VW und mal andere reinsteckt - klappt alles.

Tja, nur dumm das nachher, wenn es richtig läuft das Feld Marke nie gefüllt ist und daher auch VWs gesendet werden.

Hätte ich folgenden Code, wo die Klasse AutoPreisFarbeDto nur die Felder Preis, Farbe und ggf. eine ID um die Verknüpfung zum kompletten Objekt herstellen zu können.

Java:
public void sendeAnSchnittstelle(List<AutoPreisFarbeDto> autosDto) {
  autosDto.forEach(this::sendeEinzelAutoAnSchnittstelle);
}
Dann kann der Entwickler an der Stelle keinen Fehler machen, sondern muss entweder die Selektion direkt anpassen oder das DTO erweitern und die Felder füllen.

So vermeidet - vor allem in größeren Projekten, wo die Software über Jahre gewartet und weiterentwickelt wird, Fehler.
Klar könnte man das im JavaDoc dokumentieren, aber dann müsste man das an jeder Methode, die ein Auto bekommt wo nur Preis und Farbe gefüllt sind machen - was den Aufwand erhöht. Und zum anderen gilt im Sinne von Clean Code - wenn ich Dokumentation brauche um meinen Code zu erklären, ist der Code meist nicht sauber. Man sollte nichts über Kommentare dokumentieren, was man auch durch sauberen Code ausdrücken kann.
 
NicoDeluxe

NicoDeluxe

Also ist das so richtig wie ich das vor habe? Die Fehlerprävention mit dem Filter ist ein gutes Beispiel danke.

BTW auf Ud*my sind grad alle Kurse reduziert, kennt jemand Kurse im Bezug auf Spring (Boot) und die DTO Geschichte, die ich unbedingt kaufen sollte? Ich deck mich gleich mal für das restliche Jahr ein :D
 
mrBrown

mrBrown

BTW auf Ud*my sind grad alle Kurse reduziert, kennt jemand Kurse im Bezug auf Spring (Boot) und die DTO Geschichte, die ich unbedingt kaufen sollte? Ich deck mich gleich mal für das restliche Jahr ein :D
Kann dir nur Bücher empfehlen, Patterns of Enterprise Application Architecture zB.
 
S

Schuriko

Also ist das so richtig wie ich das vor habe? Die Fehlerprävention mit dem Filter ist ein gutes Beispiel danke.

BTW auf Ud*my sind grad alle Kurse reduziert, kennt jemand Kurse im Bezug auf Spring (Boot) und die DTO Geschichte, die ich unbedingt kaufen sollte? Ich deck mich gleich mal für das restliche Jahr ein :D
Ich habe mir am Anfang hier auch ein paar Videos zugelegt. Wenn du des englischen mächtig bist, dann kann ich dir für den Anfang von Spring
(persönlicher Favorit) John Thompson: Spring Framework 5: Beginner to Guru
Er erklärt schon viel und gründlich, allerdings muss man definitiv sagen "Guru" ist hier übertrieben, da Spring sehr sehr viele unterschiedliche Module zur Verfügung stellt und er wirklich nur die wichtigsten Module ausführlich erklärt. Was z.B. fehlt ist sind Module wie z.B. Spring Cloud - Diese Module brauchst du als Einsteiger aber auch nicht. Ich würde den Titel eher umbenennen in "Beginner to Advanced"
Weitere nennenswerte Tutoren wären
Chad Darbey: Spring & Hibernate for Beginners
Chad geht auch schon etwas tiefer rein, deshalb find ich "Beginners" ist etwas untertrieben. Aber es kommt halt immer auf die Definition an was sind Beginners, was sind Fortgeschrittene, was sind Experten und was sind Gurus.

und 28minutes (allerdings letzter ist Inder und hat dem entsprechend ein indisch-englisch drauf)
 
NicoDeluxe

NicoDeluxe

28minutes bin ich schon Stammkunde 😂 John thomsen hab ich mir genau den Kurs gekauft und zusätzlich noch einen mit spring Boot. Hab dummerweise mit Boot begonnen statt mit dem normalen spring Kurs.
 
mihe7

mihe7

Irgendwie ergibt sich mir der Sinn eines DTO nicht.
Der originäre Sinn besteht darin, die Objekt-Kommunikation (über das Netzwerk) zu minimieren.

In den Anfängen von Java EE (J2EE, wie es damals noch hieß) war es durchaus nicht unüblich, neben local auch remote EJBs zu verwenden, bei denen Methodenaufrufe in einen Netzwerkaufruf mit entsprechendem Overhead übersetzt wurden. Wurden also 5 Getter aufgerufen, führte dies zu 5 Anfragen über das Netzwerk und damit zu 5 x Latenzzeit plus Overhead. Stattdessen wurde ein DTO eingeführt, das mit einer einzigen Antwort des Servers an den Client geschickt wurde.

Dieser ursprüngliche Hauptzweck ist weitestgehend entfallen, weil man vom Paradigma der Remote Procedure Calls abgekommen ist, und man sich an Ressourcen orientiert hat. Da die Darstellung einer Ressource implizit ein DTO ist, braucht es dafür natürlich auch kein Pattern mehr.
 
Thema: 

Verständnisfrage DTO Spring Boot

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben