Toutes les idées et tous les commentaires sont les bienvenus :)
Je rencontre un problème pour savoir comment gérer la logique commerciale autour de mon Entités de la doctrine 2 dans un grand Application Symfony2 . (Désolé pour la longueur du message)
Après avoir lu de nombreux blogs, livres de cuisine et autres ressources, je trouve que :
- Les entités peuvent être utilisées uniquement pour la persistance de la cartographie des données ("modèle anémique"),
- Les contrôleurs doivent être les plus fins possibles,
- Les modèles de domaine doivent être découplés de la couche de persistance (l'entité ne connaît pas le gestionnaire d'entité).
Ok, je suis tout à fait d'accord, mais : où et comment gérer des règles commerciales complexes sur des modèles de domaine ?
Un exemple simple
NOS MODÈLES DE DOMAINE :
- a Groupe peut utiliser Rôles
- a Rôle peut être utilisé par différents Groupes
- a Utilisateur peut appartenir à plusieurs Groupes avec de nombreux Rôles ,
Dans un SQL nous pourrions modéliser ces relations sous la forme de :
NOS RÈGLES DE GESTION SPÉCIFIQUES :
- Utilisateur peut avoir Rôles en Groupes seulement si Rôles est attaché au Groupe .
- Si nous détachons un Rôle R1 d'un Groupe G1 , tous UserRoleAffectation avec le groupe G1 et le rôle R1 doit être supprimé
Il s'agit d'un exemple très simple, mais j'aimerais connaître la (les) meilleure(s) façon(s) de gérer ces règles de gestion.
Solutions trouvées
1- Mise en œuvre dans la couche service
Utiliser une classe de service spécifique comme :
class GroupRoleAffectionService {
function linkRoleToGroup ($role, $group)
{
//...
}
function unlinkRoleToGroup ($role, $group)
{
//business logic to find all invalid UserRoleAffectation with these role and group
...
// BL to remove all found UserRoleAffectation OR to throw exception.
...
// detach role
$group->removeRole($role)
//save all handled entities;
$em->flush();
}
- (+) un service par classe / par règle de gestion
- (-) Les entités de l'API ne sont pas représentatives du domaine : il est possible d'appeler
$group->removeRole($role)
de ce service. - (-) Trop de classes de service dans une grande application ?
2 - Mise en œuvre dans les gestionnaires d'entités de domaine
Encapsuler cette logique d'entreprise dans des "gestionnaires d'entités de domaine" spécifiques, également appelés fournisseurs de modèles :
class GroupManager {
function create($name){...}
function remove($group) {...}
function store($group){...}
// ...
function linkRole($group, $role) {...}
function unlinkRoleToGroup ($group, $role)
{
// ... (as in previous service code)
}
function otherBusinessRule($params) {...}
}
- (+) toutes les règles commerciales sont centralisées
- (-) Les entités de l'API ne sont pas représentatives du domaine : il est possible d'appeler $group->removeRole($role) en dehors du service...
- (-) Les gestionnaires de domaine deviennent des gestionnaires de FAT ?
3 - Utiliser des auditeurs lorsque c'est possible
Utiliser des écouteurs d'événements symfony et/ou Doctrine :
class CheckUserRoleAffectationEventSubscriber implements EventSubscriber
{
// listen when a M2M relation between Group and Role is removed
public function getSubscribedEvents()
{
return array(
'preRemove'
);
}
public function preRemove(LifecycleEventArgs $event)
{
// BL here ...
}
4 - Mettre en œuvre des modèles riches en étendant les entités
Utiliser les entités comme sous-classe/classe parentale des classes de modèles de domaine, qui encapsulent une grande partie de la logique du domaine. Mais cette solution me semble plus confuse.
Pour vous, quelle est la meilleure façon de gérer cette logique métier, en se concentrant sur un code plus propre, découplé et testable ? Vos commentaires et bonnes pratiques ? Vous avez des exemples concrets ?
Principales ressources :
- Gestion des entités par Symfony
- Symfony2/Doctrine, devoir mettre la logique métier dans mon contrôleur ? Et dupliquer le contrôleur ?
- Extension de Doctrine Entity afin d'ajouter de la logique métier
- http://iamproblematic.com/2012/03/12/putting-your-symfony2-controllers-on-a-diet-part-2/
- http://l3l0.eu/lang/en/2012/04/anemic-domain-model-problem-in-symfony2/
- https://leanpub.com/a-year-with-symfony