02 - Decouper un monolithe : par ou commencer
Ce que tu vas apprendre
- Comment identifier les frontieres naturelles dans un monolithe
- Le pattern strangler fig pour migrer progressivement
- Comment éviter le piège du monolithe distribue
Prerequisites
01 - Monolithe vs microservices
Ne decoupe pas au hasard
La pire facon de passer aux microservices, c'est de prendre un monolithe et de le découper par couche technique. Un service "controllers", un service "database", un service "utils". Ca n'a aucun sens. Tu obtiens des services qui ne peuvent pas fonctionner l'un sans l'autre -- un monolithe distribue avec le réseau en plus.
La bonne approche, c'est de découper par capacité métier. Et pour ca, il faut comprendre ou sont les frontieres dans ton domaine.
Les bounded contexts : trouver les frontieres
Si tu as lu la serie sur les domaines et cycles de vie, tu connais deja le concept. Un bounded context, c'est un perimetre dans lequel un terme a une signification precise et un modèle a une coherence.
Prenons un e-commerce. Le mot "produit" n'a pas le meme sens partout :
- Catalogue : un produit a un nom, une description, des photos, un prix public
- Stock : un produit a un SKU, une quantité disponible, un emplacement en entrepot
- Commandes : un produit est une ligne de commande avec un prix a l'instant T et une quantité
Trois bounded contexts, trois modèles différents du meme concept. Chacun pourrait etre un microservice.
Pour identifier les bounded contexts dans ton code, cherche :
- Les modules qui partagent peu de tables en base
- Les parties du code qui changent pour des raisons différentes
- Les équipes qui travaillent sur des zones distinctes
- Les termes qui ont des significations différentes selon le contexte
Exemple concret : extraire le RSS de paltemps.fr
Sur paltemps.fr, le scraping des feeds RSS est un bon candidat theorique pour une extraction. Pourquoi ? Parce qu'il a des caracteristiques d'un service independant :
paltemps.fr (monolithe actuel)
├── api/ # Routes Elysia (REST)
├── scraper/ # Scraping RSS + contenu articles
├── feeds/ # Gestion des feeds (CRUD)
├── auth/ # Authentification
└── frontend/ # Rendu des pages
# Si on devait extraire le scraper :
service-scraper/
├── src/
│ ├── scheduler.ts # Cron de scraping
│ ├── parser.ts # Parsing RSS/Atom
│ ├── fetcher.ts # HTTP fetch avec retry
│ └── store.ts # Sauvegarde des articles
├── proto/
│ └── scraper.proto # API gRPC pour demander un scrape
└── Dockerfile
Le scraper a ses propres donnees (les articles scrapes), son propre rythme (un cron), et une interface simple avec le reste de l'app ("donne-moi les articles du feed X"). C'est le candidat parfait pour une première extraction.
Mais -- et c'est important -- sur paltemps.fr, cette extraction ne se justifie pas. Un seul développeur, pas de contrainte de scaling différente, pas de besoin de techno différente. Le monolithe fait le job. Je montre l'exemple pour illustrer la demarche, pas pour dire qu'il faut le faire.
Le pattern strangler fig
Le strangler fig (figuier etrangleur), c'est un pattern de migration nomme par Martin Fowler. L'idee : au lieu de reecrire tout d'un coup, tu fais pousser le nouveau service a cote de l'ancien code, et tu migres progressivement.
En pratique :
- Identifie la fonctionnalité a extraire (le module le moins couple)
- Cree le nouveau service avec la meme API que le module existant
- Route le trafic vers le nouveau service via un proxy ou un feature flag
- Supprime l'ancien code une fois que le nouveau service est stable
- Repete pour le prochain module
Etape 1 : Tout passe par le monolithe
[Client] --> [Monolithe (scraping + API + auth)]
Etape 2 : Le proxy route le scraping vers le nouveau service
[Client] --> [Proxy/Gateway]
├── /api/* --> [Monolithe (API + auth)]
└── /scrape/* --> [Service Scraper]
Etape 3 : Le code scraping est supprime du monolithe
[Client] --> [Proxy/Gateway]
├── /api/* --> [Monolithe (API + auth)]
└── /scrape/* --> [Service Scraper]
L'avantage : a chaque étape, l'application fonctionne. Pas de big bang. Si le nouveau service pose problème, tu reroutes vers l'ancien code en 30 secondes.
Le piège du monolithe distribue
C'est l'erreur la plus courante. Tu decoupes ton monolithe en 8 services, mais :
- Les services partagent la meme base de donnees
- Un changement dans le service A nécessité un changement dans le service B
- Tu ne peux pas déployer un service sans déployer les autres
- Les services s'appellent en cascade synchrone pour chaque requête
Felicitations, tu as un monolithe distribue. C'est le pire des deux mondes : la complexité des microservices sans les avantages.
Comment le détecter :
- Déploiement groupe : si tu dois déployer 3 services en meme temps, c'est un monolithe distribue
- Base partagee : si deux services font des JOIN sur les memes tables, c'est un monolithe distribue
- Changements en cascade : si modifier l'API du service A casse les services B, C et D, c'est un monolithe distribue
Comment l'éviter :
- Chaque service a sa propre base de donnees. Point.
- La communication passe par des contrats explicites (REST, gRPC, messages)
- Les services sont conçus pour tolérer l'indisponibilite des autres
- Les déploiements sont independants -- si tu ne peux pas déployer un service seul, tes frontieres sont mauvaises
Un plan de migration realiste
Si tu decides de migrer, voici un plan en 4 phases :
Phase 1 : Structurer le monolithe (2-4 semaines) Reorganise le code en modules avec des interfaces claires. C'est ce que fait l'architecture hexagonale : des ports entre les modules. Pas encore de microservices. Juste du code propre.
Phase 2 : Extraire le premier service (2-3 semaines) Choisis le module le moins couple. Cree le service, mets en place le pipeline CI/CD, la communication inter-services. Ce premier service va te coûter cher en temps -- c'est normal. Tu mets en place toute la plomberie.
Phase 3 : Stabiliser (2-4 semaines) Monitoring, alerting, tracing distribue. Si tu sautes cette étape, tu vas souffrir. L'article sur l'infrastructure couvre le sujet.
Phase 4 : Extraire les services suivants (iteratif) Chaque extraction suivante va plus vite parce que la plomberie est en place. Mais ne te precipite pas. Extrais un service quand il y a une raison concrète, pas pour le plaisir.
Résumé
- Decoupe par capacité métier (bounded context), pas par couche technique
- Le strangler fig pattern permet de migrer sans big bang
- Le monolithe distribue est pire qu'un monolithe -- vérifié que tes services sont vraiment independants
- Commence par structurer ton monolithe, puis extrais le module le moins couple
Article précédent : 01 - Monolithe vs microservices Article suivant : 03 - Communication inter-services : REST, gRPC et messages