J'ai accompagné une PME dans la migration d'un monolithe PHP de 12 ans vers une architecture modulaire. 18 mois, 0 downtime, 0 régression critique. Voici la recette.

Diagnostic initial

Avant de migrer, il faut comprendre. J'ai passé 2 semaines à cartographier : modules, dépendances, flux de données critiques, zones chaudes au niveau perf, zones mortes.

Outil indispensable : Deptrac pour visualiser les dépendances réelles entre namespaces.

Le piège du microservices big-bang

Voir un monolithe et penser "on va tout casser en 20 microservices" = burn-out assuré. La bonne question : où sont les frontières métier naturelles ?

Chez ce client : 3 grands domaines (Catalogue, Commandes, Comptes), 2 services techniques (Notifications, Facturation). Objectif final : 5 services, pas 20.

Étape 1 : modulariser in-place

Avant d'extraire, on sépare. Chaque domaine devient un module isolé, avec ses propres entités, services et contrôleurs. Communication entre modules uniquement via interfaces publiques.

Deptrac enforce les règles : le module Orders peut dépendre de Catalog mais pas l'inverse. Build failure si violation. Discipline incontournable.

Étape 2 : strangler pattern

On choisit un sous-domaine (pas le cœur métier — le plus risqué à déplacer). Dans notre cas : Notifications.

On crée un service externe qui expose une API. On bascule les nouveaux appels du monolithe vers ce service via un feature flag. Jour J, on observe les logs. Jour J+7, tout est stable, on retire le vieux code du monolithe.

La clé : à chaque instant, le système fonctionne. Pas de big-bang, pas de nuit de déploiement, pas de "maintenance programmée".

Étape 3 : Outbox pattern

Quand le monolithe doit communiquer avec les services extraits, on utilise un outbox : chaque événement métier est écrit dans une table outbox_events dans la même transaction que le changement de données. Un worker asynchrone pousse ensuite vers Kafka/RabbitMQ.

Avantage : pas de "phantom read" entre DB et message bus. Pas de perte de message en cas de crash.

Étape 4 : extraction progressive

Commandes est le plus risqué (cœur métier, données sensibles). On l'a gardé pour la fin. À ce stade, le monolithe ne contient plus que les Comptes et le Catalogue. Extraction simple, le reste ayant servi de rodage.

Ce qui a été gardé dans le monolithe

Quelques features peu critiques, utilisées par une petite partie des utilisateurs : administration interne, reporting annuel. Microserviser n'apportait rien. Règle : si ça marche et ça ne grandit pas, ne touchez pas.

Les pièges évités

  • Double-écriture : toujours outbox, jamais "écris dans les deux DB en parallèle".
  • Transaction distribuée : évitée systématiquement via saga pattern ou eventual consistency.
  • Ops explosion : 5 services = 5 déploiements, 5 monitoring, 5 alertes. Automatisez tout avant d'extraire le deuxième.
  • Latence : mesurez avant/après. Certains appels intra-module en 1ms deviennent 50ms en inter-service.

Les outils

  • Deptrac (dépendances).
  • Symfony Messenger ou Laravel queues avec Redis/RabbitMQ.
  • OpenTelemetry pour le tracing distribué (indispensable).
  • Grafana + Prometheus pour observer.

Vous avez un monolithe qui craque aux jointures ? Je fais ce type de mission.