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

MVC Discussion :

design objet relations Entite/Repository


Sujet :

MVC

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut design objet relations Entite/Repository
    Bonjour
    Afin d'illustrer mon probleme je vais prendre un exemple tres simple :
    J'ai un mini projet de Todolist MVC avec 2 entités: Directeur et Employé.
    Le langage n'a pas d'importance, par contre je ne peux pas utiliser de couche ORM.
    le Directeur crée les todos et les assigne aux employés qui eux doivent les traiter
    Il y a ainsi 2 pages
    1. une page avec les états de toutes les todolists (backend Directeur)
    2. une page filtrée avec juste les todos de l'employé connecté (frontend)

    Ca va paraitre bête, mais j'ai une hésitation coté Model :

    1. soit je fais une composition d'objets
    entre l'entité et un Repository unique nommé TodosRepository qui contient findAll(), findBy*()
    l'entité contient un getter tel que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public function todos(){
       if ($this->_todos == null)
         $this->_todos = new TodosRepository($this);
       return $this->_todos;
    }
    Les rôles sont bien séparés : L'entité expose des actions métier, mais en interne elle appelle le Repo qui lui s'occupe du requetage BDD.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    j'utilise alors soit $entite->todos()->findAll();
    soit un wrapper $entite->findAllTodos();
    La fonction TodosRepository::findAll() peut etre appelée aussi bien par Directeur que par Employe. En soi ca parait intéressant.
    Mais le requetage dépend du type de l'entité : dans le cas où c'est le Directeur qui invoque la fonction, aucun filtrage ne doit s'appliquer. Dans le cas de l'Employer, le filtrage doit s'appliquer.
    Pour savoir si le filtre doit s'appliquer ou non je me base sur le type réel de l'objet. Donc la au niveau modélidation objet ya un probleme.
    Ou alors findAll() doit attendre un parametre $idEntite;
    si $idEntite == null alors pas de filtrage.

    2. soit je crée des Repository dédiés
    "DirecteurTodos", "EmployeTodos" et chacun sait ce qu'il a a faire, mais c'est dommage de se passer de la composition d'objets

    3. soit j'implémente findAll() dans l'Entite.
    Du coup le Repositiory n'a plus lieu d'etre mais ya mélange Métier+Persistance

    J'ai du mal a voir ce qui est le mieux a faire.
    help ! merci d'avance

  2. #2
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut
    autre possibilité :

    4. Soit je polymorphe la fonction findAll() dans chaque Entite.
    Ya pas de classe TodosRepository avec une méthode
    prédéterminée et générique findAll() car c'est trop conditionnel pou etre générique :
    en effet ya 2 entités qui l'utilisent mais de facon tres différente.

    Ainsi $entite->findAll() appelle en interne l'unique requeteur de table Repository et le paramètre avec les filtres dont il a besoin.
    Résultat : Repository agit comme un messager qui travaille aupres de la BDD et qui renvoit des data, mais il ne sait pas d'avance quelles data il doit renvoyer.
    Il ne connait meme pas l'objet (l'entité) qui le manipule, en revanche l'entité, elle , sait comment manipuler le Repository pour obtenir ce qu'elle veut

    Je pense que la 4e est une bonne maniere de faire

    qu'en pensez vous ?

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    Août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 89
    Points : 170
    Points
    170
    Par défaut
    Bonjour,

    Je pense que votre problème se situe dans le fait que vous essayez d'attribuer une responsabilité (obtenir les tâches) à un objet qui ne devrait pas l'avoir (entité directeur ou employé), parce qu'a priori, il ne connait pas l'information (au moins en base de données).

    Je pense d'ailleurs que l'entité n'a pas besoin de cette méthode.
    En essayant d'analyser votre application, il y a un élément de votre message qui me paraît essentiel :
    Il y a ainsi 2 pages
    1. une page avec les états de toutes les todolists (backend Directeur)
    2. une page filtrée avec juste les todos de l'employé connecté (frontend)
    Il indique qu'il y a au moins deux vues séparées, et surtout que l'application propose deux fonctionnalités séparées (une pour les directeurs, l'autre pour les employés), donc deux contrôleurs.
    NB : Si une interface administrateur est également prévue, elle nécessiterait probablement aussi une vue et un contrôleur dédiés.

    Si on essaye d'identifier les besoins des contrôleurs par rapport au modèle, on en distingue deux : accéder aux tâches "créée par" et accéder aux tâches "attribuée à".

    Donc de mon point de vue, l'interactivité proposée aux différents types d'entité se fait principalement au niveau contrôleur.
    Au niveau du modèle, Les entités 'Manager' et 'Employee' ne doivent être présentes qu'afin de retourner les infos propres à ces entités (id, nom, fonction, coordonnées, etc..) et éventuellement de vérifier les droits d'accès de l'utilisateur.
    Par contre, le modèle doit implémenter un objet TodoRepository qui implémenterait des méthodes getTodosCreatedBy(managerId) et getTodosAssignedTo(employeeId).

    Voilà, je ne sais pas si je répond à la question dans le sens où je n'indique pas un des choix proposés. J'espère au moins que ce message vous donnera quelques pistes de réflexion.


    Sinon, un peu hors-sujet, considérer que seuls les directeurs peuvent assigner des tâches et seuls les employés peuvent avoir des tâches assignées est assez dangereux.
    Que se passe-t-il lorsqu'un 'employé' est promu 'directeur'?
    Lorsqu'un 'directeur' reçoit des tâches des ses chefs?
    Lorsqu'un 'employé' est jugé suffisamment responsable pour pouvoir créer des tâches mais qu'il n'est pas 'directeur'?
    Si c'est une application qui doit perdurer, il vaut mieux privilégier une architecture avec un seul type d'entité (et une seule table) qui aura un attribut 'canCreateTodos'

  4. #4
    Membre émérite
    Inscrit en
    Janvier 2011
    Messages
    805
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Janvier 2011
    Messages : 805
    Points : 2 918
    Points
    2 918
    Par défaut
    Définitivement la 4.

    Et encore, il n'y a pas forcément besoin de polymorphisme puisqu'il n'y a apparemment pas de code client qui va appeler todos() sur un objet sans savoir si c'est un Directeur ou un Employé. En effet la liste des tâches et le backend avec les TodoList paraissent deux pages complètement séparées.

    En d'autres termes, on peut sans souci avoir employe.todos() dans le premier cas et directeur.allEmployeeTodos() dans le deuxième. Même si on peut objecter au niveau métier que cette méthode n'a pas forcément sa place dans l'objet Directeur (autant il y a une association 1-n claire entre employé et todo, autant le directeur ne possède pas les todo de ses employés...).

    La 1. ne me parait pas viable car comme tu dis tu vas devoir faire un if sur le type de l'objet (on foule au pieds le polymorphisme) ou passer en paramètre l'id de l'entité au Repository ce qui est contre-intuitif et sans aucun sens.

    La 2. viole un peu le concept de repository puisque l'intérêt est d'avoir un repository par entité et pas un repository par objet appelant.

    Sur la 3. tu pointes bien le problème : classe à plusieurs responsabilités, faible cohésion, peu maintenable, sujette à bugs...

    Attention aussi au couplage fort entre Employé et Repository lorsque tu fais new TodosRepository($this) (d'ailleurs, pourquoi passer $this ?) Injecter le repository ou à la rigueur un service locator améliorerait la testabilité et la maintenabilité.

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par tristan_m
    Bonjour,
    Je pense que votre problème se situe dans le fait que vous essayez d'attribuer une responsabilité (obtenir les tâches) à un objet qui ne devrait pas l'avoir (entité directeur ou employé), parce qu'a priori, il ne connait pas l'information (au moins en base de données).
    ok tu verrais ca tel que : Controller A qui demande au service TodosRepository les Todos de l'entité B.

    Alors que pour moi je trouve plus évident d'interroger l'entité concernée.
    C'est elle qui doit savoir répondre a la question "quelles sont mes Todos".
    C'est pas juste un container de données (le terme c'est DTO je crois),
    mais un objet "intelligent", qui dans sa fonction getMyTodos() etre capable d'interroger TodosRepository
    pour recup les données demandées a l'intérieur de la méthode getTodos().
    Sinon a quoi sert l'entité, elle n'aurait aucune intelligence ?
    Ce qui n'empeche pas d'inverser le décors et d'interroger directement le TodosRepository sans passer par Entite::getMyTodos()

    finalement on peut travailler des 2 manieres :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class EmployeTodoListView
    {
        public void EmployeTodoListView(int IdEmploye)
        {
            // IdEmploye : récupéré via GET (http://todoappli.local/employe/1/todos    
            // recup l'Entite employé
            Employer employer = EmployesRepository.instance().findById(IdEmploye);
            // 1re maniere: on demande au Repository les todos de l'entité 
            List<Todo> todoList = TodosRepository.instance().findForEmploye(employe);
            // 2e maniere: on demande a l'employé ses Todolist
            List<Todo> todoList = employe.getTodoList(); 
        }
    }
    dans le fond les 2 exemples font la meme chose, mais le chemin logique est différent :
    - dans le 1er cas on va se demander "quoi et comment obtenir", et donc réfléchir au chemin logique necessaire pour obtenir la TodoList
    - dans le 2e cas on va se demander seulement "qu'est ce qu'on veut obtenir"
    la question du "comment obtenir" selon moi relève du travail de la fonction getMytodo() de l'entité
    qui doit abstraire la manière.
    On pourrait résumer la demande telle que : "Je me fiche de savoir comment tu fais, je veux juste que tu me donne l'info que j'attend"
    Ca me rappelle une celebre phrase :
    "pour faire avancer un chien on lui ordonne d'avancer, on ne lui bouge pas les pattes".

    Citation Envoyé par tristan_m
    Je pense d'ailleurs que l'entité n'a pas besoin de cette méthode.
    nous avons donc 2 points de vue différents. c'est intéressant.

    Citation Envoyé par tristan_m
    En essayant d'analyser votre application, il y a un élément de votre message qui me paraît essentiel :

    Il indique qu'il y a au moins deux vues séparées, et surtout que l'application propose deux fonctionnalités séparées (une pour les directeurs, l'autre pour les employés), donc deux contrôleurs.
    NB : Si une interface administrateur est également prévue, elle nécessiterait probablement aussi une vue et un contrôleur dédiés.

    Si on essaye d'identifier les besoins des contrôleurs par rapport au modèle, on en distingue deux : accéder aux tâches "créée par" et accéder aux tâches "attribuée à".
    en effet il ya 2 actions et donc 2 vues (relation 1:1) :
    * pour un Employé : "voir mes todos"
    -> + 1 sous action : " changer l'etat d'une todo"
    * pour un Directeur : voir l'etat d'avancement de toutes les todos par employé

    Citation Envoyé par tristan_m
    Donc de mon point de vue, l'interactivité proposée aux différents types d'entité se fait principalement au niveau contrôleur.
    Oui less actions utilsateur sont déclenchées a partir de L'IHM et interceptées/traitées par le Controller.
    [front controller- > routage -> controlleur.action()]

    Citation Envoyé par tristan_m
    Au niveau du modèle, Les entités 'Manager' et 'Employee' ne doivent être présentes qu'afin de retourner les infos propres à ces entités (id, nom, fonction, coordonnées, etc..)
    en fait tu vois les entité comme des DTO, c a dire une structure (les infos), sans méthodes (comportement).
    Sans interactivité dans l'objet, comment faire si un employé veut changer l'etat d'une todo de "en cours" a "terminée"

    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
    
    class TodosController extends Controller
    {
        public void updateStatus(int idEmploye, int idTodo)
        {
            Employer employer = EmployesRepository.instance().findById(idEmploye);
        
            //toi tu verras plutôt : 
            Todo todo = TodosRepository.instance().findByNum(idTodo);
            todo.status = TodoStatus.DONE;
            todo.todoDoneAt = new Date();
            
            //pour ma part je le vois ainsi :
            employe.todos.findByNum(idTodo)->setStatus(TodoStatus.DONE);
            
            //puis pour mémoriser ce changement en base :
            TodoRepository.instance().save(todo);   // ou persist, ou flush...
            
            // mais pas ca (meme si c'est un raccourci séduisant)  sinon amalgame entre couche Domain et couche Persistance
            - todo.update()
        }    
    }
    Citation Envoyé par tristan_m
    et éventuellement de vérifier les droits d'accès de l'utilisateur.
    Ca fait porter 2 responsabilités à l'entité : métier et ACL
    Il faut un objet responsable des ACL , qui se situe dans la fonction du Controller de sorte a regulere ce qu'il manipule/

    Citation Envoyé par tristan_m
    Par contre, le modèle doit implémenter un objet TodoRepository qui implémenterait des méthodes getTodosCreatedBy(managerId) et getTodosAssignedTo(employeeId).
    TodosRepository n'est donc plus un DataMapper agnostique avec des méthodes classiques comme findBy*(), findAll(),
    mais un Repository dédié a la table des todos avec des méthodes prédéterminées.
    En soit l'idée est intéressante, c'est "comme des vues SQL".

    Mais ce qui me gene et je pense que c'est ici quse se trouve le flou de mon histoire :
    "la question de l'appartenance" :

    - getTodosCreatedBy() est une méthode publique du Repository : donc TodosRepository est comme un entrepot de diverses fonctions
    applicables a la table des todos.
    - ca signifie que c "open bar" : n'importe quelle entité peut l'appeller librement meme si elle est pas censée d'apres la Domain Logic
    on balaie la notion d'appartenance entre l'Entite et les fonctions qui concernent qu'elle.
    - ca donne plus de boulot a l'objet d'ACL : si pas de scope, il faut créer des regles en plus pour pas que
    le Controller puisse permettre a un employé de voir les todos d'un autre employé.
    Tandis que si la fonction appartient a Employé, le scope est restreint, le Controlleur peut afficher que
    celles attribués à cet employé ci. Point barre, le perimetre est fixé.

    Citation Envoyé par tristan_m
    Sinon, un peu hors-sujet, considérer que seuls les directeurs peuvent assigner des tâches et seuls les employés peuvent avoir des tâches assignées est assez dangereux.
    Que se passe-t-il lorsqu'un 'employé' est promu 'directeur'?
    Lorsqu'un 'directeur' reçoit des tâches des ses chefs?
    Lorsqu'un 'employé' est jugé suffisamment responsable pour pouvoir créer des tâches mais qu'il n'est pas 'directeur'?
    Si c'est une application qui doit perdurer, il vaut mieux privilégier une architecture avec un seul type d'entité (et une seule table) qui aura un attribut 'canCreateTodos'
    [/quote]

    non c'est pas hors sujet, ta raison c'est au contraire tres important.
    Il ne faut pas figer le modele.
    La on touche en plein au sujet des ACL :
    L'erreur c'est de trop specialiser des types d'objet qui sont constant, qui ne pourront pas evoluer plus tard,
    et d'attribuer des droits a ces objets constant.
    En fait il faut attribuer des rôles a ces objets et des droits à ces rôles, car un rôle est variable, il peut évoluer
    aussi bien que l'assignation des droits.

    Tu as raison : Employé n'est pas une entité en soi mais un rôle.
    On aurait plutôt une entité Collaborateur qui a le rôle soit de Manager, soit d'Employé, soit de PDG, etc...
    On peut ainsi décrire un organigramme d'entreprise dans une matrice de configuration des ACL.
    Comme le fait un Active Directory en somme : on gère des groupes (des roles), leurs droits,
    on met des utilisateurs dans ces groupes pour leur donner tel role, et tout ca évolue.
    Les utilisateurs du réseau de l'entreprise en eux meme restent inchangés. c'est leur accès aux ressources qui change.

    Par contre Collaborator.canCreateTodo() serait a mon avis un wrapper ves l'objet qui gere les ACL.

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par Luckyluke34
    Définitivement la 4.
    En d'autres termes, on peut sans souci avoir employe.todos() dans le premier cas et directeur.allEmployeeTodos() dans le deuxième. Même si on peut objecter au niveau métier que cette méthode n'a pas forcément sa place dans l'objet Directeur (autant il y a une association 1-n claire entre employé et todo, autant le directeur ne possède pas les todo de ses employés...).
    ou alors directeur.employes.allEmployesTodos()
    "toutes les todos des employés supervisé par ce directeur"

    par contre comme le dit tristant_m il ne faut pas de classe Manager ni Employe mais un type plus abstrait Collaborateur.
    Sauf que on sait pas d'avancE le role du collaborateur.
    Dans le cas d'un Employé il peut avoir un getter "todos()" mais pas dans l'autre cas.
    Ou alors on recup les data du Collaborateur en base, et on le caste en sortie en tant qu'objet de type Role, et de sous-type Manager ou Employé pour garder la cohérence d'implémentation de chacun.

    Citation Envoyé par Luckyluke34
    La 1. ne me parait pas viable car comme tu dis tu vas devoir faire un if sur le type de l'objet (on foule au pieds le polymorphisme) ou passer en paramètre l'id de l'entité au Repository ce qui est contre-intuitif et sans aucun sens.
    voila c exactement ca.

    La 2. viole un peu le concept de repository puisque l'intérêt est d'avoir un repository par entité et pas un repository par objet appelant.

    Citation Envoyé par Luckyluke34
    Sur la 3. tu pointes bien le problème : classe à plusieurs responsabilités, faible cohésion, peu maintenable, sujette à bugs...
    oui ca pionte beaucoup la question de l'appartenance.
    Citation Envoyé par Luckyluke34
    Attention aussi au couplage fort entre Employé et Repository lorsque tu fais new TodosRepository($this) (d'ailleurs, pourquoi passer $this ?) Injecter le repository ou à la rigueur un service locator améliorerait la testabilité et la maintenabilité.
    au départ c'etait pour faire de la composition tel que TodosRepository est une classe à qui Employé délègue ses besoins de requetage.
    Par injection tu veux dire "injection de dépendance" :
    mais ca signifie que a chaque fois que j'instancie un employé, je dois lui passer la référence a TodosRepo ?
    a moins que ca soit automatisable dans un framework...?

  7. #7
    Membre émérite
    Inscrit en
    Janvier 2011
    Messages
    805
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Janvier 2011
    Messages : 805
    Points : 2 918
    Points
    2 918
    Par défaut
    Citation Envoyé par elderion Voir le message
    par contre comme le dit tristant_m il ne faut pas de classe Manager ni Employe mais un type plus abstrait Collaborateur.
    Pourquoi ?

    A la rigueur si un Manager a des Todo, pourquoi pas, mais il me parait dangereux de vouloir utiliser un type abstrait à tout prix dès que deux classes se ressemblent de près ou de loin. Ca me rappelle un peu les exemples de livres d'école où Client et Employé héritent forcément de la classe Personne...

    Encore une fois, si la classe Collaborateur est une abstraction qui aide d'autres objets à manipuler un Manager et un Employé de façon indifférente (je ne vois pas ce cas dans ton exemple), elle est utile, sinon ça sera probablement source de problèmes...

    Par injection tu veux dire "injection de dépendance" :
    Oui

    mais ca signifie que a chaque fois que j'instancie un employé, je dois lui passer la référence a TodosRepo ?
    Soit ça, soit juste passer le repository en paramètre à la méthode.

    a moins que ca soit automatisable dans un framework...?
    Oui. Par contre je ne connais pas les frameworks d'injection de dépendance qui existent en (PHP ?)

    Mettre en place l'injection de dépendance à l'échelle de l'application est un travail de longue haleine qui ne convient pas forcément à tous types d'appli, mais c'est bien de savoir que ça existe et de connaitre les problèmes liés à un couplage fort => classes moins facilement testables unitairement, moins de réutilisabilité, moins de modularité, etc.

    Autre solution si tu veux vraiment creuser plus loin : l'approche Domain Driven Design (le pattern Repository vient de là). Dans DDD, les classes métier liées entre elles sont regroupées en agrégats. Lorsqu'on charge depuis la base de données la racine d'un agrégat, on charge tout le graphe d'objets qui en dépendent. Par exemple si je charge un Employé depuis son repository, cela peuple aussi sa variable d'instance qui contient sa liste de Todo donc plus besoin que l'Employé ait accès au repository des Todo pour faire employe.todos(). D'ailleurs, il n'y aurait pas besoin de repository de Todos. Seul la racine de l'agrégat (Employé) a besoin d'un repository.

    Cette approche permet de raisonner en petit groupes d'objets liés les uns aux autres qui sont chargés en même temps, supprimés en même temps, etc.

  8. #8
    Membre habitué
    Profil pro
    Inscrit en
    Août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 89
    Points : 170
    Points
    170
    Par défaut
    Bonjour,

    Discussion extrêmement intéressante, j'apprends beaucoup de choses. Merci pour vos réponses détaillées!
    J'ai manifestement une approche de l'application très différente de la votre (j'ai probablement tort d'ailleurs )

    Citation Envoyé par elderion
    en fait tu vois les entité comme des DTO, c a dire une structure (les infos), sans méthodes (comportement).
    Tout à fait. C'est d'ailleurs comme ça que j'imagine le fonctionnement du modèle entier.
    Le rôle du modèle est, pour moi, de définir le fonctionnement de l'environnement de l'application ainsi que le périmètre autorisé. Tout ce qui est pûrement applicatif est dans le contrôleur (il définit le fonctionnement de l'application).

    Citation Envoyé par elderion
    On pourrait résumer la demande telle que : "Je me fiche de savoir comment tu fais, je veux juste que tu me donne l'info que j'attend"
    Apparemment vous souhaitez rendre les tâches transparentes du point de vue du contrôleur?
    C'est-à-dire ne jamais manipuler de tâches à proprement parler dans le contrôleur?
    Vous considérez alors les tâches comme faisant partie de l'environnement de l'application, qui ne seraient modifiées qu'en conséquences d'interactions sur un 'entity'?
    Si c'est le cas, je pense que c'est là que se situe notre principale différence de point de vue. Et du coup je n'ai peut être pas compris ce que devait faire l'application, car j'ai un réel problème à considérer les tâches complètement abstraites dans le contrôleur


    NB : En attendant un éclaircissement éventuel sur la question précédente, je répond sur les points suivants, en considérant des tâches accessibles au niveau du contrôleur

    Citation Envoyé par elderion
    Sans interactivité dans l'objet, comment faire si un employé veut changer l'etat d'une todo de "en cours" a "terminée"
    Le fait qu'un employé décide de changer l'état d'une tâche est typiquement implémenté dans le contrôleur. Pour le modèle, cette opération doit uniquement être une action sur la lecture/modification/création/suppression d'une tâche (qu'il autorise ou non) venant du contrôleur.

    Citation Envoyé par elderion
    Il faut un objet responsable des ACL , qui se situe dans la fonction du Controller de sorte a regulere ce qu'il manipule/
    Pour moi les ACL doivent au moins se situer dans le modèle, puisqu'in fine c'est lui qui donne l'autorisation pour effectuer une transaction (lecture ou écriture). Rien n'empêche de faire une première passe dans le contrôleur par contre.

    Citation Envoyé par elderion
    ca signifie que c "open bar" : n'importe quelle entité peut l'appeller librement meme si elle est pas censée d'apres la Domain Logic
    on balaie la notion d'appartenance entre l'Entite et les fonctions qui concernent qu'elle.
    NB : J'aime bien l'expression "open bar"
    Encore une fois, pour moi ce sont des objets "Todos" qui sont manipulés directement au sein du contrôleur. Par contre effectivement, dans le contrôleur on ne peut pas empêcher qu'un employé crée une tâche. Dans ce cas là : Le modèle peut toujours bloquer avec les ACL, ou alors c'est le comportement de l'application (donc défini dans le contrôleur) qui veut ça.

    Citation Envoyé par elderion
    ca donne plus de boulot a l'objet d'ACL : si pas de scope, il faut créer des regles en plus pour pas que
    le Controller puisse permettre a un employé de voir les todos d'un autre employé.
    A moins qu'il y ait une subtilité au niveau de l'implémentation à laquelle je ne pense pas, ce n'est vrai que si le contrôleur manipule directement l'objet 'entity' du modèle (auquel cas il y aurait une autre divergence de points de vue). Sinon le modèle doit au moins vérifier que l'id de l'utilisateur correspond à l'"entity" indiqué dans l'action envoyée par le contrôleur.


    Citation Envoyé par elderion
    On aurait plutôt une entité Collaborateur qui a le rôle soit de Manager, soit d'Employé, soit de PDG, etc...
    Citation Envoyé par Luckyluke34
    Encore une fois, si la classe Collaborateur est une abstraction qui aide d'autres objets à manipuler un Manager et un Employé de façon indifférente (je ne vois pas ce cas dans ton exemple), elle est utile, sinon ça sera probablement source de problèmes...
    En fait ce que je voulais dire, c'est que être directeur ou employé c'est une chose, avoir les droits de créer une tâche ou non en est une autre.
    Directeur est un rôle au sein de l'entreprise.
    Au niveau de l'application, c'est le droit à créer une tâche qui est important.
    Relier les deux n'est pas forcément judicieux, et les notions de PDG et autres ne devraient pas être significatives/interprétées dans l'application.
    Après, comme le fait remarquer Luckyluke34, il peut y avoir plusieurs classes pour désigner les différents types de collaborateurs, mais par contre j'éviterai d'avoir des classes 'Manager' et 'Employee' qui sont des notions extérieures à l'application.
    Personnellement, dans ce cas précis, je confirme mon choix pour une seule classe
    • Pour les raisons que j'ai invoquées dans mon message précédent
    • Parce que bien sûr, pour moi les tâches ne sont pas manipulées par des 'entity' dans le modèle
    • Parce que que mes 'entity' à moi sont "bêtes" .


    Merci encore de partager vos arguments fort intéressants!

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par Luckyluke34
    A la rigueur si un Manager a des Todo, pourquoi pas, mais il me parait dangereux de vouloir utiliser un type abstrait à tout prix dès que deux classes se ressemblent de près ou de loin.
    mais en base j'aurai une seule table collaborateur. Ya aucun intéret a avoir 1 table pour chaque type.
    Un Manager et un employé partagent les meme infos : nom, prénom, adresse, ...
    Selon moi il y a aussi un champ "role" qui permet déterminer quel sera le type de l'entité au sein de l'appli.

    Ma 1re question est :
    comment recupérer un Collaborateur dans la BDD et en faire un objet Employé ou Manager
    manipulable comme tel par le Controller ?
    il faudrait caster dynamiquement le raw object dans le type réel lors du findById(), en fonction de rawCollab.getRole()

    Je sais pas si c'est une bonne pratique...
    mais je vois pas trop comment faire autrement en fait ...
    (quelque part ca me parait cochon)

    Citation Envoyé par Luckyluke34
    Ca me rappelle un peu les exemples de livres d'école où Client et Employé héritent forcément de la classe Personne...
    d'un point de vue anthropologique l'héritage se justifie
    Ca me rappelle quand je suis arrivé dans ma boite : yavait une webapp eLearning où toutes les entités héritaient
    d'une gigantesque classe fourre-tout nommée Personne (qu'il s'agisse d'un formateur, d'un étudiant, d'un organisme...)
    La conception (enfin plutôt de l'anti-conception) avait été faite tellement dans l'abstraction que plus rien n'avait de sens.

    Citation Envoyé par Luckyluke34
    Encore une fois, si la classe Collaborateur est une abstraction qui aide d'autres objets à manipuler un Manager et un Employé de façon indifférente (je ne vois pas ce cas dans ton exemple), elle est utile, sinon ça sera probablement source de problèmes...
    En fait l'entité est a la fois un DTO et une Business Entity :

    * entité en tant que DTO
    on peut lire indifféremment les infos d'une Entité X ou Y
    -> nom, prénom, adresse, role, ...

    * entité en tant que Business Entity
    chaque entité a ses propres méthodes métier :
    -> employe.getTodos(), employe.getTodos(TodoStatus.PENDING), employe.getTodos(1).setStatus(TodusStatus.DONE).update()...
    -> manager.getTodosOf(employe)

    ma 2e question est : le rapport Entité /repository / Controller

    * Le rapport entité/repository me parait simple :
    l'Entité au travers de ses méthodes métier, se sert de TodosRepository pour renvoyer de l'information qui le concerne.
    Le controlleur se sert de l'Entité pour obtenir de l'information. L'entité sert d'intermédiaire.
    TodosRepository n'a pas de méthode prédéterminée comme getTodosAssignedTo().
    Il a uniquement des méthodes généralistes: findById(), findBy*(), findAll(), insert() et update(), delete();
    c'est par ex l'entité Employé qui a une méthode getTodosAssigned()
    qui lui va invoquer TodosRepository.findBy('assigned_to', $this->ID);
    et retourner la List<Todo> au Controlleur qui l'a demandé.

    * le rapport controlleur/repository me parait plus ambigu :

    si le Controller peut bosser avec TodosRepository alors il sait faire le boulot de l'entité
    - l'entité ne sert plus a rien sauf a transporter ses données,
    - le Controlleur devient métier, il se rend service tout seul.

    Probleme :
    si le Controlleur veut recuperer un entité, il ne peut pas, il n'a pas accès aux *Repository
    Peut etre alors faut il faire selon les besoins :
    - cacher TodosRepository au yeux du Controlleur, ne l'exposer qu'aux l'entités qui sont les seules a en avoir besoin
    - exposer CollabsRepository dont se sert le Controlleur pour les récupérer.
    [/QUOTE]

    Citation Envoyé par Luckyluke34
    Soit ça, soit juste passer le repository en paramètre à la méthode.
    - refactoring -
    Ca veut dire que le jour où je dois changer le nom de la classe ou changer de moteur de requetage, je suis bon pour refactoriser
    tous les appels dans toute l'appli ?
    j'ai deja vu cette approche, mais j'ai du mal avec ca, mais ca se defend tres certainement.

    - injection de dépendances -
    Qand l'objet Collaborateur est instancié j'estime qu'il est censé en interne savoir s'interfacer avec TodosRepository.
    Encore une fois c'est comme dans la réalité : le patron (Controller) sait ce qu'il veut (décideur),
    le technicien (Entité) sait comment le réaliser (compétence).

    Moi je suis le Controlleur ,
    Mon job c'est de travailler avec les data et les services proposés par l'Entité.
    c'est pas mon job de dire a l'Entité a quel moteur de requetage il doit s'adresser et comment le manipuler.
    Je me fiche de sa tamboulle interne, comment il travaill et avec quel intermediaire, etc...
    (Loi de Demeter)
    Par ailleurs s'il faut fournir le repository c'est pas a l'entité qu'il faut le founir, mais a
    l'objet qui régit les entités et c'est la a mon avis que l'injection de dépendances opère.
    je me trompe ?

    Ainsi les méthodes métiers des entités créent un niveau d'abstraction intermédiaire, acessible a partir du Controller.

    En fait je crois que j'ai résoluement une vision de Services :
    le controller est un client quelconque qui demande de l'info a une entité-service-web.
    et le but du service web est ala fois de fournir de l'infomation et de masquer l'implémentation interne.

    Citation Envoyé par Luckyluke34
    Oui. Par contre je ne connais pas les frameworks d'injection de dépendance qui existent en (PHP ?)
    En géénral le systeme d'IoD est intégré dans un framework de plus haut niveau.

    Citation Envoyé par Luckyluke34
    Mettre en place l'injection de dépendance à l'échelle de l'application est un travail de longue haleine qui ne convient pas forcément à tous types d'appli, mais c'est bien de savoir que ça existe et de connaitre les problèmes liés à un couplage fort => classes moins facilement testables unitairement, moins de réutilisabilité, moins de modularité, etc.
    La a priori il s'agit uniquement d'injection concernant du moteur de requetage (le Repository)

    Citation Envoyé par Luckyluke34
    Autre solution si tu veux vraiment creuser plus loin : l'approche Domain Driven Design (le pattern Repository vient de là). Dans DDD, les classes métier liées entre elles sont regroupées en agrégats. Lorsqu'on charge depuis la base de données la racine d'un agrégat, on charge tout le graphe d'objets qui en dépendent. Par exemple si je charge un Employé depuis son repository, cela peuple aussi sa variable d'instance qui contient sa liste de Todo donc plus besoin que l'Employé ait accès au repository des Todo pour faire employe.todos(). D'ailleurs, il n'y aurait pas besoin de repository de Todos. Seul la racine de l'agrégat (Employé) a besoin d'un repository.
    Cette approche permet de raisonner en petit groupes d'objets liés les uns aux autres qui sont chargés en même temps, supprimés en même temps, etc.
    intéressant cette notion de graphe et d'agregat.
    En fait en DDD, c'est la racine de l'agrégat qui sait comment recup toutes les todos de l'employé.
    ce n'est plus au développeur de manipuler a la main le Repository (ou écrire la requete SQL) en écrivant le code a la main.
    Donc en DDD ya plus de Repository, l'agregat se débrouille tout seul grace aux graphes ?
    Par contre ca pose la question du lazy loading : si on veut que l'employé, sans ses todos :
    peut on recuperer que l'employé en base, c a dire une portion seulement du graphe ?

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par tristan_m Voir le message
    Bonjour,

    Discussion extrêmement intéressante, j'apprends beaucoup de choses. Merci pour vos réponses détaillées!
    J'ai manifestement une approche de l'application très différente de la votre (j'ai probablement tort d'ailleurs )
    D'un simple post au depart tous es échanges, conseils et points de vue deviennent passionnant !
    Ravi que ca t'aide

    Citation Envoyé par tristan_m Voir le message
    Tout à fait. C'est d'ailleurs comme ça que j'imagine le fonctionnement du modèle entier.
    Le rôle du modèle est, pour moi, de définir le fonctionnement de l'environnement de l'application ainsi que le périmètre autorisé. Tout ce qui est pûrement applicatif est dans le contrôleur (il définit le fonctionnement de l'application).
    Donc si j'ai bien compris ta vision :
    - Les objets du Model sont simplement des transporteurs de données (DTO)
    ce sotn des objets a l'image des rows en base de données. Il n'otn aucun intelligence
    - Les controllers renferment la logique métier, l'intelligence, le focntionnement de l'appli (Domain / Business)
    - il n'y a pas de Business Entity.

    Citation Envoyé par tristan_m Voir le message
    Apparemment vous souhaitez rendre les tâches transparentes du point de vue du contrôleur?
    C'est-à-dire ne jamais manipuler de tâches à proprement parler dans le contrôleur?
    A mon sens oui l'implémentation fonctionnelle est a l'initiative de l'Entité.
    Elle abstrait le fonctionnelle au bénéfice du Controlleur.
    Le Controlleur ne sait pas ce qui se passe dedans.
    Autrement dit, au lieu d'avoir une entité "inerte" qui force le Ctrl a se taper tout le boulot a sa place
    autant donner des compétences, des responsabilités aux entités, de sorte que chacune soit responsable d'une partie
    du fonctionnement de l'application.
    Mais certainement que des tas de tutos rejeteront mon approche.

    Mais c'est la toute la question :
    est ce aux Controllers d'implémenter le fonctionnement de l'appli ou aux Entités concernée ?

    Exemple simple et concret : si moi employé je veux mettre le statut "termine" a la todo n°12
    qui doit implémenter cette action ?

    - le controller ? (exemple ?)
    Controller.updateTodoStatus(int idEmploye, int idTodo){
    Employe employe = this.repository('employes').findById(idEmploye);
    // ou si repositiory est capable de caster collab en un employé qui implémente ICollab
    ICollab employe = this.repository('collabs').findById(idEmploye);
    Todo hisTodo = employe.findTodoByNum(idTodo);
    hisTodo.status = TodoStatus.DONE;
    this.repository('todos').update(hisTodo);
    Notifier.notify(employe.getManager());
    }

    - l'employé ? exemple :
    Controller.changeTodoStatus(int idEmploye, int idTodo){
    // l'employé fait que ce qui le concerne
    Employe employe = this.repository('employes).findById(idEmploye);
    employe.changeTodoStatus(idTodo, TodoStatus.DONE);

    // un autre objet Notifier, fait lui aussi son job
    this.Notifier.notify(employe.getManager());

    // Le Controlleur rassemble des objets et délenche des actions pour réaliser son objectif
    // il ne sait pas comment employé met a jour sa todo et la sauve en base
    // il ne sait pas comment Notifier notifie le manager
    }

    Fonctionnement c'est exactement pareil. Mais l'implémentation est dispatchée différemment
    Je sais pas quelle est la mieux.
    Dans le 1er cas : employé est inerte, il se laisse manipuler par le Ctrl
    Dans le 2e cas : le Ctrl est une Facade quelque part.

    Citation Envoyé par tristan_m Voir le message
    Vous considérez alors les tâches comme faisant partie de l'environnement de l'application, qui ne seraient modifiées qu'en conséquences d'interactions sur un 'entity'?
    Si c'est le cas, je pense que c'est là que se situe notre principale différence de point de vue. Et du coup je n'ai peut être pas compris ce que devait faire l'application, car j'ai un réel problème à considérer les tâches complètement abstraites dans le contrôleur
    - "comment faisant partie[...]" ? selon toi une Todo est dissociable de l'environnement de l'appli ?
    c'est quoi alors l'environnement ?
    - "qui ne seraient modifiées qu'en consequence[...]" oui l'entité est le seul intermediaire par lequel on peut modifier une tache

    Tu veux dire que la tache doit etre autonomie, c a dire pas besoin de charger l'envronnement de l'appli pour agir directement sur la tache ?

    Effectivement selon ma maniere, pour marquer une tache "terminée", on doit connaitre d'abord celui a qui elle est attribuée,
    et ensuite celui ci change l'etat de la tache. Elle peut pas etre modifiée directement.
    De toutes facon c'est l'employé et lui seul dans sa page qui peut terminer sa tache.
    Bon apres on peut aller plus loin et conserer que le Manager puisse terminer une tache a la place d'un employé.
    Et alors soit le manager peut le faire lui meme via une ACL, soit il le fait en tant que l'employé.

    Citation Envoyé par tristan_m Voir le message
    Le fait qu'un employé décide de changer l'état d'une tâche est typiquement implémenté dans le contrôleur. Pour le modèle, cette opération doit uniquement être une action sur la lecture/modification/création/suppression d'une tâche (qu'il autorise ou non) venant du contrôleur.
    mm donc le Controlleur declenche toujours l'action sur l'entité. Il fait ce que la requete HTTP lui demande.
    C'est dans la fonction CRUD de l'entité, que l'objet consulte l'ACL pour savoir si l'appelant (le Ctrl) a le droit ou pas.
    Ca serait pas au Controller de consulter l'ACL pour savoir s'il peut ou non délencher l'action sur l'entité ?
    Apres tout "le Controller contrôle".
    Ca me parait dangereux que la demande d'action arrive jusqu'au Model sans verification en amont.

    Citation Envoyé par tristan_m Voir le message
    Pour moi les ACL doivent au moins se situer dans le modèle, puisqu'in fine c'est lui qui donne l'autorisation pour effectuer une transaction (lecture ou écriture). Rien n'empêche de faire une première passe dans le contrôleur par contre.
    NB : J'aime bien l'expression "open bar"
    Encore une fois, pour moi ce sont des objets "Todos" qui sont manipulés directement au sein du contrôleur. Par contre effectivement, dans le contrôleur on ne peut pas empêcher qu'un employé crée une tâche. Dans ce cas là : Le modèle peut toujours bloquer avec les ACL, ou alors c'est le comportement de l'application (donc défini dans le contrôleur) qui veut ça.
    [/QUOTE]

    Que verrais tu comme ACL ?
    - "un Manager ne peut pas modifier l'etat d'une todo" ?
    - "un Employé ne peut pas reassigner une todo a quelqu'un d'autres"
    Comment vois tu ca coté code : implémentation, endroit d'appel

    Citation Envoyé par tristan_m Voir le message
    A moins qu'il y ait une subtilité au niveau de l'implémentation à laquelle je ne pense pas, ce n'est vrai que si le contrôleur manipule directement l'objet 'entity' du modèle (auquel cas il y aurait une autre divergence de points de vue). Sinon le modèle doit au moins vérifier que l'id de l'utilisateur correspond à l'"entity" indiqué dans l'action envoyée par le contrôleur.
    Je sais pas torp comment représenter la mécanique d'ACL
    mais d'apres ce qu tu dis, ca reveindrait a:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Controller.changeTodoStatus(int idTodo)
    {
      if (session.User().ID == todo.ownerID){
        //...on peut modifie le status
      }
      
    }
    sauf que la on appelle jamais l'objet d'ACL, l'assertion est en dur dans la fonction.


    Citation Envoyé par tristan_m Voir le message
    En fait ce que je voulais dire, c'est que être directeur ou employé c'est une chose, avoir les droits de créer une tâche ou non en est une autre.
    Directeur est un rôle au sein de l'entreprise.
    Au niveau de l'application, c'est le droit à créer une tâche qui est important.
    Relier les deux n'est pas forcément judicieux, et les notions de PDG et autres ne devraient pas être significatives/interprétées dans l'application.
    oui . c'est ce que j'ai repondu a luckyluke34
    La nature de la fonction dans l'entreprise en soi on s'en fiche
    mais c'est le rôle (donneur d'ordre VS executant ) qui va déterminer les droits
    J'y pense d'un coup (mais n'allons pas si loin) : les rôles peuvent etre double : un Manager est l'executant de son PDG,
    l'Employé est donneur d'ordre d'un prestaire extérieur.

    Citation Envoyé par tristan_m Voir le message
    Après, comme le fait remarquer Luckyluke34, il peut y avoir plusieurs classes pour désigner les différents types de collaborateurs, mais par contre j'éviterai d'avoir des classes 'Manager' et 'Employee' qui sont des notions extérieures à l'application.
    Personnellement, dans ce cas précis, je confirme mon choix pour une seule classe
    • Pour les raisons que j'ai invoquées dans mon message précédent
    • Parce que bien sûr, pour moi les tâches ne sont pas manipulées par des 'entity' dans le modèle
    • Parce que que mes 'entity' à moi sont "bêtes" .

    Tu verrais plutôt des classes "DonneurDordre" et Executant" ? (dans le cas où l'appli prévoit pas un Model récursif)

    Si j'ai bien capté ton approche (qui se justifie tout a fait) :
    • les taches (todos?) sont manipulées par le Ctrl (changement d'etat de l'objet + update)
    • tes entités sont "betes" au sens où c'est que des DTO, sans aucun méthode.

    [/QUOTE]

  11. #11
    Membre habitué
    Profil pro
    Inscrit en
    Août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 89
    Points : 170
    Points
    170
    Par défaut
    Bonjour,

    Citation Envoyé par elderion
    Donc si j'ai bien compris ta vision :
    - Les objets du Model sont simplement des transporteurs de données (DTO)
    ce sotn des objets a l'image des rows en base de données. Il n'otn aucun intelligence
    - Les controllers renferment la logique métier, l'intelligence, le focntionnement de l'appli (Domain / Business)
    - il n'y a pas de Business Entity.
    Ca ressemble à ça en ce qui concerne cette application, mais de façon générale ce n'est pas tout à fait exact.
    Je ne connais pas trop les significations de business, domain et business entity, donc ça n'aide pas
    Par environnement j'entends les règles imposées à l'application ainsi que leur limite. Dans un jeu de plateforme, ce serait par exemple la physique
    Donc le modèle ne possède pas que des objets "bêtes", mais la logique applicative est dans le contrôleur.
    Tout ce qui est en interaction directe avec l'utilisateur est par contre dirigé par le contrôleur. Dans l'exemple du jeu de plateforme, le personnage joué est dirigé par le contrôleur.
    Après il y a toujours une frontière entre ce qui fait partie de l'environnement et ce qui fait partie de l'application, c'est un choix d'implémentation.
    Tout ce qui est score peut être implémenté dans le modèle ou le contrôleur ou les deux : Tel type d'ennemi vaut 100 points (modèle), éliminer 2 ennemis en moins de 10 secondes = 2*100 + bonus (contrôleur)

    Citation Envoyé par elderion
    Exemple simple et concret : si moi employé je veux mettre le statut "termine" a la todo n°12
    qui doit implémenter cette action ?
    Je vois ça d'une autre façon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Controller.updateTodoStatus(int userId, int idTodo){
            if (Model.updateTodo(userId, idTodo, TodoStatus.DONE)) {
                    Model.notifyTodoUpdateToCreator(idTodo);
            }
    }
    En gros pas besoin de l'id de l'employé, c'est l'id de l'utilisateur qui est utilisé (qui servira à la vérification). On peut d'ailleurs imaginer que cet id utilisateur soit une variable interne au contrôleur.
    A noter que je ne sais pas trop comment est censé fonctionner le Notifier, et il n'y a ni besoin de l'id employé ni de l'id manager, puisque les infos sont associées à la tâche elle-même.
    Egalement à noter, selon l'automatisation de la notfication souhaitée, la notification peut éventuellement être effectuée dans le modèle lorsque la tâche est mise à jour.

    Citation Envoyé par elderion
    Ca serait pas au Controller de consulter l'ACL pour savoir s'il peut ou non délencher l'action sur l'entité ?
    Apres tout "le Controller contrôle".
    Ca me parait dangereux que la demande d'action arrive jusqu'au Model sans verification en amont.
    Moi justement, je trouve plus rassurant de tout vérifier le plus tardivement possible, lorsque tous les éventuels traitements sont faits, que de vérifier, puis passer par plusieurs opérations pas forcément maîtrisées.
    Ca fait gagner en temps de traitement lorsque l'accès est refusé, mais pas en sécurité.
    A mon avis, il est plus sûr de diminuer au maximum le nombre d'opérations faites entre la vérification et la réalisation concrète des modifications.
    Après comme je disais dans mon messages précédent, rien n'empêche de faire une première passe dans le contrôleur.

    Citation Envoyé par elderion
    Que verrais tu comme ACL ?
    - "un Manager ne peut pas modifier l'etat d'une todo" ?
    - "un Employé ne peut pas reassigner une todo a quelqu'un d'autres"
    Comment vois tu ca coté code : implémentation, endroit d'appel
    Pareil, je ne suis pas très à l'aise avec la notion d'ACL, j'ai juste vu que ça parlait de droits d'accès
    En gros la seule règle que je vois c'est "un employé ne peut pas créer des todos"
    Citation Envoyé par elderion
    mais d'apres ce qu tu dis, ca reveindrait a:
    [...]
    sauf que la on appelle jamais l'objet d'ACL, l'assertion est en dur dans la fonction.
    Oui je voyais ça comme ça en effet .
    Pour tout ce qui est de vérifier qu'un collaborateur n'ait accès qu'aux todos qui le concernent, je ne vois pas de règle qui se passerait des ids indiquées dans les tâches.

    Citation Envoyé par elderion
    Tu verrais plutôt des classes "DonneurDordre" et Executant" ? (dans le cas où l'appli prévoit pas un Model récursif)
    Oui c'est l'idée, bien qu'en l'occurence, s'il y a vraiment beaucoup de code différent, j'envisagerai plus des classes polymorphes, chacune implémentant les traitements liés à l'un des états de l'attribut 'canCreateTodo'.
    Ca permettra d'ajouter à l'avenir d'autres attributs à la classe 'Collaborateur' sans avoir une explosion combinatoire sur le nombre de type dérivé de collaborateur.

    Citation Envoyé par elderion
    Si j'ai bien capté ton approche (qui se justifie tout a fait) :

    les taches (todos?) sont manipulées par le Ctrl (changement d'etat de l'objet + update)
    tes entités sont "betes" au sens où c'est que des DTO, sans aucun méthode.
    C'est ça, (enfin les entités ont quand même des accesseurs )

  12. #12
    Membre émérite
    Inscrit en
    Janvier 2011
    Messages
    805
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Janvier 2011
    Messages : 805
    Points : 2 918
    Points
    2 918
    Par défaut
    Citation Envoyé par elderion Voir le message
    Ma 1re question est :
    comment recupérer un Collaborateur dans la BDD et en faire un objet Employé ou Manager
    manipulable comme tel par le Controller ?
    Il existe plusieurs stratégies de mapping d'une hiérarchie de classes vers la base de données. Celle que tu mentionnes est Single Table Inheritance mais il en existe d'autres.

    Une fois la stratégie définie, il suffit de récupérer les données de la base et d'instancier un objet du bon sous-type (pas forcément utile d'instancier un objet du type de la superclasse pour ensuite le caster). A noter que les frameworks d'ORM le font automatiquement pour toi.

    Pour moi, ça devrait être le Repository le garant du type des objets retourné. Si on lui demande des Collaborateurs, il retourne une liste de Collaborateurs. Idem avec Employé et Manager. Le consommateur du Repository devrait savoir de quel type d'objet il a besoin et s'y tenir (est-ce que ça a un sens que le contrôleur qui affiche une liste de collaborateurs puisse caster un de ses collaborateurs en Boss ? Je ne crois pas...)

    Citation Envoyé par elderion Voir le message
    * le rapport controlleur/repository me parait plus ambigu :

    si le Controller peut bosser avec TodosRepository alors il sait faire le boulot de l'entité
    - l'entité ne sert plus a rien sauf a transporter ses données,
    - le Controlleur devient métier, il se rend service tout seul.

    Probleme :
    si le Controlleur veut recuperer un entité, il ne peut pas, il n'a pas accès aux *Repository
    Peut etre alors faut il faire selon les besoins :
    - cacher TodosRepository au yeux du Controlleur, ne l'exposer qu'aux l'entités qui sont les seules a en avoir besoin
    - exposer CollabsRepository dont se sert le Controlleur pour les récupérer.
    Tu pointes du doigt un réel problème, que Domain Driven Design dont je parlais plus haut a résolu. Dans DDD, seules les racines d'agrégats (les objets qui sont à la tête d'une grappe d'objets satellites) ont leurs Repository. Donc :

    • Les seules entités métier dont les Contrôleurs (ou tout autre objet) peuvent obtenir une référence directe sont les racines d'agrégat. Toutes les autres entités ne sont obtenables qu'en "traversant" une racine d'agrégat.
    • La tentation de faire faire par les Contrôleurs le boulot des entités est amoindrie puisqu'ils n'ont qu'un accès très limité à ces entités et aux données qu'elles contiennent.
    • Dans DDD, il y a une règle claire qui est que les racines d'agrégat doivent assurer les invariants (= règles de validation métier) portant sur les entités de leur agrégat. Ce comportement ne peut donc pas se retrouver dans les contrôleurs.


    Citation Envoyé par elderion Voir le message
    Ca veut dire que le jour où je dois changer le nom de la classe ou changer de moteur de requetage, je suis bon pour refactoriser
    tous les appels dans toute l'appli ?
    Heu oui, enfin c'est aussi valable si tu fais des appels directs sans injection de dépendance

    Citation Envoyé par elderion Voir le message
    Moi je suis le Controlleur ,
    Mon job c'est de travailler avec les data et les services proposés par l'Entité.
    c'est pas mon job de dire a l'Entité a quel moteur de requetage il doit s'adresser et comment le manipuler.
    Je me fiche de sa tamboulle interne, comment il travaill et avec quel intermediaire, etc...
    (Loi de Demeter)
    Par ailleurs s'il faut fournir le repository c'est pas a l'entité qu'il faut le founir, mais a
    l'objet qui régit les entités et c'est la a mon avis que l'injection de dépendances opère.
    je me trompe ?
    Non, tu as tout à fait raison. C'est pour ça qu'on délègue en général la liaison des objets entre eux à une Composition Root dont c'est la responsabilité. C'est valable avec l'injection par constructeur ou par propriété mais je crois que certains frameworks d'injection de dépendance peuvent aussi le faire avec l'injection par méthode.

    Citation Envoyé par elderion Voir le message
    Donc en DDD ya plus de Repository, l'agregat se débrouille tout seul grace aux graphes ?
    Si, il y a un Repository par racine d'agrégat. Les racines d'agrégat sont les quelques entités principales d'une application vers lesquelles on a un point d'entrée direct via leurs Repositories. Le reste des entités métier est masqué à l'intérieur des agrégats.

    Citation Envoyé par elderion Voir le message
    Par contre ca pose la question du lazy loading : si on veut que l'employé, sans ses todos :
    peut on recuperer que l'employé en base, c a dire une portion seulement du graphe ?
    Oui, en faisant du lazy loading à la main ou en se reposant sur celui de l'ORM.

  13. #13
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par tristan_m Voir le message
    Bonjour,

    Ca ressemble à ça en ce qui concerne cette application, mais de façon générale ce n'est pas tout à fait exact.
    Je ne connais pas trop les significations de business, domain et business entity, donc ça n'aide pas
    Par environnement j'entends les règles imposées à l'application ainsi que leur limite. Dans un jeu de plateforme, ce serait par exemple la physique
    Donc le modèle ne possède pas que des objets "bêtes", mais la logique applicative est dans le contrôleur.
    Tout ce qui est en interaction directe avec l'utilisateur est par contre dirigé par le contrôleur. Dans l'exemple du jeu de plateforme, le personnage joué est dirigé par le contrôleur.
    Après il y a toujours une frontière entre ce qui fait partie de l'environnement et ce qui fait partie de l'application, c'est un choix d'implémentation.
    Tout ce qui est score peut être implémenté dans le modèle ou le contrôleur ou les deux : Tel type d'ennemi vaut 100 points (modèle), éliminer 2 ennemis en moins de 10 secondes = 2*100 + bonus (contrôleur)
    ok pour toi
    - environnement (Model)= l'espace vivant et ses lois physiques (contraintes, regles de gestion)
    - logique applicative (Controllers) = gère le comportement des elements mobile dans cet espace, en fonction de ces contraintes physiques

    Globalement je suis d'accord

    Maintenant revenons au jeu de plateformes en tant qu'exemple d'"Entité intelligente"
    le personnage - on va dire le sujet - doit se déplacer :
    les controllers savent comment déplacer une plateforme en l'air tout autant que faire bouger/sauter le heros ?
    ca me parait plus logique d'avoir des classes polymorphes
    et que le controller demande a l'Entité de satisfaire sa demande "heros : bouge stp!"

    Je prend maintenant un contre-exemple, celui de l'"Entité inerte" :
    un spectacle de marionnettes :
    la marionnette a des membres et tout un tas de fils avec des combinaisons pour le faire bouger
    mais elle est inerte.
    C'est le marionnettiste (Controller) qui la manipule, car il sait le faire, c'est son métier.

    Je crois que ce qui me gene le plus avec les entités inertes,
    c'est que je vois où est le paradigme POO, là où chacun devrait avoir ses compétences propres.

    Un article qui confirme mon approche
    (pas dans le sens où la tienne est fausse, mais qui verifie au moins que je n'affirme pas n'importe quoi )
    I have seen several demos and examples on presentations where the Controllers are more or less used as a Business logic layer and based on the definition of the MVC Pattern, that is not the correct way of using the Controller. The Controller in the MVC is not a replacement of the Business Logic Layer. The Model in the MVC is the Business Logic Layer. The Controllers purpose is to be a contract between the View and the Model, and handle user actions. Think of the Controller like the Web Form's Code-behind, the logic you put in the code-behind can sort of be added to the Controller, the benefit you get by using MVC over Web Forms is that you can now with the MVC write unit test to test "complex" presentation layer code, and it's not easy to write test against the code-behind because it's event driven.
    The correct design when using the MVC would be:
    Presentation (View) -> Controller -> Business Logic (Model) -> Data source
    article a propos de Business logic
    Selon cet article mes entiés dites "intelligentes" sont des Business Entities. Ils possèdent les méthodes métier.
    De ce que je comprend, les "Business Process Objects" quant a eux, sont des workflows (des "super Entites")
    qui gère des taches plus importantes.
    Pour cela ils impliquent plusieurs Business Entities qui ensemble permettent d'effectuer cette tache.
    Je pense d'ailleurs que ces BPO utilisent le pattern Facade pour masquer la complexité de leur travail
    et faciliter la lecture.

    Mais Graffito le dit mieux que moi

    Pour résumer grossierement :
    tu es marionnette et moi plateforme.

    Citation Envoyé par tristan_m Voir le message
    Je vois ça d'une autre façon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Controller.updateTodoStatus(int userId, int idTodo){
            if (Model.updateTodo(userId, idTodo, TodoStatus.DONE)) {
                    Model.notifyTodoUpdateToCreator(idTodo);
            }
    }

    Ce qui me plait pas dans cette approche :
    - une classe unique Model qui sert de "fourre-tout" pour toutes les méthodes métier
    - le développeur manque de guidage,
    - Model est trop généraliste pour pouvoir updater une Todo + notifier un tiers . Ce n'est pas une Todo
    - l'appel a updateTodo() est toujours fait,
    on ne decide qu'en tout bout de chaine (a l'intéieur de Model.updateTodo) de continuer ou pas en fonction de l'ACL.
    • illustration #1 :
      "avant de verifier que l'employé de banque a le droit de saisir le code secret sur la porte blindée du coffre
      pour pouvoir l'ouvrir, il faut d'abord verifier que celui-ci a le droit d'acceder a la salle du coffre."
      -> On parlera meme pas d'ailleurs de verifier s'il a le droit de connaitre le code secret ou pas : la connaissance ne justifie pas le droit.
    • illustration #2:
      "c'est pas parce que je connais une URL particuliere d'une webapp que j'ai le droit d'y acceder."


    En revanche la méthode notifyTodoUpdateToCreator() est intéressante
    car tu passe l'ID de la Todo au lieu de l'objet Manager.
    Cependant :
    la "Loi de Demeter" précise qu'un objet ne doit travailler qu'avec son voisin le plus proche
    jamais avec le voisin du voisin.
    Ainsi il faudrait que cette méthode appartienne a la classe Todo (qui connait son createur)
    car Model n'est pas une Todo, donc elle n'a aucun lien avec le createur.

    Concernant la question de la responsabilité :
    l'objet est il chargé de 1 ou de 2 responsabilités ?
    a mon avis yen a que 1 car je me focalise sur l'action, pas aux operations qui permettent de la realiser.
    - action = on veut changer le status de l'objet
    - operations : changer le status + notifier le chagement aupres du tiers (cf : la notion de AOP)

    illustration de la logique de la séquence (simpliste certes): "
    quand une personne dépose une lettre (interaction) a la Poste (application web)
    le responsable de plateforme (controller) charge le préposé (objet dans le Model)
    d'effectue l'envoi (méthode métier) :
    mettre un coup le tampon + mettre la lettre dans le panier de distribution
    "
    Tout dépend ce que l'on désigne comme responsabilité.
    Pour moi les 2 ne forment qu'une seule et meme action. Si on supprimait la notification
    l'action ne serait que partiellement réalisée.

    Par contre pour notifier, Todo doit demander a l'objet Notifier de le faire pour lui.
    D'abord parce que c'est pas son job, ca lui donnera une responsabilé supplémentaire. Yaurait mélange des genres.
    Ensuite parce que Notifier connait l'entité Createur car il doit connaitre le mode de notification qu'il souhaite.
    (d'ailleurs il pourrait n'avoir qu'un accès tres réduit aux data du createur)
    il est donc le seul habilité a le notifier.

    Techniquement pour notifier, soit la Todo invoque le Notifier directement
    soit elle implémente le pattern Observable : attacher le createur comme objet observable
    et déclencher une méthode createur.xxx() lors de l'evenement "status update".

    [quote=tristan_m;6854838]
    En gros pas besoin de l'id de l'employé, c'est l'id de l'utilisateur qui est utilisé (qui servira à la vérification). On peut d'ailleurs imaginer que cet id utilisateur soit une variable interne au contrôleur.
    Citation Envoyé par tristan_m Voir le message
    plutôt l'ID de l'utilisateur en session. les valeurs en dur dans le Controller, mmmh non.
    Citation Envoyé par tristan_m Voir le message
    A noter que je ne sais pas trop comment est censé fonctionner le Notifier, et il n'y a ni besoin de l'id employé ni de l'id manager, puisque les infos sont associées à la tâche elle-même.
    cf : explication ci-dessus.
    Le Notifier sait :
    - qui il doit notifier (peut etre que l'objet doit implémenter une interface "Notifiable"),
    - la nature de la notification ("todos status update")
    - le mode de notification (mail, SMS...)
    - realiser l'operation technique (faire appel a une fabrique d'adaptateurs suivant le mode, ...)

    Citation Envoyé par tristan_m Voir le message
    Egalement à noter, selon l'automatisation de la notfication souhaitée, la notification peut éventuellement être effectuée dans le modèle lorsque la tâche est mise à jour.
    comme je disias plus haut, le pattern Observable définit dans la classe Todo.
    Par contre je sais pas si utiliser un addEventListener(event, object, params)
    est une vraie facon d'implémenter le pattern Observable...

    Citation Envoyé par tristan_m Voir le message
    Moi justement, je trouve plus rassurant de tout vérifier le plus tardivement possible, lorsque tous les éventuels traitements sont faits, que de vérifier, puis passer par plusieurs opérations pas forcément maîtrisées.
    Ca fait gagner en temps de traitement lorsque l'accès est refusé, mais pas en sécurité.
    A mon avis, il est plus sûr de diminuer au maximum le nombre d'opérations faites entre la vérification et la réalisation concrète des modifications.
    Après comme je disais dans mon messages précédent, rien n'empêche de faire une première passe dans le contrôleur.
    cf : mon illustration sur le coffre de la banque
    Je fonctionne a l'inverse : mettre l'accent sur la sécurité quite a pecher sur les performances.
    je prefere que mon Gmail soit plus lent mais plus sûr.

    Citation Envoyé par tristan_m Voir le message
    Pareil, je ne suis pas très à l'aise avec la notion d'ACL, j'ai juste vu que ça parlait de droits d'accès
    En gros la seule règle que je vois c'est "un employé ne peut pas créer des todos"
    Oui je voyais ça comme ça en effet .
    Pour tout ce qui est de vérifier qu'un collaborateur n'ait accès qu'aux todos qui le concernent, je ne vois pas de règle qui se passerait des ids indiquées dans les tâches.
    ouais non mais en fait les exemple d'ACL ca s'ecarte un peu du sujet

    Citation Envoyé par tristan_m Voir le message
    Oui c'est l'idée, bien qu'en l'occurence, s'il y a vraiment beaucoup de code différent, j'envisagerai plus des classes polymorphes, chacune implémentant les traitements liés à l'un des états de l'attribut 'canCreateTodo'.
    Ca permettra d'ajouter à l'avenir d'autres attributs à la classe 'Collaborateur' sans avoir une explosion combinatoire sur le nombre de type dérivé de collaborateur.
    "canCreateTodo" serait une méthode de nature ACL qui appartient au Collaborateur.
    je suis d'accord que cette méthode renvoit un boolen et soit attaché au Collaborateur.
    Je suis d'accord aussi que cette entité fournit les regles.
    Mais je suis pas d'accord qu'elle effectue elle meme la verification.
    Paske techniquement ca induit qu'il n'y a de moteur qui régit les ACL.
    Exemple avce un moteur de graphiques : le client lui fournit ses valeurs, mais c'est bien le moteur
    qui va exploiter ces elements pour produire un diagramme, un camembert,...

    Citation Envoyé par tristan_m Voir le message
    C'est ça, (enfin les entités ont quand même des accesseurs )
    soit, mais les accesseurs (getters et setter) relèvent de l'encapsulation, c'est pas du comportement

  14. #14
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut
    Citation Envoyé par Luckyluke34 Voir le message
    Il existe plusieurs stratégies de mapping d'une hiérarchie de classes vers la base de données. Celle que tu mentionnes est Single Table Inheritance mais il en existe d'autres.
    yes ca me donne des bonnes pistes de lecture. merci

    Citation Envoyé par Luckyluke34 Voir le message
    Une fois la stratégie définie, il suffit de récupérer les données de la base et d'instancier un objet du bon sous-type (pas forcément utile d'instancier un objet du type de la superclasse pour ensuite le caster). A noter que les frameworks d'ORM le font automatiquement pour toi.
    Oui Strétégie est bien le mot.
    D'un côté j'ai l'impression de chercher midi a 14h, d'aller trop loin par rapport a ce que mon appli de todo exige,
    mais d'un autre je sens que c'est une bonne chose au contraire de creuser les sujets du DDD, les agregats, graphes d'objet, IoD, tout ca.

    Citation Envoyé par Luckyluke34 Voir le message
    Pour moi, ça devrait être le Repository le garant du type des objets retourné. Si on lui demande des Collaborateurs, il retourne une liste de Collaborateurs. Idem avec Employé et Manager. Le consommateur du Repository devrait savoir de quel type d'objet il a besoin et s'y tenir (est-ce que ça a un sens que le contrôleur qui affiche une liste de collaborateurs puisse caster un de ses collaborateurs en Boss ? Je ne crois pas...)
    J'etais pas du tout sur de moi sur ce coup ala, yavait un truc qui me perturbait
    Oui tu as raison ca serait un non-sens qu'un Repository renvoit un type mixed
    et aussi une grosse source de bugs.
    Renvoyer toujours le meme type de données est une garantie qu'il faut tout le temps apporter.
    Et peu importe qu'on travaille sur un langage faiblement ou fortement typé
    Le langage doit etre au service d'un doc de demandes fonctionnelles, pas l'inverse.

    Citation Envoyé par Luckyluke34 Voir le message
    Tu pointes du doigt un réel problème, que Domain Driven Design dont je parlais plus haut a résolu. Dans DDD, seules les racines d'agrégats (les objets qui sont à la tête d'une grappe d'objets satellites) ont leurs Repository. Donc :

    • Les seules entités métier dont les Contrôleurs (ou tout autre objet) peuvent obtenir une référence directe sont les racines d'agrégat. Toutes les autres entités ne sont obtenables qu'en "traversant" une racine d'agrégat.
    • La tentation de faire faire par les Contrôleurs le boulot des entités est amoindrie puisqu'ils n'ont qu'un accès très limité à ces entités et aux données qu'elles contiennent.
    • Dans DDD, il y a une règle claire qui est que les racines d'agrégat doivent assurer les invariants (= règles de validation métier) portant sur les entités de leur agrégat. Ce comportement ne peut donc pas se retrouver dans les contrôleurs.

    point n°1 : donc si un Controlleur doit modifier le statut d'une Todo : il dit transverser l'objet employé
    pour accedra la Todo et la modifier.
    Mais j'hesite sur la maniere de faier dans le Controller
    • Todo todo = employe.todos(12);
      todo.status = TodoStatus.DONE;
      TodosRepository.update(todo);

      -> le Ctrl accede Todo et change sa valeur : il effectue l'action métier
      [pas bon : violation lio de Demeter]
    • employe.changeTodoStatus(todo, TodoStatus.DONE)

      -> il indique a employé l'objet Todo a modifier, il lui delegue la tache
      [bon : pas d'action métier dans le Ctrl]
    • employe.changeTodoStatus(12, TodoStatus.DONE)

      -> pareil que celle d'avant sauf que le Ctrl passe l'ID de la Todo au lieu de la Todo elle.
      Ca revient au meme.
      Ya juste besoin d'une resolution ID -> objet dans changeTodoStatus()

      Inconvénient :
      - ca demande de créer autant de wrapper dans l'entité qu'il ya d'actions possibles dans Todo
      qui concernent Employé.
      Avantage :
      - les wrappers permettent de restreindre le scope aux seules actions que la classe Employé peut effectuer avec la classe Todo.

    [*] employe.todos(12).changeStatus(TodoStatus.DONE);
    -> et celle ci.
    Le Ctrl recoit la Todo via .todos(12), la méthode changeStatus
    est juste un alias du setter de status
    (pas bon : violation de la Loi de Demeter)


    point n°2
    complement amoindrie en fait car Ctrl se limite a appeler les méthodes métier des entités.

    point n°3
    donc le moteur ACL intervient au sein des racines d'agrégats.
    C'est l'agregat+ACL qui autorisent entité-A a voir, interagir avec entité-B, C, D...
    ainsi que de charger 1 ou plusieurs collections d'elements rattaché a l'entité A.

    Citation Envoyé par Luckyluke34 Voir le message
    Non, tu as tout à fait raison. C'est pour ça qu'on délègue en général la liaison des objets entre eux à une Composition Root dont c'est la responsabilité. C'est valable avec l'injection par constructeur ou par propriété mais je crois que certains frameworks d'injection de dépendance peuvent aussi le faire avec l'injection par méthode.
    si c'est a l'initiative du framework, alors les classes métie devront adapter la signature de leurs méthodes.
    A 1re vue ca me parait plus logique de faire de l'injection dans le constructeur,
    car d'une methode sur l'autre, elle utiliseront le meme objet injecté.
    Maintenant imaginons que pour une meme classe:
    - une méthode A va chercher des infos sur une base MySql localhost,
    - une méthode B sur un Oracle sur un autre serveur du réseau local (SGBD attaché a un progiciel)
    - une méthode C sur un service web distant.
    La en effet on va etre obligé de faire de l'injection par méthode, car chacune utilisera un Adaptateur différents pour s'interfacer.
    A moins de crer un objet Registre passé au constructeur, dans lequel on inscrit les différents Adaptateurs pour prévoir chaque cas.

    Citation Envoyé par Luckyluke34 Voir le message
    Si, il y a un Repository par racine d'agrégat. Les racines d'agrégat sont les quelques entités principales d'une application vers lesquelles on a un point d'entrée direct via leurs Repositories. Le reste des entités métier est masqué à l'intérieur des agrégats.
    Si j'ai bien suivi :
    - Collaborateur (Employé, Manager, etc..) est l'entité principale, active, vivante.
    - les Todos ne sont pas des entités principales, donc ni point d'entrée, ni Repository, ni racine d'agrégat.
    Ils sont par contre agrégés par la racine d'agrégat par le lien de rattachement "créé par" ou le "assigné à".
    On doit forcement connaitre l'entité vivante et passer par son seul intermediaire pour effectuer des actions sur les Todos.

    Citation Envoyé par Luckyluke34 Voir le message
    Oui, en faisant du lazy loading à la main ou en se reposant sur celui de l'ORM.
    ah ok un ORM intègre un systeme pour le LazyLoading.
    Je vais creuser le sujet pour voir comment ca marche

    merci pour ces infos et conseils
    Ca va me faire bien avancer.
    Ca devient très très intéressant.

    c'est peut etre que anecdotique mais...
    Ce que je trouve étrange/dommage c'est que j'ai jamais ce genre de discussions avec mon équipe de développeurs PHP
    A contratio il semble la plupart des personnes sur ce forum qui évoquent ces sujets d'architecture sont plutôt des développeurs Java.
    Question d'état d'esprit, de philosophie, de vision des choses différents, d'une techno a l'autre ?

  15. #15
    Membre habitué
    Profil pro
    Inscrit en
    Août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 89
    Points : 170
    Points
    170
    Par défaut
    Bonjour,

    Une réponse rapide pour dire un grand MERCI, pour toutes ces infos et les efforts pour fournir des liens et des exemples pertinents.

    J'aurais bien des points à préciser et argumenter, mais je m'aperçois que cette double discussion nuit à la lisibilité du sujet et que "ma partie" devient un peu hors-sujet. Elle ressemble maintenant plus à du support du fait que je ne sois pas très familier avec plusieurs notions abordées ici.

    Malgré tout l'intérêt que je porte à ce sujet, je vais donc me retirer.
    Merci encore à vous deux

  16. #16
    Membre émérite
    Inscrit en
    Janvier 2011
    Messages
    805
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Janvier 2011
    Messages : 805
    Points : 2 918
    Points
    2 918
    Par défaut
    Citation Envoyé par elderion Voir le message
    Mais j'hesite sur la maniere de faier dans le Controller
    • Todo todo = employe.todos(12);
      todo.status = TodoStatus.DONE;
      TodosRepository.update(todo);

      -> le Ctrl accede Todo et change sa valeur : il effectue l'action métier
      [pas bon : violation lio de Demeter]
    • employe.changeTodoStatus(todo, TodoStatus.DONE)

      -> il indique a employé l'objet Todo a modifier, il lui delegue la tache
      [bon : pas d'action métier dans le Ctrl]
    • employe.changeTodoStatus(12, TodoStatus.DONE)

      -> pareil que celle d'avant sauf que le Ctrl passe l'ID de la Todo au lieu de la Todo elle.
      Ca revient au meme.
      Ya juste besoin d'une resolution ID -> objet dans changeTodoStatus()

      Inconvénient :
      - ca demande de créer autant de wrapper dans l'entité qu'il ya d'actions possibles dans Todo
      qui concernent Employé.
      Avantage :
      - les wrappers permettent de restreindre le scope aux seules actions que la classe Employé peut effectuer avec la classe Todo.

    [*] employe.todos(12).changeStatus(TodoStatus.DONE);
    -> et celle ci.
    Le Ctrl recoit la Todo via .todos(12), la méthode changeStatus
    est juste un alias du setter de status
    (pas bon : violation de la Loi de Demeter)
    Oui, c'est exactement les 2 alternatives que tu as en DDD :

    • Une option stricte où l'unique interlocuteur est la racine d'agrégat (Employé) et tout passe par des méthodes situées directement sur elle : le contrôleur ne viole pas la loi de Demeter. Le problème, c'est que la racine d'agrégat va vite se retrouver surchargée de toutes les méthodes concernant toutes les entités de l'agrégat. C'est un peu trop extrémiste pour moi.


    • Une option plus souple où le contrôleur a une référence (transitoire) vers un Todo et appelle simplement les méthodes du Todo. En effet dans DDD on a le droit d'avoir une référence vers une entité non racine d'agrégat tant qu'on ne stocke pas cette référence (= la Todo ne devient pas une variable d'instance du Contrôleur). C'est ce que je préfère. La violation de Demeter n'est que légère voire inexistante si on passe au Contrôleur directement l'instance de Todo et pas l'instance d'Employé (solution que je privilégierais).


    Donc les 2 sont possibles.

    Citation Envoyé par elderion Voir le message
    donc le moteur ACL intervient au sein des racines d'agrégats.
    ACL ?

    Citation Envoyé par elderion Voir le message
    Maintenant imaginons que pour une meme classe:
    - une méthode A va chercher des infos sur une base MySql localhost,
    - une méthode B sur un Oracle sur un autre serveur du réseau local (SGBD attaché a un progiciel)
    - une méthode C sur un service web distant.
    La en effet on va etre obligé de faire de l'injection par méthode, car chacune utilisera un Adaptateur différents pour s'interfacer.
    A moins de crer un objet Registre passé au constructeur, dans lequel on inscrit les différents Adaptateurs pour prévoir chaque cas.
    Tout à fait, en fait on considère souvent que si une dépendance est utilisée seulement dans une méthode de notre classe, alors il vaut mieux la passer en paramètre de la méthode. Si elle est utilisée dans de multiples méthodes ou si on a besoin de la stocker entre deux appels de la même méthode, alors il vaut mieux l'injecter par constructeur (ou propriété).

    Citation Envoyé par elderion Voir le message
    Si j'ai bien suivi :
    - Collaborateur (Employé, Manager, etc..) est l'entité principale, active, vivante.
    - les Todos ne sont pas des entités principales, donc ni point d'entrée, ni Repository, ni racine d'agrégat.
    Ils sont par contre agrégés par la racine d'agrégat par le lien de rattachement "créé par" ou le "assigné à".
    On doit forcement connaitre l'entité vivante et passer par son seul intermediaire pour effectuer des actions sur les Todos.
    C'est exactement ça.

    Citation Envoyé par elderion Voir le message
    c'est peut etre que anecdotique mais...
    Ce que je trouve étrange/dommage c'est que j'ai jamais ce genre de discussions avec mon équipe de développeurs PHP
    A contratio il semble la plupart des personnes sur ce forum qui évoquent ces sujets d'architecture sont plutôt des développeurs Java.
    Question d'état d'esprit, de philosophie, de vision des choses différents, d'une techno a l'autre ?
    C'est peut-être usurpé mais effectivement PHP n'a pas la réputation d'être un langage dans lequel et par la communauté duquel les problématiques de POO sont traitées à leur juste importance. C'est sûrement dû au fait qu'à ses débuts, il n'était pas du tout orienté objet. Effectivement ces problématiques sont beaucoup plus connues par les développeurs Java ou C#.

  17. #17
    Membre émérite
    Inscrit en
    Janvier 2011
    Messages
    805
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Janvier 2011
    Messages : 805
    Points : 2 918
    Points
    2 918
    Par défaut
    Citation Envoyé par tristan_m Voir le message
    J'aurais bien des points à préciser et argumenter, mais je m'aperçois que cette double discussion nuit à la lisibilité du sujet et que "ma partie" devient un peu hors-sujet. Elle ressemble maintenant plus à du support du fait que je ne sois pas très familier avec plusieurs notions abordées ici.

    Malgré tout l'intérêt que je porte à ce sujet, je vais donc me retirer.
    Merci encore à vous deux
    Pas de souci, je précise que ce que je dis n'est qu'une approche parmi d'autres, en orienté objet comme partout il n'y a pas de vérité mais plusieurs visions des choses

  18. #18
    Membre régulier
    Profil pro
    Inscrit en
    Février 2005
    Messages
    87
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 87
    Points : 95
    Points
    95
    Par défaut
    Exactement ya des tas de point de vue pour un meme objectif, tout aussi valables les uns que les autres.
    En général on se pose plein de questions pour mieux cerner quelle approche, quelle stratégie il vaut mieux adopter et auss lequelles il faudra écarter.
    C'est pour ca que c'est tres intéressant de comparer les points de vue
    Maintenant c'est vrai que ca peut sembler étonnant qu'un sujet prétendument aussi simple qu'une Todolist amène à autant d'échanges.
    Mais c'est comme les icebergs et puis ca permet d'avancer et et de casser automatismes et hautes certitudes.

    La stratégie universelle n'existe pas...enfin sauf dans le cas suivant :
    Exemple : ya une stratégie built-in dans certains frameworks MVC pour monter une petite appli CRUD en quelques lignes de commandes.
    Cela reste vrai pour ce qui concerne la partie automatisable de l'ossature (MDA).
    En revanche sur l'autre partie, c'est a dire l'implémentation métier, là evidemment le framework ne peut rien deviner.

Discussions similaires

  1. [2.x] Relation entité et/ou design pattern
    Par deezpowned dans le forum Symfony
    Réponses: 7
    Dernier message: 12/12/2014, 15h49
  2. design, objet php
    Par N.OTHMANE dans le forum Bibliothèques et frameworks
    Réponses: 2
    Dernier message: 09/08/2008, 00h48
  3. Réponses: 2
    Dernier message: 27/02/2008, 17h22
  4. [Design/ID] Relation de dépendance entre composants
    Par ze_corsaire dans le forum UML
    Réponses: 12
    Dernier message: 17/01/2008, 21h42
  5. Réponses: 1
    Dernier message: 13/02/2006, 03h13

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