Quelle est l'utilisation de DTO au lieu d'Entity?

18

Je travaille sur l'application RCP, je suis nouveau sur cette application.

Les beans Spring sont utilisés pour écrire la logique métier pour enregistrer / récupérer des entités.

Mais, au lieu d'envoyer des entités directement au client, nous nous convertissons en DTO et remplissons le client. Lors de la sauvegarde, nous convertissons à nouveau DTO en entité et enregistrons.

Quel est l'avantage de ces conversions? Quelqu'un peut-il expliquer?

Naveen Kocherla
la source
What's the benefit of these conversions?découpler le modèle de données de persistance du modèle de données (représentation) proposé aux consommateurs. Les avantages du découplage ont été largement discutés dans SE. Cependant, l'objectif sous les DTO est de rassembler en une seule réponse autant d'informations que nécessaire pour que les clients puissent enregistrer les appels vers le serveur. Ce qui rend la communication client-serveur plus fluide.
Laiv
Votre exemple est sympa. Lorsque vous êtes le client (vues ...) est pénible à changer, mais le plus gros problème est quand le système a déjà des intégrations de tiers, c'est impossible à changer (contrat, frais ...). Si votre système aura une intégration tierce, utilisez jamais DTO.
Lucas Gonçalves

Réponses:

44

Chaque fois qu'un développeur demande "quel est l'intérêt de faire cela?", Ce que cela signifie vraiment, c'est "je ne vois aucun cas d'utilisation où cela offre un avantage". À cette fin, permettez-moi de vous montrer quelques exemples.


Tous les exemples seront basés sur ce modèle de données simple:

Une Personentité a cinq propriétés:Id, FirstName, LastName, Age, CityId

Et vous pouvez supposer que l'application utilise ces données de différentes manières (rapports, formulaires, popups, ...).

L'application entière existe déjà. Tout ce que je mentionne est une modification de la base de code existante. C'est important à retenir.


Exemple 1 - Modification de la structure de données sous-jacente - Sans DTO

Les exigences ont changé. L'âge de la personne doit être extrait dynamiquement de la base de données du gouvernement (supposons en fonction de son prénom et de son nom).

Comme vous n'avez plus besoin de stocker la Agevaleur localement, elle doit donc être supprimée de l' Personentité. Il est important ici de se rendre compte que l'entité représente les données de la base de données , et rien de plus. Si ce n'est pas dans la base de données, ce n'est pas dans l'entité.
Lorsque vous récupérez l'âge du service Web du gouvernement, celui-ci sera stocké dans un autre objet (ou int).

Mais votre frontend affiche toujours un âge. Toutes les vues ont été configurées pour utiliser la Person.Agepropriété, qui n'existe plus. Un problème se pose: toutes les vues qui se réfèrent à la Agepersonne doivent être corrigées .


Exemple 2 - Modification de la structure de données sous-jacente - Avec DTO

Dans l'ancien système, il y a aussi une PersonDTOentité avec les mêmes cinq propriétés: Id, FirstName, LastName, Age, CityId. Après avoir récupéré un Person, la couche de service le convertit en un PersonDTO, puis le renvoie.

Mais maintenant, les exigences ont changé. L'âge de la personne doit être extrait dynamiquement de la base de données du gouvernement (supposons en fonction de son prénom et de son nom).

Comme vous n'avez plus besoin de stocker la Agevaleur localement, elle doit donc être supprimée de l' Personentité. Il est important ici de se rendre compte que l'entité représente les données de la base de données , et rien de plus. Si ce n'est pas dans la base de données, ce n'est pas dans l'entité.

Cependant, puisque vous avez un intermédiaire PersonDTO, il est important de voir que cette classe peut conserver la Agepropriété. La couche de service récupérera le Person, le convertira en un PersonDTO, puis récupérera également l'âge de la personne sur le service Web du gouvernement, stockera cette valeur dans PersonDTO.Ageet passera cet objet.

La partie importante ici est que quiconque utilise la couche de service ne voit pas de différence entre l'ancien et le nouveau système . Cela inclut votre frontend. Dans l'ancien système, il recevait un PersonDTOobjet complet . Et dans le nouveau système, il reçoit toujours un PersonDTOobjet complet . Les vues n'ont pas besoin d'être mises à jour .

C'est ce que nous voulons dire lorsque nous utilisons l'expression séparation des préoccupations : il y a deux préoccupations différentes (stockage des données dans la base de données, présentation des données à l'interface) et elles ont chacune besoin d'un type de données différent. Même si ces deux types de données contiennent les mêmes données à l'heure actuelle, cela pourrait changer à l'avenir.
Dans l'exemple donné, il Agey a une différence entre les deux types de données: Person(l'entité de base de données) n'a pas besoin d'un Age, mais PersonDTO(le type de données frontal) en a besoin.
En séparant les préoccupations (= création de types de données distincts) depuis le début, la base de code est beaucoup plus résistante aux modifications apportées au modèle de données.

Vous pourriez faire valoir que le fait d'avoir un objet DTO, lorsqu'une nouvelle colonne est ajoutée à la base de données, signifie que vous devez effectuer un double travail, en ajoutant la propriété à la fois dans l'entité et dans le DTO. C'est techniquement correct. Il faut un peu d'effort supplémentaire pour maintenir deux classes au lieu d'une.

Cependant, vous devez comparer l'effort requis. Lorsqu'une ou plusieurs nouvelles colonnes sont ajoutées, copier / coller quelques propriétés ne prend pas autant de temps. Lorsque le modèle de données change structurellement, devoir changer le frontend, peut-être de manière à ne provoquer des bogues qu'au moment de l'exécution (et non au moment de la compilation), demande beaucoup plus d'efforts et nécessite que le ou les développeurs partent à la recherche de bogues.


Je pourrais vous donner plus d'exemples mais le principe sera toujours le même.

Résumer

  • Des responsabilités (préoccupations) distinctes doivent fonctionner séparément les unes des autres. Ils ne devraient pas partager de ressources telles que les classes de données (par exemple Person)
  • Ce n'est pas parce qu'une entité et son DTO ont les mêmes propriétés que vous devez les fusionner dans la même entité. Ne coupez pas les coins.
    • À titre d'exemple plus flagrant, disons que notre base de données contient des pays, des chansons et des personnes. Toutes ces entités ont un Name. Mais ce n'est pas parce qu'ils ont tous une Namepropriété que nous devons les faire hériter d'une EntityWithNameclasse de base partagée . Les différentes Namepropriétés n'ont pas de relation significative.
    • Si l'une des propriétés devait changer (par exemple, si une chanson Nameest renommée Titleou si une personne obtient un FirstNameet LastName), vous devrez consacrer plus d'efforts à annuler l'héritage dont vous n'aviez même pas besoin en premier lieu .
    • Bien que ce ne soit pas aussi flagrant, votre argument selon lequel vous n'avez pas besoin d'un DTO lorsque vous avez une entité est le même. Vous regardez maintenant , mais vous ne vous préparez à aucun changement futur. SI l'entité et le DTO sont exactement les mêmes, et SI vous pouvez garantir qu'il n'y aura jamais de changements dans le modèle de données; alors vous avez raison de pouvoir omettre le DTO. Mais le fait est que vous ne pouvez jamais garantir que le modèle de données ne changera jamais.
  • Les bonnes pratiques ne sont pas toujours payantes immédiatement. Cela pourrait commencer à porter ses fruits à l'avenir, lorsque vous devrez revoir une ancienne application.
  • Le principal tueur des bases de code existantes est de laisser la qualité du code baisser, ce qui rend continuellement plus difficile la maintenance de la base de code, jusqu'à ce qu'elle se transforme en un gâchis inutile de code spaghetti qui n'est pas gérable.
  • Les bonnes pratiques, telles que la mise en œuvre d'une séparation des préoccupations de l'accès, visent à éviter cette pente glissante de mauvais entretien, afin de maintenir la base de code maintenable aussi longtemps que possible.

En règle générale, pour envisager de séparer les préoccupations, pensez-y de cette façon:

Supposons que toutes les préoccupations (l'interface utilisateur, la base de données, la logique) soient gérées par une personne différente dans un emplacement différent. Ils ne peuvent communiquer que par email.

Dans une base de code bien séparée, une modification d'une préoccupation particulière ne devra être gérée que par une seule personne:

  • La modification de l'interface utilisateur implique uniquement le dev de l'interface utilisateur.
  • La modification de la méthode de stockage des données implique uniquement le développement de la base de données.
  • Changer la logique métier implique uniquement le développement commercial.

Si tous ces développeurs utilisaient la même Personentité et qu'une modification mineure était apportée à l'entité, tout le monde devrait être impliqué dans le processus.

Mais en utilisant des classes de données distinctes pour chaque couche, ce problème n'est pas aussi répandu:

  • Tant que le développeur de la base de données peut renvoyer un PersonDTOobjet valide , l'entreprise et le développeur de l'interface utilisateur ne se soucient pas qu'il ait changé la façon dont les données sont stockées / récupérées.
  • Tant que le développeur d'entreprise stocke les données dans la base de données et fournit les données nécessaires au frontend, la base de données et les développeurs d'interface utilisateur ne se soucient pas s'il décide de retravailler ses règles métier.
  • Tant que l'interface utilisateur peut être conçue en fonction du `PersonViewModel, le développeur de l'interface utilisateur peut créer l'interface utilisateur comme il le souhaite. La base de données et les développeurs commerciaux ne se soucient pas de la façon dont cela est fait, car cela ne les affecte pas.

La phrase clé ici est qu'elle ne les affecte pas . La mise en œuvre d'une bonne séparation des préoccupations vise à minimiser les répercussions (et donc à impliquer) les autres parties.

Bien sûr, certains changements majeurs ne peuvent pas éviter d'inclure plus d'une personne, par exemple lorsqu'une entité entièrement nouvelle est ajoutée à la base de données. Mais ne sous-estimez pas le nombre de modifications mineures que vous devez apporter pendant la durée de vie d'une application. Les changements majeurs sont une minorité numérique.

Flater
la source
Réponse complète, merci. J'ai des questions; Appréciez si vous répondez: 1 - Corrigez-moi, si je me trompe. L'objet métier ou l' objet vue est destiné au transfert de données entre la couche de présentation et la couche métier et l' objet entité est destiné au transfert de données entre la couche métier et la couche d'accès aux données . et DTO peut être utilisé comme un BO stupide. 2 - Supposons que deux vues nécessitent des informations différentes sur une société, alors nous avons besoin de deux sociétésDTO différentes?
Arash
1
@Arash (1) "DTO" est vraiment une définition fourre-tout pour toute classe de données utilisée pour l'échange entre deux couches. Un objet métier et un objet vue sont tous deux des DTO. (2) Cela dépend beaucoup de beaucoup de choses. La création d'un nouveau dto pour chaque collection de champs dont vous avez besoin est une tâche lourde. Il n'y a rien de fondamentalement mauvais à renvoyer simplement un DTO complet de l'entreprise (lorsque cela est raisonnable) et à laisser ensuite la vue choisir les domaines qui l'intéressent.
Flater
Maintenant, c'est logique pour moi. Merci beaucoup. Flater.
Arash
Une autre question. Vous avez dit "objet métier et objet de vue". Je pensais que les deux sont égaux. Lorsque j'ai cherché, j'ai réalisé que Business Object avait une signification générale à comparer avec View Object . Mais Business Object doit être dérivé du cas d'utilisation et Entity Object doit être dérivé du modèle de données, ils sont donc différents, ai-je raison? pourriez-vous l'expliquer un peu, s'il vous plaît?
Arash
@Arash: La différence entre ce que vous appelez un "objet métier" et un "objet de vue" est le contexte . Pour nous, humains, cette distinction est importante pour bien comprendre les choses. Mais le compilateur (et par extension le langage lui-même) ne voit pas de différence technique entre eux. Quand je dis que ce sont les mêmes, je veux dire que d'un point de vue technique. Les deux ne sont qu'une classe avec des propriétés destinées à contenir des données et à les transmettre. À cet égard, il n'y a aucune différence entre eux.
Flater