web-dev-qa-db-fra.com

Entité à l'utilisation du DTO

J'ai essayé de trouver un flux pour une application Web à plusieurs niveaux de base et j'ai lu des informations contradictoires en ligne. Ce que j'essaie de comprendre, c'est s'il y a un avantage à continuer à utiliser des objets DTO de votre couche DAO vers Service via l'utilisation d'une sorte de mappeur.

Le flux de base que je prévois est le suivant:

  1. Modèle/formulaire d'interface utilisateur -> Contrôleur
  2. Le contrôleur convertit le modèle en objet de domaine (entité)
  3. Objet de domaine -> Couche de service
  4. Objet de domaine -> DAO
  5. DAO -> Objet (s) de domaine
  6. Service -> UI
  7. L'interface utilisateur convertit le domaine en modèles d'interface utilisateur

Si le DTO était suivi, DAO transmettrait un DTO et non l'entité. Après avoir fait quelques lectures, il semble que le DTO soit légèrement disparu puisque (au moins en Java) les entités sont devenues des POJO annotés, ce qui signifie que leur empreinte mémoire est devenue très petite.

Est-ce le cas, ou faut-il utiliser des DTO pour encapsuler complètement les objets de domaine au sein de la couche DAO et, si tel est le cas, que passerait la couche service au DAO?

Merci beaucoup!

15
dardo

Selon moi, passer un POJO persistant, comme par exemple un bean géré par JPA, n'est pas LA bonne pratique.

Pourquoi?

Je vois trois raisons principales:

  1. Problème potentiel avec les collections paresseuses. http://Java.dzone.com/articles/avoid-lazy-jpa-collections
  2. L'entité doit contenir un comportement (contrairement à un modèle de domaine anémique ) Vous ne voudrez peut-être pas laisser votre interface utilisateur appeler un comportement inattendu.
  3. Dans le cas d'un modèle de domaine anémique, vous ne souhaiterez peut-être pas exposer la structure de votre modèle à l'interface utilisateur, car chaque nouvelle modification du modèle peut casser l'interface utilisateur.

Je préfère laisser ma couche de service convertir les entités en DTO correspondant dans les deux sens. DAO retourne toujours l'entité (ce n'est pas son travail d'assurer la conversion).

20
Mik378

L'une des raisons pour lesquelles je pense que cette discussion revient à plusieurs reprises est parce que cela semble être une grave douleur dans le cul de prendre un objet avec toutes les données dont vous avez besoin et de le convertir en un objet qui semble identique ou presque identique à celui vous remettez.

C'est vrai, c'est un PITA. Mais il y a plusieurs raisons (en plus de celles énumérées ci-dessus) pour le faire.

  • Les objets de domaine peuvent devenir très lourds et contenir beaucoup d'informations inutiles pour l'appel. Ce ballonnement ralentit l'interface utilisateur en raison de toutes les données transmises, rassemblées/non contrôlées et analysées. Lorsque vous considérez qu'un FE aura de nombreux liens faisant référence à vos services Web et étant appelé avec AJAX ou une autre approche multi-thread, vous rendrez rapidement votre interface utilisateur lente. Tout cela se traduit par l'évolutivité générale de services Web
  • La sécurité peut facilement être compromise en exposant trop de données. Au minimum, vous pouvez exposer les adresses e-mail et les numéros de téléphone des utilisateurs si vous ne les supprimez pas du résultat DTO.
  • Considérations pratiques: Pour qu'un objet défile en tant qu'objet de domaine persistant ET qu'un DTO, il devrait avoir plus d'annotations que de code. Vous aurez un certain nombre de problèmes avec la gestion de l'état de l'objet lors de son passage à travers les couches. En général, il s'agit bien plus d'un PITA à gérer que de simplement faire l'ennui de copier des champs d'un objet de domaine vers un DTO.

Mais, vous pouvez le gérer assez efficacement si vous encapsulez la logique de traduction dans une collection de classes de conversion

Jetez un œil à lambdaJ où vous pouvez faire 'convertir (domainObj, toDto)' il y a une surcharge de ceci pour une utilisation avec des collections. Voici un exemple de méthode de contrôleur qui l'utilise. Comme vous pouvez le voir, ça n'a pas l'air si mal.

    @GET
    @Path("/{id}/surveys")
    public RestaurantSurveys getSurveys(@PathParam("id") Restaurant restaurant, @QueryParam("from") DateTime from, @QueryParam("to") DateTime to) {

        checkDateRange(from, to);

        MultiValueMap<Survey, SurveySchedule> surveysToSchedules = getSurveyScheduling(restaurant, from, to);
        Collection<RestaurantSurveyDto> surveyDtos = convert(surveysToSchedules.entrySet(), SurveyToRestaurantSurveyDto.getInstance());
        return new RestaurantSurveys(restaurant.getId(), from, to, surveyDtos);

    }
13