Tous les quelques ans, l'industrie logicielle redécouvre une vérité ancienne : les décisions d'architecture ne sont pas définitives, et les modifier coûte cher. Le chapitre actuel de cette histoire récurrente est la migration du monolithe vers les microservices, un parcours qui a produit autant de succès que d'histoires d'avertissement.
Chez Globe Software Solutions, nous avons accompagné plusieurs clients dans cette transition, des jeunes pousses qui ont dépassé leur architecture initiale aux grandes entreprises avec des systèmes vieux de décennies. La leçon la plus importante : l'objectif n'est pas les microservices. L'objectif est une architecture système qui sert vos besoins actuels et à court terme. Parfois cela signifie des microservices. Parfois un monolithe bien structuré. Souvent quelque chose entre les deux.
Cadre de décision : faut-il vraiment migrer ?
Avant d'aborder comment migrer, il faut se demander si vous devez le faire. Les microservices résolvent des problèmes précis. Si vous n'avez pas ces problèmes, la migration créera de la complexité sans apporter de valeur.
Migrez si :
- La mise à l'échelle indépendante est un vrai besoin. Différentes parties de votre système ont des profils de charge vraiment différents et vous devez les faire évoluer indépendamment. Un module de reporting qui connaît des pics trimestriels et une API temps réel nécessitant une faible latence constante en sont un exemple classique.
- L'autonomie des équipes est freinée par le monolithe. Plusieurs équipes doivent déployer des changements sur le même système selon des calendriers différents, et les conflits de fusion, les trains de release partagés et la coordination ralentissent tout le monde.
- La diversité technologique est nécessaire. Certaines parties de votre système gagneraient à utiliser d'autres langages, frameworks ou bases de données, mais le monolithe impose une stack unique.
- L'isolation des pannes est critique. Un bug dans un module ne doit pas pouvoir faire tomber tout le système. Dans un monolithe, une fuite mémoire dans le module de traitement d'images peut faire planter le parcours d'achat.
Restez sur le monolithe si :
- Votre équipe est petite. Une équipe de 5 à 10 ingénieurs qui fait tourner une architecture microservices passera plus de temps sur l'infrastructure que sur les fonctionnalités. La charge opérationnelle ne devient rentable qu'au-delà d'une certaine taille d'équipe, typiquement 20+ ingénieurs.
- Votre domaine n'est pas bien compris. Les microservices exigent des frontières de domaine claires. Si vous êtes encore en train de découvrir ce qu'est votre produit et comment ses composants s'articulent, vous tracerez mal les frontières, et redessiner les limites des microservices coûte bien plus cher que refactoriser un monolithe.
- Vous n'avez pas la maturité opérationnelle. Les microservices exigent l'orchestration de conteneurs, un service mesh, le tracing distribué, la centralisation des logs et une CI/CD avancée. Si votre équipe n'a pas l'expérience de ces systèmes, la migration créera plus de problèmes qu'elle n'en résoudra.
« Si vous ne savez pas construire un monolithe bien structuré, vous ne savez pas construire des microservices. Les microservices ne corrigent pas une discipline d'ingénierie défaillante ; ils l'amplifient. »
Le pattern Strangler Fig : migrer sans big bang
Pour les organisations qui décident de migrer, nous recommandons presque systématiquement le pattern Strangler Fig, du nom du figuier tropical qui enveloppe et remplace progressivement l'arbre hôte. L'idée est simple : au lieu de réécrire le monolithe de zéro, vous extrayez progressivement les fonctionnalités vers de nouveaux services tandis que le monolithe continue de servir le trafic de production.
Phase 1 : poser les fondations
Avant d'extraire un seul service, mettez en place l'infrastructure requise par les microservices :
- Une plateforme d'orchestration de conteneurs (Kubernetes est le standard de fait)
- Un service mesh ou une API gateway pour le routage, l'authentification et l'observabilité
- Des logs centralisés et du tracing distribué (nous privilégions la stack OpenTelemetry)
- Une pipeline CI/CD permettant de déployer les services individuellement
- Un système de gestion des secrets
Ce travail de fond n'est pas glamour, mais le négliger est la cause la plus fréquente d'échec des migrations. Les équipes extraient quelques services, découvrent qu'elles ne peuvent pas déboguer les problèmes inter-services, et soit reviennent au monolithe soit restent indéfiniment dans un état hybride pénible.
Phase 2 : identifier le premier candidat à l'extraction
Le candidat idéal pour commencer est un module qui :
- A une frontière de domaine claire avec des entrées et sorties bien définies
- A un état partagé minimal avec le reste du monolithe
- Est opérationnellement significatif, pour que l'équipe tire de vraies leçons sur l'exploitation d'un service en production
- Est assez peu risqué pour que les erreurs ne provoquent pas d'incidents critiques
Bons candidats courants : systèmes de notification, modules de reporting, pipelines de traitement de fichiers, fonctionnalité de recherche. Mauvais candidats courants : l'authentification utilisateur (trop couplée) et la logique métier centrale (trop risquée pour un premier essai).
Phase 3 : créer la couture
Avant d'extraire le code, créez une interface propre dans le monolithe qui isole la fonctionnalité cible. Cette « couture » est une frontière API interne : toute communication avec le module cible passe par une interface définie, et non par des appels de fonction directs ou un accès base partagée.
Cette étape est cruciale et souvent ignorée. Si vous extrayez du code sans avoir d'abord établi une interface propre, vous découvrirez des dizaines de dépendances cachées — via la base de données, des caches mémoire partagés, des chemins de fichiers — qui rendront l'extraction bien plus complexe que prévu.
Phase 4 : extraire et faire tourner en parallèle
Avec la couture en place, construisez le nouveau service et routez le trafic vers lui, d'abord en mode shadow (réception sans servir le trafic de production), puis progressivement en déploiement canary. Gardez l'implémentation du monolithe comme repli. Ne désactivez la version du monolithe qu'après que le nouveau service ait fait ses preuves en production pendant une période significative — nous recommandons au moins quatre semaines.
Phase 5 : répéter et affiner
Chaque extraction vous apprend quelque chose sur les frontières de domaine, les capacités opérationnelles et la préparation de l'équipe. La deuxième extraction est toujours plus fluide que la première, et à la quatrième ou cinquième, cela devient une routine.
Le problème des données
La partie la plus difficile de toute migration monolithe vers microservices, ce sont les données. Les monolithes partagent typiquement une base unique, et démêler cet état partagé est là que se trouve la plus grande complexité.
Nous suivons une approche en trois étapes :
- Séparation logique d'abord. Avant de séparer physiquement les bases, imposez une séparation logique : le code de chaque domaine ne peut accéder qu'à ses propres tables, via sa propre couche d'accès aux données. Cela fait remonter les requêtes cross-domain cachées.
- Répliquer, ne pas migrer. Utilisez la capture des changements de données (CDC) pour répliquer les données du service extrait vers une nouvelle base, en gardant les deux synchronisées pendant la transition. Cela évite une migration de données unique et risquée.
- Accepter la cohérence à terme. Dans un monde microservices, certaines données qui étaient auparavant immédiatement cohérentes (car dans une seule base) deviendront à cohérence à terme. Identifiez où c'est important et mettez en œuvre les patterns adaptés : sagas pour les transactions distribuées, pattern outbox pour la publication d'événements fiable, transactions de compensation pour la gestion des échecs.
Anti-patterns que nous rencontrons souvent
Le monolithe distribué. Des services qui doivent être déployés ensemble, qui partagent une base, ou qui ne peuvent pas fonctionner sans appels synchrones à plusieurs autres services. Vous avez toute la complexité opérationnelle des microservices sans en avoir les bénéfices.
L'extraction prématurée. Extraire des services avant que les frontières de domaine soient claires, ce qui mène à des services bavards avec des dépendances circulaires qu'il faut ensuite fusionner à nouveau.
Ignorer le réseau. Dans un monolithe, les appels de fonction sont rapides et fiables. Dans les microservices, chaque appel traverse une frontière réseau qui peut échouer, être lente ou renvoyer des résultats inattendus. Les services doivent être conçus pour l'échec réseau dès le départ : retries, circuit breakers, timeouts et dégradation gracieuse.
Sous-investir dans l'observabilité. Dans un monolithe, vous pouvez souvent comprendre le parcours d'une requête en lisant une stack trace. Dans les microservices, une seule requête utilisateur peut toucher quinze services. Sans tracing distribué, le débogage devient du tâtonnement.
Un calendrier réaliste
Les clients demandent souvent combien de temps dure une migration. La réponse honnête dépend de la taille et de la complexité du monolithe, mais voici un ordre de grandeur :
- Mise en place des fondations : 2 à 4 mois pour une équipe nouvelle en orchestration de conteneurs et service mesh
- Première extraction de service : 2 à 3 mois, temps d'apprentissage inclus
- Extractions suivantes : 1 à 2 mois chacune, en s'accélérant avec l'expérience
- Migration complète (système de complexité moyenne) : 12 à 24 mois avec une équipe dédiée
L'idée clé : c'est un marathon, pas un sprint. Prévoyez une livraison incrémentale de valeur à chaque étape plutôt qu'une date de fin big bang lointaine.
Vous envisagez une migration du monolithe vers les microservices ? Nous pouvons vous aider à évaluer si c'est la bonne décision et accompagner la transition avec un minimum de perturbation. Commençons par en discuter.