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.