web-dev-qa-db-fra.com

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

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 convertissons en DTO et de remplir le client. Lors de l'enregistrement, nous convertissons à nouveau DTO en entité et enregistrons.

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

18
Naveen Kocherla

Chaque fois qu'un développeur demande "quel est l'intérêt de faire cela?", Ce qu'ils veulent vraiment dire, 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 entité Person 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 récupéré dynamiquement dans 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 valeur Age localement, elle doit donc être supprimée de l'entité Person. 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 le Person.Age propriété, qui n'existe plus. Un problème se présente: Toutes les vues qui font référence au Age d'une personne doivent être corrigées.


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

Dans l'ancien système, il existe également une entité PersonDTO avec les cinq mêmes 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 récupéré dynamiquement dans 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 valeur Age localement, elle doit donc être supprimée de l'entité Person. 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 garder la propriété Age. La couche de service récupérera le Person, le convertira en un PersonDTO, elle récupérera également l'âge de la personne sur le service Web du gouvernement, stockera cette valeur dans PersonDTO.Age, et passe cet objet.

La partie importante ici est que toute personne qui utilise la couche 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 objet PersonDTO complet. Et dans le nouveau système, il reçoit toujours un objet PersonDTO 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 existe 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 besoin d'un type de données différent chacune. Même si ces deux types de données contiennent actuellement les mêmes données, cela pourrait changer à l'avenir.
Dans l'exemple donné, Age est 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 séparés) 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 qu'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 d'une manière qui ne cause que des bogues 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.

Pour résumer

  • Des responsabilités (préoccupations) distinctes doivent fonctionner séparément les unes des autres. Ils ne doivent pas partager de ressources telles que des 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 propriété Name que nous devons les faire hériter d'une classe de base EntityWithName partagée. Les différentes propriétés Name n'ont aucune relation significative.
    • Si l'une des propriétés devait changer (par exemple, le Name d'un morceau est renommé en Title, ou une personne obtient un FirstName et LastName), ils vous ' Je vais devoir dépenser plus d'efforts pour 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 le 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 que il n'y aura jamais de changements au 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 entité Person 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 base de données peut renvoyer un objet PersonDTO valide, le développeur d'entreprise et d'interface utilisateur ne se soucie 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 autour du `PersonViewModel, alors le développeur de l'interface utilisateur peut construire l'interface utilisateur comme il le souhaite. La base de données et les développeurs ne se soucient pas de la façon dont cela est fait, car cela ne les affecte pas.

La phrase clé ici est car elle ne les affecte pas. La mise en œuvre d'une bonne séparation des préoccupations vise à minimiser les effets (et donc la participation) des 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 effectuer pendant la durée de vie d'une application. Les changements majeurs sont une minorité numérique.

45
Flater