REST API - DTOs oder nicht? [geschlossen]

154

Ich erstelle derzeit eine REST-API für ein Projekt und habe Artikel für Artikel über Best Practices gelesen. Viele scheinen gegen DTOs zu sein und legen einfach nur das Domänenmodell offen, während andere DTOs (oder Benutzermodelle oder wie auch immer Sie es nennen möchten) für eine schlechte Praxis halten. Persönlich dachte ich, dass dieser Artikel sehr viel Sinn macht.

Ich verstehe jedoch auch die Nachteile von DTOs mit all dem zusätzlichen Mapping-Code, Domänenmodellen, die möglicherweise zu 100% mit ihrem DTO-Gegenstück identisch sind, und so weiter.

Unsere API wurde größtenteils so erstellt, dass andere Clients Daten verwenden können. Wenn wir es jedoch richtig machen, möchten wir sie nach Möglichkeit auch für unsere eigene Web-GUI verwenden.

Die Sache ist, dass wir möglicherweise nicht alle Domänendaten für die anderen Clientbenutzer verfügbar machen möchten. Ein Großteil der Daten ist nur in unserer eigenen Webanwendung sinnvoll. Außerdem möchten wir möglicherweise nicht in allen Szenarien alle Daten zu einem Objekt verfügbar machen, insbesondere nicht zu Beziehungen zu anderen Objekten usw. Wenn wir beispielsweise eine Liste eines bestimmten Objekts verfügbar machen, möchten wir nicht unbedingt die gesamte Objekthierarchie verfügbar machen. damit die Kinder des Objekts nicht entlarvt werden, sondern über Links (Hateoas) entdeckt werden können.

Wie soll ich dieses Problem lösen? Ich habe darüber nachgedacht, Jackson-Mixins in unseren Domain-Modellen zu verwenden, um zu steuern, welche Daten in verschiedenen Szenarien verfügbar gemacht werden. Oder sollten wir nur DTOs verwenden - trotz ihrer Nachteile und Kontroversen?

benbjo
quelle
9
Seien Sie nicht überrascht, wenn diese Frage geschlossen wird. Es ist eher eine diskussionsbasierte Frage, was bedeutet, dass es keine klare richtige Antwort gibt. Wenn Sie verschiedene Personen fragen, erhalten Sie eine andere Antwort.
Ben Thurley
7
@pinkpanther Das ist ein großartiger Artikel und es ist schade, dass er nicht mehr verfügbar ist. Hier ist die zwischengespeicherte Version des Webarchivs .
Cassiomolin

Antworten:

251

Warum Sie DTOs in Ihrer REST-API verwenden sollten

DTO steht für D ata T ransfer O bject .

Dieses Muster wurde mit einem sehr genau definierten Zweck erstellt: Übertragen von Daten auf Remote-Schnittstellen , genau wie bei Webdiensten . Dieses Muster passt sehr gut in eine REST-API und DTOs bieten Ihnen auf lange Sicht mehr Flexibilität .

Die Modelle, die die Domäne Ihrer Anwendung darstellen, und die Modelle, die die von Ihrer API verarbeiteten Daten darstellen, sind (oder sollten es zumindest sein) unterschiedliche Anliegen und sollten voneinander entkoppelt werden. Sie möchten Ihre API-Clients nicht beschädigen, wenn Sie ein Feld zum Anwendungsdomänenmodell hinzufügen, entfernen oder umbenennen.

Während Ihre Service-Schicht über die Domänen- / Persistenzmodelle arbeitet, sollten Ihre API-Controller über einen anderen Satz von Modellen arbeiten. Wenn sich Ihre Domänen- / Persistenzmodelle weiterentwickeln, um beispielsweise neue Geschäftsanforderungen zu unterstützen, möchten Sie möglicherweise neue Versionen der API-Modelle erstellen, um diese Änderungen zu unterstützen. Möglicherweise möchten Sie auch die alten Versionen Ihrer API verwerfen, wenn neue Versionen veröffentlicht werden. Und es ist durchaus möglich zu erreichen, wenn die Dinge entkoppelt sind.


Um nur einige Vorteile der Offenlegung von DTOs anstelle von Persistenzmodellen zu nennen:

  • Entkoppeln Sie Persistenzmodelle von API-Modellen.

  • DTOs können auf Ihre Anforderungen zugeschnitten werden und eignen sich hervorragend, wenn nur eine Reihe von Attributen Ihrer Persistenzentitäten verfügbar gemacht werden . Sie benötigen keine Anmerkungen wie @XmlTransientund @JsonIgnore, um die Serialisierung einiger Attribute zu vermeiden.

  • Durch die Verwendung von DTOs vermeiden Sie eine Menge Annotationen in Ihren Persistenzentitäten, dh Ihre Persistenzentitäten werden nicht mit nicht persistenzbezogenen Annotationen aufgebläht.

  • Sie haben die volle Kontrolle über die Attribute, die Sie beim Erstellen oder Aktualisieren einer Ressource erhalten.

  • Wenn Sie Swagger verwenden , können Sie Ihre API-Modelle mithilfe von @ApiModelund @ApiModelPropertyAnmerkungen dokumentieren, ohne Ihre Persistenzentitäten zu beeinträchtigen.

  • Sie können für jede Version Ihrer API unterschiedliche DTOs verwenden.

  • Sie haben mehr Flexibilität beim Zuordnen von Beziehungen.

  • Sie können verschiedene DTOs für verschiedene Medientypen haben.

  • Ihre DTOs können eine Liste von Links für HATEOAS haben . So etwas sollte Persistenzobjekten nicht hinzugefügt werden. Wenn Sie Spring HATEOAS verwenden , können Sie Ihre DTO-Klassen erweitern RepresentationModel(früher bekannt als ResourceSupport) oder mit EntityModel(früher bekannt als Resource<T>) umschließen .

Umgang mit dem Boilerplate-Code

Sie müssen Ihre Persistenzentitäten nicht manuell DTOs zuordnen und umgekehrt . Es gibt viele Mapping-Frameworks, mit denen Sie dies tun können. Schauen Sie sich beispielsweise MapStruct an , das auf Anmerkungen basiert und als Maven-Anmerkungsprozessor arbeitet. Es funktioniert sowohl in CDI- als auch in Spring-basierten Anwendungen.

Sie können auch wollen betrachten Lombok Getter, Setter, zu erzeugen equals(), hashcode()und toString()Methoden für Sie.


Verwandte Themen: Um Ihren DTO-Klassen bessere Namen zu geben, lesen Sie diese Antwort .

Cassiomolin
quelle
2
Wenn ich den DTO-Weg gehen würde, würden Sie alle Domänenobjekte einem DTO zuordnen oder nur diejenigen, die nicht identisch wären? Wie würden Sie auch das Problem lösen, Daten basierend auf verschiedenen Szenarien / Kontexten verfügbar zu machen? Mehrere DTOs pro Domain-Objekt?
Benbjo
6
@benbjo Es liegt an dir. Normalerweise ordne ich DTOs nur die komplexesten Entitäten zu, die Entitäten, für die nicht alle Attribute verfügbar gemacht werden sollen, und Entitäten mit vielen Beziehungen. DTOs geben mir die Flexibilität, eine Liste von Links zu haben, die in HATEOAS verwendet werden können. So etwas würde ich meinen Persistenzobjekten nicht hinzufügen.
Cassiomolin
2
@molin vielen Dank für die Informationen und Vorschläge. Ich werde auf jeden Fall MapStruct ausprobieren. Auf den ersten Blick scheint es meinen Bedürfnissen sehr gut zu entsprechen.
Benbjo
6
Lieber Downvoter, können Sie wenigstens den Grund für Ihre Downvote erklären?
Cassiomolin
8
Es gibt auch einen architektonischen Grund, DTOs anstelle von Domänenentitäten in der REST-API zu verwenden. Die REST-API sollte nicht geändert werden, um zu vermeiden, dass vorhandene Clients beschädigt werden. Wenn Sie das Domänenmodell direkt in der API verwenden, erstellen Sie eine unerwünschte Kopplung zwischen der API und dem Domänenmodell. Gemäß dem Konstruktionsprinzip der Service Loose Coupling sollte der Servicevertrag nicht eng mit der Servicelogik oder den Implementierungsdetails gekoppelt sein.
Paulo Merson
25

Wenn Ihre API öffentlich ist und Sie mehrere Versionen unterstützen müssen, müssen Sie sich für DTOs entscheiden.

Wenn es sich jedoch um eine private API handelt und Sie sowohl den Client als auch den Server steuern, überspringe ich in der Regel die DTOs und mache das Domänenmodell direkt verfügbar.

David Siro
quelle
Ich stimme Ihnen im letzten Teil zu und tue dies normalerweise, aber dies ist meine erste öffentliche API. Ich werde überlegen, was Sie über die Verwendung von DTOs für den öffentlichen Teil sagen. Vielleicht sollten der private und der öffentliche Teil der API tatsächlich getrennt sein, auch wenn "essen Sie Ihr eigenes Hundefutter" ein gutes Prinzip ist.
Benbjo
11

Ich neige dazu, DTOs zu verwenden.

Ich mag die Nachteile nicht, aber es scheint, dass die anderen Optionen noch schlimmer sind:

Das Aussetzen von Domänenobjekten kann zu Sicherheitsproblemen und Datenlecks führen. Jackson-Anmerkungen scheinen das Problem zu lösen, aber es ist zu einfach, einen Fehler zu machen und Daten offenzulegen, die nicht offengelegt werden sollten. Beim Entwerfen einer DTO-Klasse ist es viel schwieriger, einen solchen Fehler zu machen.

Auf der anderen Seite können die Nachteile des DTO-Ansatzes durch Dinge wie Objekt-zu-Objekt-Zuordnung und Lombok für weniger Boilerplate reduziert werden .

Argb32
quelle
9

Wie Sie bereits selbst festgestellt haben, handelt es sich eindeutig um eine meinungsbezogene Frage. Ich selbst bin mehr vom No-DTO-Ansatz angezogen, einfach wegen des gesamten Boilerplate-Codes, den Sie benötigen.

Dies gilt hauptsächlich für die Antwortseite einer JSON / Rest-API. Ich habe sogar ein Jackson-Addon geschrieben, um zu vermeiden, dass für diese Fälle viele JSON-Ansichten / Filter geschrieben werden: https://github.com/Antibrumm/jackson-antpathfilter

Andererseits sind DTOs eine gute Sache auf der Anforderungseingabeseite solcher APIs. Die direkte Arbeit an Entitäten kann sehr schwierig sein, wenn beispielsweise bidirektionale Beziehungen berücksichtigt werden. Außerdem möchten Sie nicht, dass ein Anrufer beispielsweise ein "Ersteller" -Attribut ändert. Daher müssten Sie bestimmte Felder während der Zuordnung solcher Anforderungen nicht zulassen.

Martin Frey
quelle
2
Ich stimme zu, dass meine Frage etwas meinungsbezogen (und entmutigt) ist, suchte aber auch nach Tipps, wie ich mein Problem lösen kann. Ich werde viel mit Ihrem Jackson-Addon zu tun haben. Denken Sie jedoch, dass die Verwendung von Mixins zur Steuerung der Daten, die in verschiedenen Szenarien verfügbar gemacht werden sollen, eine gute Vorgehensweise ist?
Benbjo