“On va passer en microservices.” C’est une phrase qu’on entend régulièrement en début de mission. Rarement parce que l’équipe a analysé ses contraintes et conclu que c’est la bonne architecture. Plus souvent parce que c’est ce que font les autres, parce que le recrutement l’exige, ou parce qu’un consultant l’a recommandé sans creuser le contexte.
Le résultat, trop souvent : une complexité opérationnelle qui explose, une latence qui augmente, et une équipe qui passe plus de temps à orchestrer des services qu’à livrer de la valeur.
Le problème n’est pas le monolithe, c’est le monolithe mal structuré
Il faut distinguer deux choses que le discours tech confond constamment :
Le monolithe big ball of mud : tout est couplé à tout, les responsabilités sont mélangées, changer une ligne peut casser n’importe quoi. C’est effectivement un problème. Mais ce n’est pas un problème de topologie de déploiement : c’est un problème de structure interne.
Le monolithe modulaire : un unique artefact déployable, mais avec des modules internes aux responsabilités claires, des interfaces explicites entre modules, et des règles strictes sur qui peut appeler qui. C’est une architecture parfaitement viable pour une très large gamme de systèmes.
La confusion entre les deux pousse des équipes à “résoudre” un problème de structure interne en ajoutant de la complexité de distribution. C’est traiter le mauvais problème avec le mauvais outil.
Ce que les microservices apportent, et ce qu’ils coûtent
Les bénéfices réels
- Scalabilité indépendante : scaler le service de traitement d’images sans scaler le service utilisateur
- Déploiement indépendant : une équipe peut livrer son service sans coordination avec les autres
- Isolation des pannes : un service qui tombe n’emporte pas tout le système
- Hétérogénéité technologique : chaque service peut utiliser la stack adaptée à son problème
Les coûts réels, souvent sous-estimés
- Complexité opérationnelle : vous avez maintenant 30 services à monitorer, déployer, debugger. La surface d’erreur explose
- Latence réseau : un appel local devient un appel réseau. Sur des workflows qui chaînent 10 services, ça se cumule
- Transactions distribuées : ce qui était une transaction ACID locale devient un problème de cohérence distribuée (saga pattern, compensation, idempotence)
- Découverte et observabilité : sans tracing distribué (Jaeger, Tempo), comprendre pourquoi une requête échoue devient un exercice de détective
- Tests d’intégration : tester l’interaction entre services est exponentiellement plus complexe que tester des modules internes
Martin Fowler le dit clairement depuis des années : “Don’t start with microservices.” Ce n’est pas un rejet des microservices, c’est une reconnaissance que le coût d’entrée est élevé et qu’il ne se justifie que dans certains contextes.
Les vrais critères de décision
Critère 1 : la taille et la structure de l’équipe
Conway’s Law dit que l’architecture d’un système tend à refléter la structure de communication de l’organisation qui le produit. C’est une observation, pas une prescription.
Les microservices sont naturels quand vous avez plusieurs équipes produit autonomes qui doivent pouvoir livrer indépendamment. Si vous avez une seule équipe de 5 à 8 développeurs, les microservices ajoutent des frictions inter-services sans apporter les bénéfices d’autonomie.
Question à se poser : est-ce que le couplage organisationnel est le vrai problème ? Si oui, les microservices peuvent aider. Sinon, ils ajoutent de la complexité sans résoudre rien.
Critère 2 : les besoins de scalabilité différenciés
Si toutes les parties de votre système ont les mêmes besoins de scalabilité (elles grandissent ensemble, elles sont utilisées de la même façon), un monolithe scalable horizontalement est suffisant.
Les microservices apportent de la valeur quand une partie spécifique du système a des besoins radicalement différents des autres. Exemple concret : un service de génération de rapports à la demande, gourmand en CPU, qui ne doit pas affecter la latence du service transactionnel principal.
Critère 3 : la maturité opérationnelle
Les microservices requièrent une infrastructure de déploiement, de monitoring et d’observabilité mature. Sans Kubernetes (ou équivalent), sans service mesh ou gateway, sans tracing distribué, sans alerting granulaire par service, les microservices sont une promesse sans filet.
Question brutale : avez-vous une équipe ops capable de maintenir cette infrastructure ? Si la réponse est non, vous allez créer une dette opérationnelle supérieure à la dette technique que vous cherchez à résorber.
Critère 4 : les exigences de conformité et d’isolation
Dans des contextes réglementés (données de santé, données financières, données à caractère personnel sensibles), l’isolation des données entre domaines peut être une exigence de conformité. Les microservices avec des bases de données dédiées par service répondent naturellement à cette exigence.
Un monolithe avec une base de données partagée peut aussi y répondre via des schémas séparés et des contrôles d’accès au niveau de la base, mais c’est moins naturel.
Le monolithe modulaire : comment le construire correctement
Si le monolithe modulaire est la bonne réponse pour votre contexte, voici les principes qui le rendent viable à long terme.
Définir des frontières de module explicites
Chaque module a une responsabilité claire, une interface publique documentée, et un espace interne privé. Les modules ne s’appellent pas directement : ils passent par les interfaces publiques.
En Java : packages avec accès package-private sur les implémentations. En Go : packages avec exported/unexported. En TypeScript : barrel files et règles d’import enforced par eslint (dependency-cruiser).
Un module = une base de données logique
Même si tout partage physiquement la même base, chaque module doit avoir son propre schéma ou ensemble de tables, et ne jamais accéder directement aux tables des autres modules. C’est la règle la plus difficile à tenir, et la plus importante.
Si votre monolithe modulaire respecte cette règle, la migration vers des microservices plus tard (si le contexte l’exige) est possible : il suffit d’extraire le module et sa base dans un service séparé.
Des interfaces synchrones et asynchrones entre modules
Les modules communiquent via des interfaces explicites. Pour les appels synchrones : méthodes ou interfaces typées. Pour les événements : un bus d’événements interne (simple channel Go, event emitter Node, ApplicationEventPublisher Spring).
L’asynchronisme interne permet de tester la tolérance aux pannes et prépare naturellement une éventuelle distribution future.
Quand passer aux microservices est la bonne décision
Il y a des cas où les microservices sont la bonne réponse :
- Équipes multiples avec des cycles de livraison différents : l’autonomie de déploiement est un vrai besoin
- Partie du système avec des exigences de scalabilité radicalement différentes : une fonction de traitement temps réel qui doit scaler indépendamment
- Intégration de systèmes existants hétérogènes : les microservices comme couche d’intégration autour de systèmes legacy qu’on ne peut pas modifier
- Exigences d’isolation forte pour la conformité : chaque domaine de données dans son propre périmètre
Et souvent, la bonne réponse est un système hybride : un monolithe modulaire pour le cœur du système, avec quelques services extraits pour des besoins spécifiques bien identifiés.
Conclusion
L’architecture n’est pas une compétition de prestige. Ce n’est pas parce que Netflix et Uber utilisent des microservices que c’est la bonne réponse à votre problème. Ils ont des équipes de milliers de personnes et des contraintes de scalabilité que vous n’avez probablement pas.
La question n’est pas “microservices ou monolithe ?”. La question est “quelle architecture minimise la complexité accidentelle et maximise la capacité de l’équipe à livrer de la valeur de façon durable ?”. Parfois c’est des microservices. Souvent, c’est un bon monolithe modulaire.
Vous êtes en train de définir votre trajectoire d’architecture ? Échangeons.