IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Plateformes (Java EE, Jakarta EE, Spring) et Serveurs Discussion :

[Architecture / Services] Graphe d'objets à sauvegarder


Sujet :

Plateformes (Java EE, Jakarta EE, Spring) et Serveurs

  1. #1
    Membre habitué
    Inscrit en
    Décembre 2002
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 186
    Points : 130
    Points
    130
    Par défaut [Architecture / Services] Graphe d'objets à sauvegarder
    voila, j'ai un probleme pour concevoir mon architecture au niveau service:

    g 2 couches de services:
    - la 1ère que j'appele service métier, ne manipule que des objets métiers (BO). Elle fait appel à plusieurs DAO qui retourne aussi des BO. Comme on gère un modele objet riche, certaines relations sont donc mappée, ce qui donne un graph d'objet
    EX:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Projet getProjetLotTache(Integer idProjet)
    retourne
    Projet
    ....+ List<Lot>
    ................+ List<Tache>

    - ensuite g une couche qui va adapter les services métiers aux contraintes des clients: appliWeb, rmi, WebServices, ...
    Cette couche manipule de BO mais retourne des ValueOject (VO)
    La raison est que je souhaite masquer au client les objets du domaine métiers en cas de changement du modele, et je ne veux lui présenter uniquement l'information dont il a besoin (meme si je remonte un objet avec des champs qu'il ne voit pas)
    Mais je souhaite aussi que ces VO offrent un modele objet riche, l'intelligence métier du BO en moins, donc cela donne également un graphe d'objet
    ProjetVO
    ....+ List<LotVO>
    ................+ List<TacheVO>

    J'utilise une Factory + un Adapter qui implemente plusieurs VO pour construire le VO voulu à partir d'un BO... En gros un Adapter va encapsuler un ou plusieurs BO + des infos à faire remonter. Ensuite la Factory retourne cet Adapter qui implémente l'interface du VO attendue.




    [1] Déja, que pensez vous de cette approche et de l'architecture. Est ce que vous avez déja trouvé un inconvénient à remonter des BO à la couche présentation, ce qui simplifie nettement les choses?!
    [2] Est ce qu'il ya mieux pour convertir BO --> VO
    [3] Comment réaliser la conversion VO --> BO le plus simplement et le plus rapidement possible?


    Au début je pensais propager les modifcation du VO au fur et à mesure sur les BO encapsulés au travers les setters du VO, mais cette solution ne marche pas lors de la destruction d'un VO ou seule la référence vers le BO est supprimée, mais le BO en lui meme car il est référencé ailleurs dans le graphe...

    De plus, je souhaite utiliser la puissance de la dao qui s'appuie sur Hibernate et qui détecte automatiquement les changements faits sur le BO au moment de l'update! Donc ce serait stupide de faire 2 fois le travail à cause des VO.


    j'attends vos remarques si c pas clair, merci d'avance

  2. #2
    Membre averti
    Inscrit en
    Août 2005
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 352
    Points : 427
    Points
    427
    Par défaut Re: [Architecture][Services] graphe d'objets à sauvegarder
    Citation Envoyé par mauvais_karma
    J'utilise une Factory + un Adapter qui implemente plusieurs VO pour construire le VO voulu à partir d'un BO... En gros un Adapter va encapsuler un ou plusieurs BO + des infos à faire remonter. Ensuite la Factory retourne cet Adapter qui implémente l'interface du VO attendue.
    C'est une solution mais pas celle que je choisirais dans ce cas la (voir plus loin).

    Citation Envoyé par mauvais_karma
    [1] Déja, que pensez vous de cette approche et de l'architecture. Est ce que vous avez déja trouvé un inconvénient à remonter des BO à la couche présentation, ce qui simplifie nettement les choses?!
    Je suis d'accord avec ta couche de services. Je suis un peu moins d'accord sur les termes mais c'est du détail (en gros, pour moi, un service est également un objet métier et ce que tu appelles BO est un Domain Model pour moi et fait également parti des BO).
    Je suis un peu moins d'accord en ce qui concerne ta solution pour les VO. Je me contenterais de les utiliser pour la distribution, pas pour la présentation sauf s'il y a un gros décalage entre ta vue et ton modele.

    Citation Envoyé par mauvais_karma
    [2] Est ce qu'il ya mieux pour convertir BO --> VO
    Je vois 2 autres solutions valables :
    - http://www.martinfowler.com/eaaCatalog/dataTransferObject.html : un assemblur qui prend un objet en entrée et crée le DTO (un peu comme le fait ta factory mais sans l'adapter)
    - le pattern visitor (pattern du GoF) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    public interface Visitor {
    }
     
    public interface VisitorEntreprise extends Visitor {
        public void visitEntreprise(Entreprise e);
    }
     
    public interface VisitorEmploye extends Visitor {
        public void visitEmploye(Employe e);
    }
     
    public interface Visitable {
        public void accept(Visitor visiteur);
    }
     
    public class Entreprise implements Visitable {
        private List employes;
        ...
        public void accept(Visitor visitor) {
            if (visitor instanceof VisitorEntreprise)
                ((VisitorEntreprise)visitor).visitEntreprise(this);
     
            Iterator it= employes.iterator();
            while (it.hasNext()) {
                Employe emp = (Employe)it.next();
                employe.accept(visitor);
            }
        }
    }
     
     
    public class Employe implements Visitable {
        ...
        public void accept(Visitor visitor) {
            if (visitor instanceof VisitorEmploye)
                ((VisitorEmploye)visitor).visitEmploye(this);
        }
    }
     
    public class VisiteurEntrepriseAssembleurDeDTO implements VisitorEntreprise, VisitorEmploye {
        private EntrepriseDTO resultat;
        private AssembleurDeDTO assembleurDTO;
     
        public void visitEntreprise(Entreprise e) {
            resultat = assembleurDTO.writeDTO(e);
        }
     
        public void visitEmploye(Employe emp) {
            resultat.addEmployeDTO(assembleurDTO.writeDTO(emp));
        } 
     
        public EntrepriseDTO getResultat() {
            return resultat;
        }
    }
    Tu peux ensuite ajouter un nouveau visiteur si tu as besoin, par exemple pour compter le nombre d'employés hommes en CDI d'une entreprise :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    public class VisiteurEntrepriseCompteurEmployes implements VisitorEntreprise, VisitorEmploye {
        private StatsEntreprise resultat;
        private AssembleurDeDTO assembleurDTO;
     
        public void visitEntreprise(Entreprise e) {
            resultat.setNomEntreprise(e.getNom());
            ...
        }
     
        public void visitEmploye(Employe emp) {
            if (emp.getContrat().getType().equals("CDI") 
                   && emp.getSexe().equals("M"))
                resultat.incrementCompteurCDIetSexeMasculin();
        } 
     
        public StatsEntreprise getResultat() {
            return resultat;
        }
    }
    Tu vois l'interet ? Visitor est l'un des patterns les plus puissants mais il est trop souvent ignoré.

    Citation Envoyé par mauvais_karma
    [3] Comment réaliser la conversion VO --> BO le plus simplement et le plus rapidement possible?
    Ca peut dépendre de la complexité de ton VO et de ce que tu souhaites faire.

    Citation Envoyé par mauvais_karma
    Au début je pensais propager les modifcation du VO au fur et à mesure sur les BO encapsulés au travers les setters du VO, mais cette solution ne marche pas lors de la destruction d'un VO ou seule la référence vers le BO est supprimée, mais le BO en lui meme car il est référencé ailleurs dans le graphe...

    De plus, je souhaite utiliser la puissance de la dao qui s'appuie sur Hibernate et qui détecte automatiquement les changements faits sur le BO au moment de l'update! Donc ce serait stupide de faire 2 fois le travail à cause des VO.
    Si possible, transforme le plus tot possible ton VO en BO et réassocie le à ta session (saveOrUpdate par exemple)

  3. #3
    Membre averti
    Inscrit en
    Août 2005
    Messages
    352
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 352
    Points : 427
    Points
    427
    Par défaut
    Bon je me corrige avant de me faire corriger, j'ai oublié un point essentiel (et oui, la relation est bi-directionnelle), dans mon code il manque ca :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public class Employe implements Visitable {
        ...
        public void accept(Visitor visitor) {
            if (visitor instanceof VisitorEmploye)
                ((VisitorEmploye)visitor).visitEmploye(this);
            entreprise.accept(visitor); //ET LA TU DOIS ME DIRE : QUE FAIS TU MALHEUREUX !!!
        }
    }
    Je te laisse un peu réfléchir au problème engendré par cette nouvelle ligne et à la solution à mettre en place dans le visiteur (je te conseille de réfléchir à la profondeur de ton arbre et au fonctionnement du cache de premier niveau d'hibernate, j'ai presque tout dit)

    En utilisant une approche procédurale (ta solution ou celle de martin fowler), le problème ne se posera pas, mais la solution est moins puissante (il ne faut toutefois pas dramatiser le code supplémentaire à écrire). A toi de voir...

  4. #4
    Membre régulier
    Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2002
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Octobre 2002
    Messages : 89
    Points : 111
    Points
    111
    Par défaut
    je connait pas bien hibernate, mais dans jdo il y a moyen de "détacher" les objets pour en faire des VO
    Il faut juste s'assurer que le graphe des objet est chargé au moment du detach. Ce sera le cas si toutes les méthodes sont appelé avant de détacher.

  5. #5
    Membre habitué
    Inscrit en
    Décembre 2002
    Messages
    186
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 186
    Points : 130
    Points
    130
    Par défaut
    ok merci pour ce pattern, il me faisait un peu peur jusqu'à maintenant mais il est vraiment tres pratique.
    Tout d'abord, par rapport à ta remarque sur la distribution, elle n'est malheureusement pas réelement assurée par ma solution avec l'Adapter/Wrapper, car le VO encapsule encore un BO! Il masque juste certains champs/méthodes (il évite par exemple l'axes aux champs non initialisé du proxy), donc je vais revoir ce système un peu + tard, en attendant je conserve ce systeme qui me fait écrire pratiquement aucun code pour tous mes VO...

    Toutefois, tout cela ne résout pas mon probleme de update d'un BO si je retourne des graph de VO au client... En effet ce dernier ne peut me retourner qu'un VO modifié. Donc je dois le convertir en BO pour l'update.
    La différence avec l'opération BO --> VO, c'est que je disposais de tous les champs nécessaires pour construire le VO. Or dans le cas VO --> BO, il me faut aller taper dans la base et mettre à jour les champs renseignés par le VO...

    Alors sinon la solution que j'ai en ce moment, c'est que je mémorise les actions effectuées sur les VO en leur faisant implémenter des méthodes isUpdated/isDeleted/isNew. Ensuite, soit je parcour l'arbre et je classe les objets par liste remove ou addOrUpdate, soit je reconstruit un BO complet que j'update (et hibernate fera le reste...)

    En tt cas je vais regarder si le visitor ne peut pas m'aider aussi à reconstruire mon BO + rapidement...


    Quand à la solution qui utilise le detach/merge d'Hibernate, j'évite car ce n'est utilisable efficacement que dans la couche de service, (la dao ne peut pas connaitre à l'avance l'usage de l'objet retourné) du coup c'est un peu intrusif.

    En tout cas g lu des artickles tres interessants sur les VO de toutes sortes, je résumerai un peu ca ce soir pour en discuter.

    a+

  6. #6
    ego
    ego est déconnecté
    Rédacteur

    Homme Profil pro
    Architecte de système d'information
    Inscrit en
    Juillet 2004
    Messages
    1 883
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Architecte de système d'information
    Secteur : Finance

    Informations forums :
    Inscription : Juillet 2004
    Messages : 1 883
    Points : 3 510
    Points
    3 510
    Billets dans le blog
    2
    Par défaut
    Plusieurs points :

    - Si tu as autant de modifications des tes graphes sur le client, c'est probablement que ton client en fait trop.
    - Si tu veux quand même te rappeler de toutes ces actions et donc les "conserver" avant de tout communiquer au serveur, regardes la spec SDO. Tu verras aussi que tu te lances dans qq chose de très complexe.
    - Il est préférable de communiquer plus fréquemment avec le serveur pour lui dire ce que veux faire le client (surtout dans les cas de suppression d'objets)
    - Pour les patterns et la problématique de passage de grappes d'objets voici ce que je fais :
    * Sur le serveur, j'ai des BO. Ils contiennent donc de la donnée et des traitements
    * Ce que je passe aux autres tiers, c'est uniquement la partie "données". Je ne peux donc pas encapsuler des BO dans des VO car mes BO ont de l'intelligence qui n'a pas de sens sur le client et en plus, cette intelligence risque de m'obliger à installer des librairies sur le client dont le client n'a rien à faire.
    * Pour chaque BO, je créé un VO qui comporte les attributs de mon BO. Les attributs du type d'un autre objet ou de type collection sont déclarés transients (sauf cas d'attributs jugés intrinsèques et devant toujours être serializés avec mon VO)
    * Pour mes différents échanges, je créé des DTO adaptés. Ces DTO s'appuient sur les VO et sont chargés d'être la représentation du graphe que je veux sérializer. Le DTO contient une méthode "initRelationships" que va s'exécuter sur le client. L'objectif de cette méthode est d'initialiser les références et collections de mes VO que j'ai rapatrié avec mon DTO.

    Pour faire simple, si j'ai la situation suivante (BO):

    Client--1---------*--Commande

    J'aurais :

    ClientVO
    - attributs de Client
    - Liste de Commande transient

    CommandeVO

    Et un DTO, par exemple ClientCommandesDTO pour récupérer d'un coup le client et ses commandes :

    ClientCommandesDTO
    - attribut de type Client
    - Liste des commandes du Client
    - "initRelationships" va prendre la liste des commandes du DTO pour initialiser la liste des commande du client. Oui, c'est la même que côté serveur mais la liste du client n'a pas été transmisse car elle est transiente alors que la liste du DTO n'est pas transiente et a été transmisse. Bref, je reconstitue le graphe côté client.

    Mes VO sont fait ainsi car cela me permet de les réutiliser dans plusieurs DTO voir de les utiliser eux-mêmes comme objet de transfert.

    Il faut dans tout cela penser aussi que pour un même BO on peut avoir besoin de plusieurs VO, un VO "complet" et des VO "partiels" (sortes de vues)

    Pour faire les transformation VO-BO et BO-VO, on peut utiliser des frameworks comme Dozer

Discussions similaires

  1. [RIA Services] Graphes d'objets et includes
    Par anthyme dans le forum Silverlight
    Réponses: 0
    Dernier message: 13/06/2011, 23h35
  2. Architecture services ?
    Par cocoyot dans le forum Wildfly/JBoss
    Réponses: 9
    Dernier message: 17/12/2007, 22h03
  3. Charger un graphe d'objets
    Par RogerXXX dans le forum Hibernate
    Réponses: 11
    Dernier message: 15/10/2007, 18h29
  4. Réponses: 13
    Dernier message: 22/06/2007, 19h49
  5. Réponses: 1
    Dernier message: 06/02/2007, 17h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo