web-dev-qa-db-fra.com

Est-il problématique que Spring Data REST expose les entités via les ressources REST sans utiliser les DTO)?

Dans mon expérience limitée, on m'a dit à plusieurs reprises que vous ne devriez pas passer des entités à l'extrémité avant ou via le repos, mais plutôt utiliser un DTO.

Spring Data Rest ne fait-il pas exactement cela? J'ai examiné brièvement les projections, mais celles-ci semblent simplement limiter les données renvoyées, et attendent toujours une entité en tant que paramètre d'une méthode de publication à enregistrer dans la base de données. Suis-je en train de manquer quelque chose ici, ou suis-je (et mes collègues) incorrect en ce que vous ne devriez jamais contourner l'entité?

33
chartle

tl; dr

Non. Les DTO ne sont qu'un moyen de dissocier le modèle de domaine côté serveur de la représentation exposée dans les ressources HTTP. Vous pouvez également utiliser d'autres moyens de découplage, ce que fait Spring Data REST.

Détails

Oui, Spring Data REST inspecte le modèle de domaine que vous avez du côté serveur pour déterminer à quoi ressembleront les représentations des ressources qu'il expose. Cependant, il applique quelques concepts cruciaux qui atténuent les problèmes qu'une exposition naïve d'objets de domaine pourrait entraîner.

Spring Data REST recherche les agrégats et façonne par défaut les représentations en conséquence.

Le problème fondamental avec le naïf "Je jette mes objets de domaine devant Jackson" est que, à partir du modèle d'entité simple, il est très difficile de raisonner sur des limites de représentation raisonnables. En particulier, les modèles d'entité dérivés des tables de base de données ont l'habitude de connecter pratiquement tout à tout. Cela découle du fait que des concepts de domaine importants comme les agrégats ne sont tout simplement pas présents dans la plupart des technologies de persistance (lire: en particulier dans les bases de données relationnelles).

Cependant, je dirais que dans ce cas, le "N'exposez pas votre modèle de domaine" agit plus sur les symptômes que sur le fond du problème. Si vous concevez correctement votre modèle de domaine, il y a un énorme chevauchement entre ce qui est bénéfique dans le modèle de domaine et ce à quoi ressemble une bonne représentation pour conduire efficacement ce modèle à travers des changements d'état. Quelques règles simples:

  • Pour chaque relation avec une autre entité, demandez-vous: cela ne pourrait-il pas plutôt être une référence d'identification. En utilisant une référence d'objet, vous tirez beaucoup de sémantique de l'autre côté de la relation dans votre entité. Se tromper conduit généralement les entités se référant aux entités se référant aux entités, ce qui est un problème plus profond. Au niveau de la représentation, cela vous permet de couper les données, de couvrir les portées de cohérence, etc.
  • Évitez les relations bidirectionnelles car elles sont notoirement difficiles à mettre à jour du côté des mises à jour.

Spring Data REST fait pas mal de choses pour réellement transférer ces relations d'entité dans les mécanismes appropriés au niveau HTTP: des liens en général et surtout des liens vers des ressources dédiées gérant ces relations. Il le fait en inspectant les référentiels déclarés pour les entités et remplace fondamentalement un alignement autrement nécessaire de l'entité associée par un lien vers une ressource d'association qui vous permet de gérer cette relation de manière explicite.

Cette approche fonctionne généralement bien avec les garanties de cohérence décrites par les agrégats DDD au niveau HTTP. Les demandes PUT ne s'étendent pas sur plusieurs agrégats par défaut, ce qui est une bonne chose car cela implique une étendue de cohérence de la ressource correspondant aux concepts de votre domaine.

Il ne sert à rien de forcer les utilisateurs dans les DTO si ce DTO duplique simplement les champs de l'objet de domaine.

Vous pouvez introduire autant de DTO que vous le souhaitez pour vos objets de domaine. Dans la plupart des cas, les champs capturés dans l'objet domaine se refléteront d'une manière ou d'une autre dans la représentation. Je n'ai pas encore vu l'entité Customer contenant une propriété firstname, lastname et emailAddress, et celles qui sont complètement hors de propos dans la représentation.

L'introduction des DTO ne garantit en aucun cas un découplage. J'ai vu beaucoup trop de projets où ils ont été introduits pour des raisons de culture de fret, ont simplement dupliqué tous les champs de l'entité les soutenant et par cela ont juste causé des efforts supplémentaires parce que chaque nouveau champ devait également être ajouté aux DTO. Mais bon, découplage! Ne pas. ¯\_ (ツ) _/¯

Cela dit, il y a bien sûr des situations où vous voudriez légèrement ajuster la représentation de ces propriétés, surtout si vous utilisez des objets de valeur fortement typés, par exemple un EmailAddress (bon!) mais vous voulez toujours le rendre comme un simple String en JSON. Mais ce n'est en aucun cas un problème: Spring Data REST utilise Jackson sous les couvertures qui vous offre une grande variété de moyens pour peaufiner la représentation - annotations, mixins pour garder les annotations en dehors de vos types de domaine, sérialiseurs personnalisés etc. Il y a donc une couche de cartographie entre les deux.

Ne pas utiliser les DTO par défaut n'est pas une mauvaise chose en soi. Imaginez simplement le tollé des utilisateurs sur la quantité de passe-partout nécessaire si nous avions besoin que les DTO soient écrits pour tout! Un DTO est juste un moyen pour une fin. Si cet objectif peut être atteint d'une manière différente (et il le peut généralement), pourquoi insister sur les DTO?

N'utilisez simplement pas Spring Data REST là où cela ne correspond pas à vos besoins.

Poursuivant les efforts de personnalisation, il convient de noter que Spring Data REST existe pour couvrir exactement les parties de l'API, qui suivent simplement les modèles d'implémentation de base de l'API REST qu'elle implémente. Et cette fonctionnalité est en place pour vous donner plus de temps pour réfléchir

  1. Comment façonner votre modèle de domaine
  2. Quelles parties de votre API sont mieux exprimées à travers des interactions basées sur l'hypermédia.

Voici une diapositive de la conférence que j'ai donnée à SpringOne Platform 2016 qui résume la situation.

What makes up a REST API built with Spring Data REST

Le diaporama complet se trouve ici . Il y a aussi un enregistrement de la conférence disponible sur InfoQ.

Spring Data REST existe pour que vous puissiez vous concentrer sur les cercles soulignés. En aucun cas, nous pensons que vous pouvez créer une excellente API uniquement en activant Spring Data REST. Nous voulons juste réduire la quantité de passe-partout pour que vous ayez plus de temps pour réfléchir aux bits intéressants.

Tout comme Spring Data en général, réduit la quantité de code passe-partout à écrire pour les opérations de persistance standard. Personne ne dirait que vous pouvez réellement créer une application réelle à partir uniquement des opérations CRUD. Mais en faisant l'effort des bits ennuyeux, nous vous permettons de réfléchir plus intensément aux défis du domaine réel (et vous devriez réellement le faire :)).

Vous pouvez être très sélectif en remplaçant certaines ressources pour prendre complètement le contrôle de leur comportement, notamment en mappant manuellement les types de domaine aux DTO si vous le souhaitez. Vous pouvez également placer des fonctionnalités personnalisées à côté de ce que Spring Data REST fournit et simplement connecter les deux ensemble. Soyez sélectif sur ce que vous utilisez.

Un échantillon

Vous pouvez trouver un exemple légèrement avancé de ce que j'ai décrit dans Spring RESTBucks , une implémentation Spring (Data REST) ​​de l'exemple RESTBucks dans le livre RESTful Web Services. Il utilise Spring Data REST pour gérer les instances de Order mais modifie sa gestion pour introduire des exigences personnalisées et implémenter complètement la partie paiement de l'histoire manuellement.

55
Oliver Drotbohm

Spring Data REST permet un moyen très rapide de créer un prototype et de créer une API REST basée sur une structure de base de données. Nous parlons de minutes vs jours, lorsque nous comparons avec d'autres technologies de programmation.

Le prix à payer pour cela est que votre REST API est étroitement liée à la structure de votre base de données. Parfois, c'est un gros problème. Parfois ce n'est pas le cas. Cela dépend essentiellement de la qualité de la conception de votre base de données et votre capacité à le modifier pour répondre aux besoins des utilisateurs de l'API.

En bref, je considère Spring Data REST comme un outil qui peut vous faire gagner beaucoup de temps dans certaines circonstances spéciales. Pas comme une solution miracle qui peut être appliquée à n'importe quel problème.

3
Andres

Nous avions l'habitude d'utiliser des DTO, y compris les couches entièrement traditionnelles (base de données, DTO, référentiel, service, contrôleurs, ...) pour chaque entité dans nos projets. Sauter les DTO nous sauvera un jour la vie :)

Donc pour une simple entité City qui a id,name,country,state Nous avons fait comme ci-dessous:

  1. City table avec id,name,county,.... colonnes
  2. CityDTO avec les propriétés id,name,county,.... (exactement la même que la base de données)
  3. CityRepository avec une findCity(id),....
  4. CityService avec findCity(id) { CityRepository.findCity(id) }
  5. CityController avec findCity(id) { ConvertToJson( CityService.findCity(id)) }

Trop de codes standard juste pour exposer une information sur la ville au client. Comme il s'agit d'une simple entité, aucune activité n'est effectuée le long de ces couches, seuls les objets passent. Un changement dans l'entité City commençait à partir de la base de données et a changé toutes les couches. (Par exemple, ajouter une propriété location, enfin parce qu'à la fin la propriété location doit être exposée à l'utilisateur sous la forme json). L'ajout d'une méthode findByNameAndCountryAllIgnoringCase nécessite que tous les calques soient modifiés (chaque calque doit avoir une nouvelle méthode).

Compte tenu de Spring Data Rest (of course with Spring Data), C'est plus que simple!

public interface CityRepository extends CRUDRepository<City, Long> {
   City findByNameAndCountryAllIgnoringCase(String name, String country);
}

L'entité city est exposée au client avec un code minimum et vous avez toujours le contrôle sur la façon dont la ville est exposée. Validation, Security, Object Mapping ... tout y est. Vous pouvez donc tout modifier.

Par exemple, si je veux garder le client au courant du changement de nom de propriété d'entité city (séparation des couches), je peux utiliser le mappeur d'objets personnalisé mentionné https://docs.spring.io/spring- data/rest/docs/3.0.2.RELEASE/reference/html/# customizing-sdr.custom-jackson-deserialization

Pour résumer

Nous utilisons le Spring Data Rest autant que possible, dans des cas d'utilisation compliqués, nous pouvons toujours opter pour la superposition traditionnelle et laisser les Service et Controller faire des affaires.

1
Alireza Fattahi