Au-delà du MVC : questionner un choix d'architecture que nous ne faisons plus
- Un pattern né pour les interfaces graphiques
- Une couche parmi d'autres
- Un mariage à sens unique
- Le défaut, amplifié par les agents
- Un éventail de possibilités
- Conclusion
Créez un nouveau projet Laravel : avant même la première ligne de code, votre application possède déjà un dossier pour ses controllers, un autre pour ses models, un autre encore pour ses vues ... et donc une architecture.
Mettre en place une architecture hexagonale, du CQRS ou du DDD nous paraît naturellement mériter une réflexion approfondie, le Modèle-Vue-Contrôleur échappe pourtant à cette exigence : il s'impose par défaut ou, disons-le autrement, par habitude.
Durant ma carrière, j'ai eu l'occasion de côtoyer une bonne centaine d'applications PHP en production, principalement en Laravel.
La quasi-totalité de ces applications se contentait de suivre l'organisation proposée par le framework : un dossier Controller, un dossier Model, un dossier Job ...
Toutes, sans exception, ont fini tôt ou tard à poser des problèmes de maintenabilité : un code qui se spaghettise, des temps de développement anormalement longs et des régressions en cascade.
La première observation n'explique pas à elle seule la seconde, mais une question mérite d'être posée : avant de créer votre premier controller, avez-vous réellement choisi votre architecture ?
Un pattern né pour les interfaces graphiques
Vous utilisez probablement le MVC chaque jour, pourtant ce pattern est bien plus ancien que le web.
Trygve Reenskaug le formula à la fin des années 70, alors qu'il était chercheur invité au Xerox PARC : il en réalisa la première implémentation en Smalltalk et rédigea les deux notes qui le définissent, datées de mai et de décembre 1979.

La note de présentation des rapports originaux du MVC, rédigée par Trygve Reenskaug en 2007 et archivée par l'Université d'Oslo
Son objectif était de donner à l'utilisateur le contrôle de ses informations, vues sous plusieurs angles, à travers une interface graphique.
Chaque élément de l'écran disposait alors de son propre trio : un modèle, une vue qui l'affiche et un controller qui réagit à la souris et au clavier.
Le pattern ne fut transposé au web que bien plus tard, avec WebObjects de NeXT en 1996 puis le "Model 2" des applications Java, avant de s'installer durablement dans le paysage PHP avec CakePHP, Zend Framework (devenu Laminas), Symfony et bien évidemment Laravel.
Du Smalltalk au web : un pattern pensé pour l'écran, adapté à la requête HTTP
Cette évolution a laissé des traces : une requête HTTP n'a pas grand-chose en commun avec un clic de souris, et le Model de nos frameworks, intimement lié à une table de base de données, s'éloigne fortement du Model d'origine, qui représentait le métier sans aucune notion de persistance.
À l'origine comme aujourd'hui, le MVC organise l'interaction entre un utilisateur et une application : il ne dit rien de ce que fait l'application.
Nous avons détaillé cette différence dans le chapitre « Clean Archi VS MVC » de notre article sur la Clean Architecture.
Une couche parmi d'autres
En 2011, lors de sa conférence "Architecture: The Lost Years" à la Ruby Midwest, Robert C. Martin interrogeait son auditoire sur l'architecture de leurs applications.
La réponse type, un framework accompagné d'une librairie d'authentification et d'une base de données, décrivait à ses yeux une boîte à outils, sans rien dire de l'application elle-même : le MVC y était renvoyé aux interfaces graphiques, et le web à un simple mécanisme de livraison.
Ouvrez l'arborescence d'une application Laravel classique : vous y trouverez des controllers, des models, des jobs, des events ... mais rien qui vous indique si cette application vend des livres, gère des contrats d'assurance ou corrige des examens.
Cette organisation est un découpage technique : elle range le code selon sa nature, quand une architecture le range selon les fonctionnalités qu'il sert.
À gauche, le code est rangé par nature technique. à droite, par fonctionnalité
Prenez le controller de commande que vous avez sans doute déjà croisé : il valide la requête, calcule le prix total, applique la remise et les taxes, enregistre la commande puis formate la réponse JSON.
Le MVC n'a rien à redire à ce controller, il est même parfaitement conforme au pattern.
Vous pourriez me répondre que rien n'empêche d'extraire cette logique dans des services ... et vous auriez raison, mais le MVC ne vous dira jamais où les placer, ni dans quel sens les dépendances doivent circuler entre eux.
Le MVC occupe une seule couche de votre application, celle de la présentation, et laisse tout le reste sans réponse.
Dans une architecture en couches, le MVC se cantonne à la présentation
Le MVC ne disparaît pas dans une architecture en couches : il s'y retrouve, avec ses controllers, ses vues et ses ViewModels, à la place qui a toujours été la sienne.
Un mariage à sens unique
Nous avons progressivement considéré qu'un framework était une architecture à part entière et nous avions tort : un framework est un ensemble de composants répondant à des besoins génériques, qui ne connaît rien de vos préoccupations.
La relation que vous entretenez avec lui est à sens unique : en adoptant un framework, vous vous engagez envers lui, tandis que ses créateurs ne prennent aucun engagement envers vous.
Une version majeure, un composant déprécié, une convention qui évolue ... et c'est à votre application de s'adapter, jamais l'inverse.
Longtemps, l'idée que le framework ne soit qu'un détail de l'application m'a paru saugrenue. Elle devient pourtant une évidence dès lors que votre application atteint une taille critique et accumule une grande richesse fonctionnelle.
Une fois empêtré dans un framework, il sera difficile, voire impossible, de vous en éloigner sans douleur, surtout si vos composants y sont fortement couplés.
Ce mariage est, dans une certaine mesure, inéluctable : réécrire votre propre système de routing plutôt que d'utiliser celui de Laravel serait une perte de temps, et il y a fort à parier que votre solution serait moins fonctionnelle et moins efficace.
Il doit cependant être réfléchi, car ce choix accompagnera votre application tout au long de sa vie.
Vos clients paient pour des fonctionnalités qui répondent à leurs besoins, et le nom du framework qui les fait tourner leur importe peu.
Votre logique métier est bien plus précieuse que le framework qui l'exécute.
Le défaut, amplifié par les agents
Ce choix par habitude était jusqu'ici le nôtre, il est désormais aussi celui des agents de code qui écrivent une part croissante de nos applications.
Un agent reproduit les conventions qu'il a le plus rencontrées et celles qu'il lit dans votre projet : face à une application Laravel organisée par défaut, il ajoutera un controller, un model ou un service exactement là où vous l'auriez fait vous-même.
La différence tient au rythme : un agent génère une dizaine de controllers en quelques minutes et ne remettra jamais en question une organisation qu'on ne lui a pas demandé de questionner.
La question de l'architecture, que nous nous posions déjà rarement, risque alors de ne plus se poser du tout.
À mes yeux, cette accélération rend le choix conscient plus nécessaire que jamais.
Si vous ne décrivez pas votre architecture à vos agents, ils la choisiront pour vous ... et ils choisiront le défaut.
Écrivez-la là où ils la liront : un fichier CLAUDE.md, des instructions de projet, une documentation d'architecture à la racine du dépôt. Décrivez vos modules, vos couches, le sens de vos dépendances et les règles qu'un nouveau fichier doit respecter pour trouver sa place.
Une règle écrite ne garantit rien, un agent peut l'oublier au fil d'une longue session : des outils d'analyse de dépendances comme Deptrac ou PHPArkitect, lancés en CI, vous permettront de vérifier que l'architecture décrite est bien celle du code.
Un éventail de possibilités
Le MVC n'est qu'un choix parmi d'autres, et chacune des approches suivantes répond à des enjeux différents.
Une architecture modulaire sera une première piste intéressante : elle regroupe votre code en modules fonctionnels, demande peu d'efforts et ne bouscule que peu l'organisation existante de Laravel.
Proposé par Paul M. Jones en 2014, l'Action-Domain-Responder (ADR) est un raffinement du MVC pensé pour le web : une action, dans sa propre classe, reçoit la requête et sollicite le domaine, puis un responder construit l'intégralité de la réponse HTTP.
Le controller du MVC regroupe plusieurs actions et pilote la vue, l'ADR dédie une classe à chaque action et confie la réponse au responder
Vous en faites peut-être déjà sans le savoir : les single action controllers de Laravel, avec leur unique méthode __invoke, en sont très proches, tout comme le pattern Action largement répandu dans l'écosystème.
Des architectures plus structurantes sont également envisageables, comme le CQRS, qui sépare les opérations d'écriture des opérations de lecture, ou l'architecture hexagonale et ses ports et adaptateurs, plus complexe à mettre en œuvre mais offrant une grande flexibilité.
La Clean Architecture, dont nous avons partagé un long retour d'expérience, pousse cette logique plus loin en organisant l'application en couches régies par des règles de dépendance strictes.
Enfin, si votre application développe une grande richesse fonctionnelle et se retrouve noyée sous sa logique métier, une approche Domain-Driven Design (DDD) pourrait s'avérer salvatrice.
Ne vous battez pas contre votre framework, cela en vaut rarement la peine.
Établissez toujours une limite claire entre Laravel et le cœur de votre métier.
Conclusion
Le MVC reste un choix parfaitement pertinent selon la taille de votre projet, sa complexité fonctionnelle ou tout simplement les compétences de votre équipe.
Le problème apparaît quand ce choix n'en est plus un : une architecture que personne n'a pensée finit toujours par se payer, et vous risquez de l'apprendre dans la douleur si rien n'est fait à temps.
Les agents de code ne changent rien à cette équation, ils en rapprochent seulement l'échéance.
Soyez de véritables artisans : restez maîtres de vos outils et placez l'architecture de vos projets au cœur de vos préoccupations !
« Neither architecture nor clean code insist on perfection, only on honesty and doing the best we can »
— James O. Coplien
Écrit par
Mathieu De GraciaLead & Software Architect • nublar.dev
Tu veux commenter ? Crée un compte ou connecte-toi.
A lire
Autres articles de la même catégorie
Vous écrivez pour être lu
Les bons développeurs écrivent du code que les humains peuvent comprendre.
Clean Architecture : conduire le changement au sein d'une équipe
Retour d’expérience sur la transformation d’une application en terrain d’apprentissage collectif autour de la Clean Architecture : enjeux, difficultés et enseignements.
Il y aura toujours des bugs
Quelqu'un qui pense qu'une application peut-être exempte de bugs est un fou.