Est-ce une bonne pratique d'utiliser des objets d'entité comme objets de transfert de données?

29

Je me demande parce que si c'est le cas, pourquoi Entity Framework n'offre pas de logique pour créer un nouvel objet avec les mêmes propriétés pour transférer des données entre les couches?

J'utilise les objets d'entité que je génère avec le framework d'entité.

user2484998
la source
3
Je pense que c'est une bonne question, mais je ne peux pas vraiment le dire parce que c'est si difficile à lire que je ne sais pas comment y remédier. Veuillez modifier votre question.
candied_orange
1
@CandiedOrange +1, et cela rend d'autant plus effrayant que cela a eu tant de votes positifs.
guillaume31

Réponses:

23

C'est à toi de decider.

La plupart des gens vous diront que ce n'est pas une bonne pratique mais vous pouvez vous en tirer dans certains cas.

EF n'a jamais bien joué avec DDD pour plusieurs raisons, mais deux se distinguent: vous ne pouvez pas avoir de constructeurs paramétrés sur vos entités et vous ne pouvez pas encapsuler des collections. DDD s'appuie sur cela, car le modèle de domaine doit inclure à la fois les données et le comportement.

D'une certaine manière, EF vous oblige à avoir un modèle de domaine anémique et dans ce cas, vous pouvez utiliser les entités en tant que DTO. Vous pouvez rencontrer certains problèmes si vous utilisez des propriétés de navigation, mais vous pouvez sérialiser ces entités et les envoyer par câble. Ce n'est peut-être pas pratique cependant. Vous devrez contrôler la sérialisation de chaque entité qui possède des propriétés que vous n'avez pas besoin d'envoyer. Le moyen le plus simple consiste à concevoir simplement des classes distinctes adaptées au transfert de données. Des bibliothèques comme AutoMapper sont créées à cet effet.

Par exemple: supposons que vous ayez une classe appelée Personavec la définition suivante:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; get; }

    // plus a bunch of other properties relevant about a person
}

En supposant que vous souhaitiez afficher une liste d'employés quelque part, il peut être pratique d'envoyer uniquement le Id, FirstNameet LastName. Mais vous devrez envoyer toutes les autres propriétés non pertinentes. Ce n'est pas un gros problème si vous ne vous souciez pas de la taille de la réponse, mais l'idée générale est d'envoyer uniquement les données pertinentes. D'un autre côté, vous pouvez concevoir une API qui renvoie une liste de personnes et, dans ce cas, l'envoi de toutes les propriétés peut être nécessaire, il est donc judicieux de sérialiser et d'envoyer les entités. Dans ce cas, la création d'une classe DTO est discutable. Certaines personnes aiment mélanger les entités et les DTO, d'autres non.

Pour répondre à votre question mise à jour, EF est un ORM. Son travail consiste à mapper des enregistrements de base de données à des objets et vice versa. Ce que vous faites avec ces objets avant et après avoir traversé EF ne fait pas partie de ses préoccupations. Cela ne devrait pas non plus.

devnull
la source
1
Brièvement résumé. Quelles sont les différences entre les DTO et les POCO? Ne détiennent-ils pas simplement des données?
Laiv
1
@ guillaume31 Vous ne pouvez pas avoir un vrai modèle de domaine sans tenir compte de la persistance. À quoi sert une interface publique si je suis obligé d'utiliser la réflexion (sans parler d'autres types de hacks, à condition que ma propriété publique soit soutenue par un champ privé nommé différemment) pour charger mes agrégats dans un état valide? Si EF ne me fournit pas d'infrastructure pour cacher mes agrégats en utilisant leur interface publique, je ferais mieux de garder ma logique métier ailleurs et d'avoir mon domaine anémique au lieu d'écrire une plaque de chaudière supplémentaire pour les charger à partir de la base de données.
devnull
1
Vous préférez donc rendre votre modèle de domaine entier anémique plutôt que de nommer votre collectionneur public de la même manière que le domaine privé? (mettons de côté le problème du constructeur, car il est le plus souvent contre le domaine d'exposer en premier lieu un constructeur prenant tous les champs de l'objet ...)
guillaume31
1
@ Konon d' ici : As of EF Core 2.1, only services known by EF Core can be injected. Support for injecting application services is being considered for a future release.j'aime beaucoup avoir des services d'application injectés dans mes entités.
devnull
1
@Konrad J'arrêterais d'utiliser mes entités de domaine en tant que DTO (si je les utilisais de cette manière). Les DTO sont utiles quelle que soit la prise en charge d'EF pour l'injection de service.
devnull
8

Non, ça ne l'est pas.

Idéalement, les DTO correspondront à vos référentiels de persistance (aka, vos tables de base de données).

Mais vos cours d'affaires ne sont pas nécessairement un match. Vous pourriez avoir besoin de classes supplémentaires, ou de classes séparées ou jointes à ce que vous avez dans la base de données. Si votre application est petite, vous ne verrez peut-être pas vraiment ce genre de problèmes, mais dans les applications moyennes à grandes, cela se produira souvent.

Une autre chose est que les DTO font partie du domaine de tout ce qui traite de la persistance, tandis que votre couche métier ne devrait rien en savoir.

Juan Carlos Eduardo Romaina Ac
la source
Un objet Command est un DTO que la couche métier doit connaître.
devnull
Je pense que la couche métier appellera probablement l'objet de commande indirectement, via une interface. Et je veux dire les DTO comme dans les objets simples qui transportent des données, sans aucune fonctionnalité. Je ne sais pas si un objet de commande est un DTO ( martinfowler.com/eaaCatalog/dataTransferObject.html ).
Juan Carlos Eduardo Romaina Ac
1
Habituellement, les commandes sont séparées en la commande réelle et son gestionnaire. La commande contient les données, le gestionnaire contient le comportement. Lorsqu'elle est utilisée comme ceci, la partie commande contient uniquement les données reçues du client et agit comme un DTO entre le client et la couche métier.
devnull
Les DTO ne découlent pas de la persistance vers le domaine, ils peuvent également provenir de l'extérieur comme dans les données entrantes (pensez aux API REST). Comme l'a souligné devnull, les commandes et les événements sont en quelque sorte des DTO et ils sont transférés à la couche métier. Si les gestionnaires sont une couche métier ou non, cela dépend de la conception.
Laiv
4

C'est en fait une très mauvaise idée. Martin Fowler a un article sur les DTO locaux .

Pour faire court, DTOPattern a été utilisé pour transférer des données en dehors du processus, par exemple sur le fil et non entre des couches à l'intérieur du même processus.

Constantin Galbenu
la source
Comme pour toutes les approches de programmation, il n'y a pas de solution unique sinon nous ne discuterions pas et ne discuterions pas des différents modèles et approches dans chaque application que nous construisons, nous utiliserions toujours les mêmes choses. Les DTO ne sont essentiellement que des POPO, ils sont simplement nommés pour montrer une certaine intention. Il n'y a rien à utiliser un DTO pour "transférer des données" entre par exemple un service, un contrôleur et en vue. Il n'y a rien dans le nom ou la structure des classes qui indique ou limite d'où et vers les données sont transférées
James
4

Non, c'est une mauvaise pratique.

Certaines raisons:

  1. Les nouveaux champs d'entité seront par défaut dans le Dto . Utiliser l'entité signifie que toutes les informations seront disponibles pour être consommées par défaut. Cela peut vous conduire à exposer des informations sensibles ou, au moins, à gonfler votre contrat d'API, avec beaucoup d'informations qui ne sont pas utilisées pour qui consomme l'API. Bien sûr, vous pouvez ignorer les champs à l'aide d'annotations (comme @JsonIgnoredans le monde Java), mais cela conduit au problème suivant ...
  2. Beaucoup d'annotations / conditions / contrôles sur l'entité . Vous devez contrôler ce que vous souhaitez envoyer sur Dto, lorsqu'un nom d'attribut a changé (cela rompra le contrat), ajouter un attribut qui ne provient pas de l'entité, l'ordre des attributs, etc. Bientôt, vous verrez votre simple entité avec beaucoup d'annotations, de champs supplémentaires et à chaque fois sera plus difficile de comprendre ce qui se passe.
  3. Moins de flexibilité . Avec un Dto, vous êtes libre de diviser les informations en plusieurs classes, de changer les noms d'attributs, d'ajouter de nouveaux attributs, etc. Vous ne pouvez pas faire cela si facilement avec une entité comme Dto.
  4. Pas optimisé . Votre entité sera toujours plus grande qu'un simple Dto. Vous aurez donc toujours plus d'informations à ignorer / sérialiser et probablement plus d'informations inutiles à transmettre.
  5. Problèmes paresseux . En utilisant l'entité de certains frameworks (comme Hibernate), si vous essayez de récupérer des informations qui n'étaient pas chargées paresseusement dans une transaction de base de données auparavant, le proxy ORM ne sera pas attaché à la transaction et vous recevrez une sorte d '"exception paresseuse" juste pour appeler une getméthode de l'entité.

Il est donc plus facile et sûr d'utiliser une sorte d'outil de mappage pour vous aider dans ce travail, en mappant les champs d'entité à un Dto.

Dherik
la source
1

Pour compléter ce qu'a dit @Dherik, les principaux problèmes liés à l'utilisation d'objets d'entité comme objets de transfert de données sont les suivants:

  1. Dans une transaction, vous prenez le risque de valider les modifications apportées à votre entité car vous l'utilisez en tant que DTO (même si vous pouvez détacher l'entité de la session dans une transaction, la plupart du temps, vous devrez vérifier cet état avant toute modification sur votre entité-DTO et assurez-vous que vous n'êtes pas dans une transaction ou que la session a été fermée si vous ne voulez pas que les modifications soient persistées).

  2. La taille des données que vous partagez entre le client et le serveur: parfois, vous ne voulez pas envoyer tout le contenu d'une entité au client pour minimiser la taille de la réponse de la demande. La séparation du DTO de l'entité est plus flexible, afin de spécialiser les données que vous souhaitez envoyer dans certains cas d'utilisation.

  3. Visibilité et maintenance: vous devez gérer vos annotations jpa / hibernate sur les champs de votre entité et maintenir les annotations jackson à sérialiser dans json au même endroit (même si vous pouvez les séparer de l'implémentation d'entité en les plaçant dans l'interface héritée par le entité). Ensuite, si vous modifiez votre contenu DTO en ajoutant un nouveau champ, une autre personne peut probablement penser que c'est un champ de l'entité, donc un champ de la table concernée dans votre base de données (même si vous pouvez utiliser l' @Transientannotation sur tous vos champs DTO pour le cas ..!).

À mon avis, cela crée du bruit lorsque vous lisez l'entité, mais mon avis est sûrement subjectif.

Charles Vuillecard
la source