Passer au contenu
DedicatedPHP Contact

Monolithe modulaire vs microservices en PHP : comment décider

Critères techniques et opérationnels pour décider de renforcer un monolithe PHP ou d’extraire des services sans transférer une complexité inutile.

Diagramme éditorial de décision entre un monolithe PHP modulaire et des services indépendants reliés par des contrats

La décision entre monolithe modulaire vs microservices en PHP ne se résout pas par le nombre de modules, l’ancienneté du code ou la popularité d’une architecture. Une application d’entreprise peut croître sainement au sein d’un unique déploiement si elle conserve des limites claires. À l’inverse, la diviser prématurément peut transformer de simples appels internes en un réseau de contrats, de files, de tentatives répétées et de problèmes de coordination.

La question utile n’est pas « devons-nous utiliser des microservices ? », mais « quelle capacité métier doit évoluer, échouer, être déployée ou montée en charge de manière indépendante, et pouvons-nous assumer le coût de son exploitation ainsi ? ». La réponse doit partir du domaine et de l’exploitation réelle, et non d’un diagramme cible.

La croissance fonctionnelle n’exige pas des services séparés

La croissance fonctionnelle n’exige pas des services séparés — guía visual de DedicatedPHP

Ajouter des intégrations, des processus asynchrones ou des domaines produit n’implique pas que chacun doive avoir son propre service. Un monolithe peut contenir des modules bien délimités, des tâches en arrière-plan, des files et des adaptateurs pour des systèmes externes sans perdre sa cohérence opérationnelle.

La première intervention consiste généralement à réduire le couplage interne. Si un module de facturation importe directement des classes de commandes, modifie leurs tables ou connaît les règles internes de stock, le problème n’est pas automatiquement résolu en déplaçant le code dans un autre dépôt. Il se transforme seulement en couplage réseau et de données.

Un monolithe modulaire vise à ce que chaque capacité possède une interface interne explicite, des dépendances unidirectionnelles et ses propres règles. En PHP, cela peut se matérialiser par des espaces de noms par domaine, des contrats d’application, des contrôleurs légers, des cas d’utilisation définis et des adaptateurs pour la persistance ou les API externes. Le fait de tout déployer ensemble reste compatible avec ces frontières.

Ce qu’il faut analyser avant de changer d’architecture

Avant de discuter de technologie, identifiez les capacités métier : par exemple, gestion des commandes, catalogue, identité, facturation, traitement documentaire ou notifications. Une capacité n’équivaut pas nécessairement à une entité ni à un écran ; elle regroupe des règles et des décisions qui devraient évoluer pour des raisons similaires.

  • Responsable : déterminez qui maintient les règles, priorise les changements et répond aux incidents.
  • Données : identifiez quelles informations chaque capacité crée et gouverne, qui peut les modifier et quelles lectures croisées elle nécessite.
  • Flux critiques : représentez le parcours d’une opération pertinente, y compris les validations, les dépendances externes et les étapes asynchrones.
  • Rythme de changement : distinguez les modifications fréquentes des changements ponctuels. La fréquence isolée ne suffit pas ; il importe de savoir si elle oblige à coordonner des équipes ou des releases.
  • Profil de charge : séparez le trafic interactif des tâches intensives en CPU, mémoire, stockage ou appels à des tiers.
  • Impact d’une défaillance : établissez ce qui se produit si une capacité se dégrade pendant quelques minutes ou quelques heures, et si le cœur de métier peut continuer à fonctionner.

Cet inventaire révèle des dépendances souvent cachées : transactions partagées, requêtes directes sur les tables d’autrui, règles dupliquées dans les contrôleurs et tâches planifiées qui mettent à jour plusieurs domaines. Les extraire sans les résoudre produit des services formellement séparés mais fonctionnellement imbriqués.

Six signaux pour conserver un monolithe modulaire

Ces signaux favorisent le renforcement de la conception interne plutôt que la distribution des responsabilités :

  1. Les changements traversent souvent plusieurs modules. Si une fonctionnalité métier requiert de modifier les commandes, les prix et la facturation de manière coordonnée, la séparation peut multiplier les déploiements et les contrats.
  2. La cohérence immédiate est centrale. Lorsqu’une opération nécessite une unique transaction de base de données pour préserver des invariants critiques, une frontière distribuée ajoute des décisions complexes de compensation.
  3. L’équipe est petite ou partage la responsabilité. Plusieurs services exigent une discipline d’exploitation, des astreintes, des pipelines, des versions et un diagnostic pour chaque unité.
  4. La charge évolue de manière similaire. Si les composants croissent au même rythme et qu’il n’existe pas de goulot d’étranglement isolable, la séparation n’apporte pas d’avantage clair.
  5. Les frontières de domaine sont encore instables. Extraire une capacité alors que ses règles, son vocabulaire et ses responsabilités changent constamment fige une frontière prématurée.
  6. L’observabilité et l’automatisation sont limitées. Sans logs structurés, métriques, traces, alertes et déploiements reproductibles, chaque saut réseau rendra l’investigation d’un incident plus coûteuse.

Conserver le monolithe ne signifie pas accepter du code global. L’objectif est que le module puisse évoluer avec une autonomie logique même s’il partage le processus, le dépôt et la release avec d’autres.

Six signaux qui justifient un service indépendant

L’extraction est plus défendable lorsque plusieurs de ces conditions sont réunies, et non lorsqu’une seule apparaît :

  1. Il existe une responsabilité délimitée et compréhensible. Le service a une mission concrète, des règles cohésives et un langage de domaine propre.
  2. Il peut être propriétaire de ses données. Il gère son stockage et expose des opérations, événements ou requêtes convenus, au lieu de permettre un accès direct à ses tables.
  3. Il a réellement besoin de déploiements indépendants. Son cycle de changement doit progresser sans coordonner chaque release avec le cœur.
  4. Sa charge est différenciée. Un processus de conversion, de recherche, de génération de fichiers ou de calcul intensif peut nécessiter une montée en charge et des ressources différentes.
  5. Sa défaillance peut être isolée. Le système peut se dégrader de manière explicite si cette capacité ne répond pas, au moyen de tentatives répétées, d’états en attente ou de travail différé.
  6. La responsabilité opérationnelle est suffisante. Une équipe ou un responsable peut maintenir son cycle de vie, ses alertes, ses incidents, sa sécurité et sa compatibilité.

Une API ne transforme pas à elle seule un module en microservice. L’indépendance dépend aussi des données, du déploiement, de l’exploitation et de la capacité à prendre des décisions sans dépendre des détails internes d’une autre application.

Les coûts qui apparaissent en séparant les responsabilités

Un appel de fonction échoue différemment d’un appel HTTP, d’un message en file ou d’une requête distante. Après l’extraction apparaissent la latence, les timeouts, l’authentification entre services, les limites de débit, l’indisponibilité partielle et les versions incompatibles.

Le modèle de cohérence change également. Si un service confirme une opération et qu’un autre ne reçoit pas ou ne traite pas l’événement, il faut décider comment détecter l’état, réessayer sans dupliquer les effets et compenser lorsque c’est nécessaire. Les consommateurs d’événements doivent être idempotents ; par exemple, traiter deux fois un message ne doit pas émettre deux documents ni facturer deux fois.

L’exploitation gagne en complexité : corrélation des requêtes, traces distribuées, tableaux de bord de métriques, rétention des logs, gestion des secrets, politiques de sauvegarde et tests de récupération. En outre, chaque contrat nécessite des règles de compatibilité. Ajouter des champs optionnels est généralement moins perturbant que modifier la sémantique, supprimer des champs ou réutiliser un état avec une nouvelle signification.

Architecture de transition au sein de PHP

La voie la moins risquée consiste à modulariser avant d’extraire. Définissez une couche d’application pour chaque capacité, avec des cas d’utilisation qui reçoivent des commandes ou des requêtes et renvoient des résultats bien définis. Masquez l’accès à la base de données derrière des dépôts ou des ports lorsque cela représente une dépendance pertinente ; ne transformez pas chaque classe en une abstraction sans finalité.

Le reste du monolithe doit utiliser le module via son interface publique interne, et non via ses entités ou ses tables. Si des notifications asynchrones sont nécessaires, publiez des événements de domaine ou d’intégration depuis un point contrôlé. Une boîte d’envoi transactionnelle peut aider à enregistrer le changement métier et l’événement en attente dans la même transaction, afin qu’un processus ultérieur le délivre de manière fiable.

Cette phase permet de vérifier si la frontière est réelle. Si l’interface interne ne cesse de croître, exige des objets privés d’autres modules ou nécessite des transactions partagées dans chaque cas d’utilisation, elle n’est pas encore un candidat solide à la séparation.

Comment définir la première frontière de service

Le premier service doit avoir une responsabilité facile à expliquer et une dépendance limitée au cœur. Documentez quatre éléments avant d’écrire l’infrastructure :

  • Responsabilité : quelles décisions il prend et lesquelles sont explicitement hors de son périmètre.
  • API ou événements : entrées, sorties, erreurs, authentification, délais d’attente et idempotence.
  • Propriété des données : ce qu’il stocke, quels identifiants externes il conserve et quelles informations il interroge via des contrats.
  • Compatibilité : comment producteurs et consommateurs coexisteront pendant les changements de version, y compris les messages retardés.

Évitez de concevoir une API comme un miroir des tables. Un contrat doit exprimer des opérations ou des faits métier, et non exposer des détails de persistance qui bloqueront les changements ultérieurs.

Exemple : traitement documentaire sans fragmenter le backoffice

Considérez un backoffice PHP qui gère des dossiers et doit générer, valider et stocker des documents. Au départ, le traitement peut vivre sous la forme d’un module interne : il reçoit une demande de génération, enregistre le travail, exécute une tâche asynchrone et met à jour un état visible par l’utilisateur.

L’extraction devient raisonnable si la génération consomme des ressources très différentes, nécessite des dépendances de conversion spécifiques, reçoit ses propres pics et peut fonctionner avec une demande documentaire contenant les données minimales nécessaires. Le service documentaire ne devrait pas consulter librement les tables du dossier. Le backoffice peut envoyer un ordre avec un identifiant, le template applicable, la version des données et la destination ; le résultat revient sous forme d’événement ou d’état consultable.

Avant cela, il convient de préciser qu’un template définit la structure de sortie, tandis qu’un modèle peut désigner des données de domaine ou un système d’IA. Si l’IA était intégrée pour classer des documents, il faudrait un cas d’utilisation délimité, une évaluation avec des données représentatives, une révision humaine face aux décisions sensibles, une protection des données, un contrôle des coûts et une alternative manuelle ou fondée sur des règles lorsque le fournisseur échoue.

Vérifications avant d’extraire

Vérifications avant d’extraire — guía visual de DedicatedPHP

Ne traitez pas l’extraction comme un déploiement technique isolé. Définissez une activation progressive pour un sous-ensemble contrôlé d’opérations, distincte de l’annonce du changement à tous les utilisateurs. Conservez un plan de coexistence et de réversion pendant la validation du comportement.

  • Tests de contrat entre producteur et consommateur, en plus des tests unitaires et d’intégration.
  • Métriques de latence, d’erreurs, de tentatives répétées, de files en attente, de doublons et de temps jusqu’à l’achèvement du processus.
  • Identifiants de corrélation pour suivre une opération entre le monolithe, les files et le service.
  • Procédures de récupération : réexécution sûre, réconciliation des états, sauvegarde et restauration.
  • Règles explicites de dégradation fonctionnelle lorsque le service n’est pas disponible.
  • Un critère de sortie : quelle preuve démontrera que l’extraction a réduit un problème concret et n’a pas seulement déplacé la complexité.

La meilleure décision architecturale est celle qui protège l’évolution du produit sans imposer une plateforme disproportionnée. Un monolithe PHP modulaire, mesurable et bien délimité constitue souvent l’étape appropriée jusqu’à ce qu’une capacité démontre un besoin vérifiable d’indépendance.

Vous souhaitez appliquer ces idées à votre projet ?Parlons de votre plateforme PHP.
Afficher les services associés