Monolithe, monolithe modulaire ou micro-services : comment choisir son architecture backend ?
Micro-services, micro-services, micro-services : tel un Steve Ballmer sur scène, tout un pan de l’industrie scande ce petit refrain depuis quelques années. Comme si c’était devenu le passage obligé d’une application résiliente et digne d’être le prochain Netflix.

J’ai moi-même péché par suivisme. Après avoir travaillé chez Molotov TV, dans un environnement où les micro-services répondaient à de vraies contraintes, j’ai voulu appliquer les mêmes réflexes à NuCorder. J’ai laissé pourrir son monolithe Java vieillissant, puis cédé aux sirènes des micro-services par réflexe plutôt que par besoin.
Dans un précédent article, je racontais la migration du backend de NuCorder de Java vers Go, en m’aidant de l’IA comme un outil. Cette fois, j’aimerais prendre du recul sur la vraie question : quelle architecture est la mieux adaptée à chaque phase du cycle de vie d’un projet, et pour quelles raisons ?
De quoi parle-t-on exactement ?¶
Pour faciliter la comparaison, prenons la même application fictive dans les trois cas : une boutique en ligne composée d’un catalogue, de commandes et d’un système de paiement. Seule son organisation va changer.

Monolithe : une seule boîte¶
Commençons par le monolithe : derrière ce terme un peu massif se cache une idée assez simple, à savoir que toute l’application est construite et déployée comme un seul ensemble.
Prenons l’exemple de notre boutique en ligne : la gestion du catalogue, les commandes et le paiement vivent dans la même application. Ces différentes fonctions peuvent être séparées dans le code, mais elles sont testées et mises en production ensemble.
On peut alors se représenter le monolithe comme une seule boîte qui réunit plusieurs mécanismes. Pour intervenir sur l’un d’eux, il faut généralement reconstruire et remettre en production la boîte entière.
Cela ne dit rien, en soi, de la qualité du code : un monolithe peut être parfaitement structuré, tout comme il peut être devenu un plat de spaghetti difficile à faire évoluer. Monolithe décrit son mode d’assemblage et de déploiement, pas son état de santé.

Les projets commencent souvent par un monolithe¶
La plupart des projets commencent naturellement ainsi : un seul projet à lancer, un seul environnement à reproduire et un seul déploiement.
Le monolithe n’est pas forcément provisoire : le cœur applicatif de GitHub et celui de GitLab reposent encore sur d’importants monolithes Rails vieux de plus d’une décennie, même si d’autres services gravitent autour d’eux. La question est donc de savoir quand ses limites coûtent plus cher que la complexité nécessaire pour en changer.
Monolithe modulaire : une boîte, plusieurs compartiments¶
Le monolithe modulaire conserve le principe d’une application unique, mais organise son fonctionnement interne autour de frontières explicites.
Notre catalogue, nos commandes et notre paiement sont toujours construits et déployés ensemble. En revanche, chacun forme un module distinct, responsable de sa propre logique. Un module ne vient pas fouiller directement dans les mécanismes de son voisin : il passe par les points d’entrée exposés à cet effet.
Visuellement, on retrouve donc une seule boîte, mais composée de compartiments bien délimités. Elle conserve une grande partie de la simplicité opérationnelle du monolithe tout en évitant par construction que toutes ses fonctions se mélangent progressivement.
Cette séparation ne repose pas seulement sur une arborescence de fichiers : elle doit se retrouver dans les dépendances, les interfaces et la propriété des données. Sans frontières effectivement respectées, un monolithe « modulaire » redevient rapidement un monolithe tout court.
En pratique, ces frontières sont parfois floues et demandent un véritable travail de conception. Il faut décider quel module possède quelle responsabilité, dans quel sens circulent les dépendances et jusqu’où les données peuvent être partagées. Cela peut conduire à revoir le modèle de données ou la manière dont l’ORM représente ses relations : une association très pratique dans le code peut aussi coupler directement deux modules censés rester séparés.

Micro-services : plusieurs boîtes reliées¶
Dans une architecture en micro-services, notre boutique n’est plus livrée comme un seul ensemble. Le catalogue, les commandes et le paiement deviennent des applications distinctes, déployables indépendamment et séparées par des communications réseau.
Chaque service possède une responsabilité délimitée et expose une manière de communiquer avec les autres, généralement à travers une API synchrone — ou par l’échange de messages asynchrones. Le service de commandes peut ainsi demander au catalogue si un produit est disponible, puis transmettre une demande de paiement au service concerné.
L’API gateway¶
Cette organisation s’accompagne aussi souvent d’une API gateway, une porte d’entrée commune qui route les requêtes et masque les API internes (au prix d’une couche supplémentaire à configurer et à superviser).
On obtient plusieurs boîtes spécialisées plutôt qu’une seule boîte polyvalente ; chacune peut être modifiée, redémarrée ou renforcée sans nécessairement toucher aux autres.
Le terme « micro » est d’ailleurs trompeur : il ne définit pas une taille précise. L’idée importante est moins de créer de tout petits services que de trouver des frontières leur permettant d’évoluer avec suffisamment d’autonomie.

Présentées dans cet ordre, ces architectures pourraient donner l’impression d’une évolution naturelle : une application commencerait monolithique, deviendrait modulaire, puis finirait découpée en micro-services. Ce n’est pas une trajectoire obligatoire. Chaque étape ajoute des frontières, mais aussi un coût qui doit répondre à un besoin réel.
Dans la pratique, chacune de ces architectures déplace la complexité à un endroit différent : c’est précisément ce qui rend le choix intéressant.
Trois projets, trois réponses¶
Ces définitions donnent des repères, mais elles ne permettent pas à elles seules de choisir une architecture. Pour rendre les différences plus concrètes, voici trois projets sur lesquels j’ai travaillé, avec des contraintes et des échelles très différentes.
STB Tester : le monolithe, tout simplement suffisant¶
Chez Canal+, j’ai repris un prototype local écrit en Python pour en faire STB Tester : une application web interne en Node.js permettant de piloter des campagnes de tests sur des boîtiers TV et de produire un rapport quotidien. Le périmètre était borné, le projet porté par un seul développeur et la charge parfaitement prévisible.
Dans ce contexte, le monolithe n’était pas une première étape en attendant une architecture plus sérieuse : c’était la bonne architecture. Découper l’application en plusieurs services aurait ajouté des déploiements, des échanges réseau et de la supervision, sans apporter d’autonomie ou de résilience dont le projet aurait réellement bénéficié.
NuCorder : le monolithe modulaire, par nécessité plus que par doctrine¶
NuCorder se situe à une autre échelle fonctionnelle, mais pas à une autre échelle d’équipe — j’en suis encore aujourd’hui le contributeur technique principal. J’y ai pourtant créé progressivement plusieurs services Go autour du monolithe Java existant, moins par architecture réfléchie que par accumulation de décisions locales.
Le résultat était prévisible : plusieurs applications à construire, déployer, superviser et maintenir, sans l’équipe capable d’absorber ce coût. Lors de la refonte du backend, j’ai donc rassemblé dans un monolithe modulaire tout ce qui n’avait aucune raison de vivre séparément, tout en conservant isolés les composants qui répondaient à des contraintes réellement différentes.
Molotov TV : les micro-services, à la bonne échelle¶
Chez Molotov, le contexte était totalement différent. Plusieurs squads travaillaient en parallèle sur la plateforme, avec des rythmes et des périmètres distincts, et des profils de charge très différents selon les composants.
Les micro-services permettaient d’isoler ces composants, de les dimensionner séparément et de donner davantage d’autonomie aux équipes. Leur coût répondait donc à des contraintes réelles d’organisation, de charge et de disponibilité. Je l’ai moi-même constaté en cassant involontairement le service de bookmarks : l’incident est resté localisé, le reste de la plateforme a continué de fonctionner et j’ai pu revenir rapidement à la version précédente.
Le piège du sur-découpage¶
Une architecture en micro-services ne garantit pas pour autant que les frontières soient bonnes. Chez Molotov, certaines évolutions finissaient par demander à un même développeur d’intervenir sur trois ou quatre services : la frontière réseau n’avait pas supprimé le couplage, elle l’avait seulement rendu plus coûteux à traverser.
C’est ce que l’on qualifie parfois de monolithe distribué : plusieurs applications à exploiter séparément, qui conservent pourtant le couplage que leur découpage devait justement éviter.
La taille du produit ne suffit pas à choisir une architecture. Il faut aussi regarder la taille de l’équipe, la diversité des charges, les exigences de disponibilité et la capacité à opérer le système.
Comment trancher, en pratique ?¶
Je ne choisis pas une architecture à partir des besoins que le produit pourrait avoir un jour, mais à partir des contraintes que l’équipe rencontre déjà (ou dont l’arrivée est suffisamment certaine).

Combien de personnes vont réellement travailler sur le projet ?¶
Une personne ou une petite équipe a peu à gagner à multiplier les services indépendants, là où plusieurs équipes autonomes peuvent avoir besoin de cycles de développement et de déploiement distincts. Si une évolution oblige régulièrement plusieurs équipes à se coordonner, les frontières choisies sont peut-être mauvaises — quel que soit le nombre de services.
Le découpage permettra-t-il réellement à deux équipes d’avancer indépendamment ?
Certaines parties doivent-elles évoluer ou être déployées séparément ?¶
Un service devient intéressant lorsqu’un domaine évolue véritablement à son propre rythme : mises en production plus fréquentes, contraintes de sécurité particulières, technologie spécialisée ou responsabilité clairement attribuée. Si tout est systématiquement testé et livré ensemble, plusieurs services risquent de ne produire qu’un déploiement distribué.
Ce composant doit-il pouvoir être mis en production sans coordonner le reste de l’application ?
Les contraintes de charge ou de disponibilité sont-elles différentes ?¶
La charge globale ne suffit pas à justifier les micro-services : un monolithe peut être répliqué sur plusieurs machines ou disposer de plus de ressources sur la même machine. L’extraction devient plus pertinente lorsqu’un composant présente un profil réellement distinct : un traitement très consommateur, une API bien plus sollicitée que le reste, ou une fonction critique qui doit rester disponible malgré la panne d’un autre domaine.
Ai-je besoin de dimensionner ou d’isoler ce composant indépendamment, ou puis-je simplement répliquer l’application entière ?
Les frontières fonctionnelles sont-elles suffisamment stables ?¶
Tant que le métier évolue rapidement, les responsabilités changent et les fonctionnalités traversent les frontières imaginées quelques mois plus tôt. Dans un monolithe, déplacer une responsabilité reste principalement une opération de code ; entre deux services, elle peut aussi demander de migrer des données et de maintenir temporairement plusieurs versions compatibles.
Cette frontière vient-elle de notre compréhension du métier ou seulement de l’organisation actuelle du code ?
L’équipe et l’entreprise peuvent-elles absorber le coût d’un système distribué ?¶
Créer un service supplémentaire ne coûte pas seulement quelques fichiers et un nouveau déploiement : il faut aussi l’héberger, le superviser et prévoir ce qui se passe lorsque ses dépendances ne répondent plus. Ce coût se paie deux fois, en temps humain et sur la facture d’infrastructure. Chacun de ces postes peut sembler modeste pris séparément ; répétés sur dix, vingt ou cinquante services, ils deviennent une caractéristique centrale de l’architecture.
Qui diagnostiquera ce service lorsqu’il tombera en panne un dimanche matin ?
Prime Video : quand distribuer coûte trop cher¶
En 2023, une équipe Prime Video expliquait avoir regroupé les composants distribués d’un outil d’analyse audio et vidéo dans une application unique. Les transitions d’orchestration et les échanges de données de la première version serverless étaient devenus coûteux à mesure que la charge augmentait.
En réunissant les traitements dans un même processus, déployé avec ECS sur des instances EC2, l’équipe annonçait une réduction de plus de 90 % de ses coûts tout en augmentant la capacité du système. Prime Video n’a pas abandonné les micro-services : elle a corrigé le sur-découpage d’un sous-système précis.
Ma règle de décision¶
| Situation | Choix de départ raisonnable |
|---|---|
| Outil interne borné, maintenu par une personne | Monolithe simple |
| Produit appelé à évoluer, petite équipe | Monolithe modulaire |
| Domaine stable avec une contrainte distincte | Extraction ciblée d’un service |
| Plusieurs équipes réellement autonomes | Micro-services envisageables |
| Frontières incertaines ou exploitation immature | Rester dans le monolithe |
À défaut d’une raison précise de distribuer le système, je préfère aujourd’hui commencer par un monolithe, puis évoluer vers du modulaire. Non parce que le monolithe serait intrinsèquement supérieur, mais parce qu’il conserve davantage de décisions faciles à modifier.
Je n’extrais pas un service parce qu’un domaine pourrait un jour avoir besoin d’être dimensionné séparément, isolé ou confié à une autre équipe. Je l’extrais lorsque ce besoin existe, que sa frontière est suffisamment comprise et que le gain attendu dépasse le coût de son exploitation.
Une architecture réelle est d’ailleurs rarement binaire. Elle peut réunir un monolithe pour le cœur métier, quelques services séparés pour les traitements spécialisés et des composants SaaS externes.
Ce que j’en retiens¶
Aucune de ces architectures n’est l’étape suivante obligatoire d’un projet : STB Tester, NuCorder et Molotov avaient besoin de réponses différentes, parce que leurs équipes, leurs charges et leurs exigences de disponibilité n’avaient rien de comparable.
Avec NuCorder, j’ai reproduit une architecture adaptée à un contexte beaucoup plus grand, sans disposer des mêmes problèmes ni des mêmes moyens humains. Le monolithe modulaire constitue aujourd’hui un meilleur compromis pour son cœur métier, en laissant séparés les composants qui ont de vraies contraintes spécifiques.
Une architecture est un compromis à réévaluer lorsque les contraintes changent. La bonne architecture n’est pas celle qui contient le plus de services, mais celle dont l’équipe peut assumer la complexité aujourd’hui sans se fermer inutilement les portes de demain.
Votre architecture commence à vous coûter trop cher ?¶
Votre monolithe est devenu difficile à faire évoluer ? Vos micro-services se sont accumulés au point qu’une fonctionnalité nécessite d’en modifier plusieurs ? Vous envisagez une migration, sans savoir si le problème vient réellement de l’architecture, du code ou de l’organisation de l’équipe ?
J’interviens pour comprendre les contraintes réelles, identifier ce qui mérite d’être séparé ou réuni, puis définir une trajectoire adaptée à votre équipe — sans imposer d’architecture cible ni de nouvelle stack.