Controllers et services

[toc]

J’ai entendu assez souvent :

Question :
« quand je suis sur un projet MVC server-side, le code du traitement je le met où? »
Réponse :
‘ben dans le controller »

Attention : c’est une très mauvaise pratique !

Et je ne veux pas entendre : « ba tu le met où tu veux le code, de toutes facon ca marchera »

Alors mieux que ca j’ai envie de vous expliquer pourquoi vous utilisez très mal le Controller.
Ce que nous autres vieux de plus de 30 ans qualifions d’ « usage galvaudé » d’un Controller.

Un Controlleur en vrai ça fait quoi ?

Un Controlleur a un rôle assez simple : il est une chaîne dans un workflow métier ni plus ni moins.
D’ailleurs il ne fait pas partie réellement de la couche métier. Il fait partie de la couche applicative.
Il orchestre les actions métiers à réaliser. qui elles sont encapsulées dans des services métier. Bref.

Reprenons le schema classique d’un process MVC.
1/ requête HTTP
2/ Front Controller
3/ Routage
4/ Invocation d’une méthode d’un Controller
5/ Retour d’information HTTP

Nous sommes bien d’accord qu’en gros, quand le client demande « la liste des ville de la région Aquitaine », le Controlleur a pour seule mission de les lui renvoyer (en JSON, HTML, XML…)

Nous sommes bien toujours bien d’accord que techniquement, son job à lui c’est bien de les renvoyer, PAS de faire les actions métiers qui consistent à chercher les infos en base, les filtrer, ordonner, etc…

Alors on le met où en vrai le traitement « métier » ?

Le Controlleur c’est un peu comme le chef de projet : il prend une demande client, détermine la TodoList des choses à faire en interne et fait appel pour cela à différents experts métier, chacun compétent dans sa spécialité, pour obtenir le résultat attendu.
Le Controller est donc un dispatcher  : il délègue le travail aux expert des domaines concernés.
Et c’est là qu’apparaissent les Services. Les experts métier ce sont eux !

Le service

C’est le Service qui encapsule la logique métier. C’est là dedans qu’il faut « mettre le code ».
Charge au développeur de savoir bien répartir le fameux code à exécuter.
Chaque service propose des actions qu’il est capable de réaliser dans son périmètre de compétences.
Ce n’est surtout pas un épicier qui fait un peu de tout : son rôle doit être clairement identifié et délimité.
Vous savez, le fameux S de SOLID : Single Responsibility

Un exemple parlant

Prenons un exemple très simple qui en réalité fait appel à un bon paquet de traitements métier : publier un article avec WordPress.

Ca signifie quoi concrètement « publier » ? ça sous-entend quoi ?
et bien il s’agit d’un véritable workflow à lui tout seul.
Ne soyons pas peureux, listons les :

1/ vérifier les données pour l’intégrité en base
–> Service sollicité : validation et échappement des données POST

2/ enregistrer les données en base et changer l’état du billet
–> Services sollicité : ORM

3/ générer l’URL publique
Service sollicité : générateur d’URL, URL-rewriting, URL-shortener

4/ Regénérer le sitemap pour le SEO Google
Service sollicité : générateur de sitemap XML

5/ publier la news sur Twitter, Facebook
Services sollcités : publishing sur réseaux sociaux

6/ Avertir les abonnés du site WordPress
internautes abonnés, pingback
Service sollcité : gestionnaire de Pingback

Ca en fait un paquet de service ! Et chacun a un rôle bien particulier.
On comprend alors très vite que si on mettait dans le Controller tout le code
nécessaire pour réaliser ces 6 opérations, ca serait ingérable. Il y aurait du code dans tous les sens.
On serait capable de développer, mais bien vite incapables de maintenir et de fair évoluer l’application.


Et pour peu qu’on ait besoide faire appel à une ou plusieurs de ces 6 étapes dans un contexte complètement différent, on serait obligé de faire des infâmes copier/coller, créer des conditions pour quel tel bout de code soit éxécuté ou pas.
N’oublions pas qu’un service n’a jamais (ou le moins possible) à savoir dans quel contexte il est appelé.
Ou dit autrement, le contexte d’usage ne doit pas conditionner le comportement du service.

Les conditions doivent être gérées par le controlleur. J’appelle ou j’appelle pas tel service, mais si je l’appelle je suis conscient qu’il fera toujours le même job.

Les avantages d’un service :

Les (nombreux) avantages d’un Service :

  • c’est lui qui contient le code métier (en partie)
  • on lui dit ce qu’il doit faire, pas comment le faire
  • il a une responsabilité limitée : il fait un nombre limité de choses mais les fait bien.
  • il est constant, ne se comporte pas de facon conditionnelle au contexte d’appel
  • il est utilisable partout où c’est nécessaire, invocable depuis n’importe quel Controller.
  • son implémentation est centralisée : si on veut le modifier (code, règle, librairie) tout est dedans et nulle part ailleurs. Pas besoin de propagation.
  • pas besoin de l’instancier : c’est le container de services du framework qui gère. Dès qu’on en a besoin, ya qu’à se servir.
    Merci au Lazy Loading et à la Dependency Injection (du framework) qui nous décharge de sa gestion
  • Un service est par nature instancié en tant que Singleton. Laissez le framework gérer ce pattern, il le fait mieux que vous !
Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s