Tech

Monolithe ou microservices : pourquoi organiser son architecture par domaines

Ces dernières années, beaucoup de donneurs d’ordres ont poussé vers des architectures orientées microservices, souvent présentées comme la solution ultime aux problèmes de scalabilité, de fiabilité et de flexibilité.

La réalité est moins flatteuse. En pratique, les microservices apportent une complexité largement sous-estimée, ralentissent les cycles de développement et augmentent considérablement les coûts d’exploitation. De nombreuses équipes se retrouvent aujourd’hui avec :

  • une complexité inutile,
  • une perte de lisibilité,
  • et parfois une vélocité plus faible qu’avant.

L’opposition entre monolithe et microservices est souvent mal comprise. Le discours a aussi été largement biaisé par les vendeurs cloud, qui profitent de la confusion pour vendre des solutions d’hébergement et de monitoring plus coûteuses dans les architectures hautement distribuées que dans les monolithes simples.

Pour autant, il y a une part de légitimité au débat entre monolithe et microservices car les microservices obligent une conception logicielle qui met en avant un concept fondamental : les domaines métier. La conception par domaines n’est pas une pratique stricte et n’impose pas les microservices. La question n’est donc pas “monolithe ou microservices ?”, comme si l’un était archaïque et l’autre forcément moderne. La vraie question est : à quel moment faut-il distribuer son architecture et quand suffit-il de structurer un monolithe par domaines ?

Monolithe et microservices : principes de base

Un monolithe ne veut pas dire un seul serveur. De nombreux monolithes sont d’ailleurs distribués, avec des serveurs dédiés à l’exécution des tâches de fond par exemple. Un monolithe, c’est surtout un système qui se livre comme une unité cohérente. Une même application peut exposer un portail client, une API, un backoffice d’administration et des tâches de fond. Une application monolithique peut aussi être exécutée sur plusieurs serveurs : une instance pour l’API, une autre pour les jobs, plusieurs instances derrière un load balancer. Tant que ces éléments évoluent ensemble, partagent le même cycle de release et supposent une coordination forte au déploiement, on reste dans une logique monolithique.

La base de code ne suffit pas non plus à trancher. Beaucoup de monolithes vivent dans un seul dépôt, mais ce n’est pas une obligation absolue. À l’inverse, un monorepo peut contenir plusieurs services réellement indépendants.

Les microservices, eux, reposent sur une autre promesse : découper le système en capacités métier autonomes. Un microservice n’est pas simplement une API, un backoffice ou un worker lancé sur une machine différente. C’est une partie du système qui possède son propre contrat, son propre cycle de vie, ses propres responsabilités et, idéalement, ses propres frontières de données.

Le concept à saisir dans une architecture orientée microservices est donc l’indépendance de changement et de déploiement. Un ensemble de composants techniques déployés sur plusieurs serveurs ne devient pas automatiquement une architecture microservices. Il le devient quand chaque service peut évoluer, être testé, livré et opéré sans obliger tout le reste du système à bouger en même temps.

Les microservices ne sont pas une architecture anodine

Les microservices peuvent être une excellente réponse à certains problèmes, lorsque des domaines métiers bien définis peuvent être isolés :

  • lorsque plusieurs équipes doivent pouvoir livrer indépendamment,
  • lorsqu’il existe des contraintes de scalabilité ou de disponibilité différentes selon les parties du système.

Les microservices ne sont en rien l’évolution ultime de toute plateforme logicielle, dernier chainon d’un modèle de maturité. Les microservices ne sont pas non plus une garantie de qualité technique.

Les microservices ajoutent surtout une complexité structurelle dès le premier jour : plusieurs applications à construire, plusieurs pipelines à maintenir, plusieurs versions à coordonner, plusieurs contrats d’API à stabiliser, plusieurs sources de logs à recouper. C’est ce que Martin Fowler appelle le Microservice Premium : le coût supplémentaire payé dès que l’on transforme une application en système distribué. Ce coût peut être justifié dans les cas cités précédemment : équipes nombreuses, domaines métier autonomes, contraintes fortes de scalabilité… Mais si cette complexité organisationnelle n’existe pas, ce premium sera payé inutilement, sans aucun retour sur investissement !

Une architecture hautement distribuée demande aussi une maturité d’exploitation et d’observabilité plus élevée. Il faut savoir monitorer des services séparés, suivre des traces distribuées, gérer les erreurs réseau, concevoir des mécanismes de résilience, versionner les contrats, accepter que certaines opérations ne puissent plus être transactionnelles de bout en bout. Chaque service étant petit mais autonome, le système complet devient souvent très difficile à comprendre.

Le monolithe modulaire résout déjà beaucoup de problèmes

Le monolithe souffre d’une mauvaise réputation parce que les monolithes peuvent être des systèmes legacy, vieillissant, avec du code de mauvaise qualité et dont les fonctions de chaque composants sont entremelées (c’est ce qu’on appelle du code spaghetti). Une base de code ingérable, des dépendances partout, des déploiements risqués, des équipes qui se marchent dessus : ce ne sont pas les symptômes naturels du monolithe, ce sont les symptômes d’une dette technique importante, le genre de situation qui donne souvent envie de lancer un replatforming.

Dans un monolithe, on peut cependant construire une approche modulaire par domaines. Il s’agit, très simplement, de conceptualiser les domaines métiers en tant que des modules indépendants, avec des frontières internes claires. On conserve une seule application à livrer, mais le code est organisé autour de domaines métier explicites : commande, paiement, facturation, catalogue, support, administration. Chaque domaine a ses règles, ses objets, ses interfaces internes. On garde la simplicité d’exploitation du monolithe, mais on évite que tout le système devienne un seul bloc mental.

C’est l’intérêt de l’approche Monolith First défendue par Martin Fowler. Au début d’un produit, les frontières métier sont rarement stables. On croit savoir où découper. Puis les usages, les retours client, les arbitrages produit et les contraintes commerciales déplacent les lignes. Dans un monolithe, on peut encore refactorer ces frontières avec un coût raisonnable. Dans des microservices, une mauvaise frontière devient vite un contrat réseau, une dépendance de données et une coordination de déploiement. David Heinemeier Hansson pousse cette idée avec le Majestic Monolith. Le monolithe n’est pas étape obligatoire et honteuse que l’on subit avant de devenir “mature”. C’est un choix assumé : réduire les abstractions inutiles, garder le système intégré, rendre l’équipe plus rapide et réserver la distribution aux cas où elle apporte vraiment quelque chose. C’est pragmatique.

Au final, les pain-points qui conduisent à l’envie de microservices peuvent et devraient être traités autrement :

  • en clarifiant les domaines métier ;
  • en isolant les responsabilités dans le code ;
  • en renforcant les tests autour des règles importantes ;
  • en fiabilisant l’intégration continue ;
  • en améliorant le découpage des équipes autour des modules ;
  • en rendant les dépendances visibles plutôt que les cacher derrière des appels réseau.

On n’a pas besoin de distribuer l’infrastructure pour organiser le métier : on a besoin de discipline, de frontières explicites et d’un système que l’équipe peut encore comprendre dans son ensemble.

La différence entre monolithe modulaire et microservices ne tient donc pas seulement au découpage métier. La vraie bascule se situe dans le mode de déploiement, d’exploitation et de coordination et de la complexité qu’elle engendre.

Distribuer un monolithe sans basculer trop tôt en microservices

Le mot “monolithe” peut aussi donner l’impression d’une seule machine qui ferait tout, ou en tous cas d’un manque de scalabilité. C’est trompeur. Un monolithe peut être distribué efficacement sans devenir une architecture microservices.

On peut exécuter plusieurs instances de la même application derrière un load balancer, isoler les workers de tâches de fond sur des machines dédiées, servir le front depuis un CDN, ajouter du cache, répliquer certaines lectures de base de données ou dimensionner différemment les parties les plus sollicitées. Tout cela permet d’absorber plus de trafic, de fiabiliser les traitements longs et d’éviter qu’un job coûteux ne gêne les requêtes web. Tant que le système reste pensé, versionné et livré comme une unité cohérente, on distribue l’exécution, pas encore le modèle métier.

Le passage aux microservices devient rationnel quand cette distribution technique ne suffit plus. Par exemple lorsqu’un domaine doit être livré par une équipe autonome, suivre une cadence de release différente, posséder ses propres données, tenir des contraintes de disponibilité particulières ou scaler indépendamment du reste. À ce moment-là, l’extraction n’est plus un geste esthétique. C’est une réponse à une contrainte réelle et organisationnelle.

C’est proche de l’idée de la citadelle, toujours par David Heinemeier Hansson : garder un monolithe central robuste, puis extraire quelques services spécialisés autour lorsque le besoin l’impose. On ne fait pas exploser toute l’application. On extrait ce qui a gagné le droit d’être autonome.

Comparaison entre monolithe organisé par domaines et microservices.
Dimension Monolithe organisé par domaines Microservices
Découpage métier Les domaines sont séparés dans le code, avec des frontières internes claires. Les domaines sont séparés dans des services déployés indépendamment.
Déploiement Une seule application à construire, tester et déployer. Plusieurs services, pipelines, versions et dépendances à coordonner.
Refactoring Les frontières peuvent encore bouger relativement facilement. Changer une frontière coûte cher : API, données, contrats et déploiements sont concernés.
Exploitation Logs, monitoring, performance et incidents restent plus simples à suivre. Il faut gérer réseau, latence, traces distribuées, résilience et observabilité fine.
Autonomie des équipes Suffisante tant que l'équipe reste compacte ou bien coordonnée. Utile quand plusieurs équipes doivent livrer indépendamment sur des périmètres distincts.
Scalabilité On scale l'application entière, ce qui suffit dans beaucoup de cas. On peut scaler certains domaines séparément, si le besoin est réel.
Bon moment Par défaut, au démarrage et tant que les douleurs restent maîtrisables. Quand une contrainte concrète justifie le coût d'un système distribué.

Conclusion

Monolithe modulaire et microservices Le schéma compare deux façons d'exploiter des domaines métier : dans un monolithe modulaire avec un cycle de livraison commun, ou dans des microservices avec des cycles de vie séparés. Domaines métier : même point de départ Le choix d'architecture porte surtout sur le cycle de vie et l'exploitation. Domaines. Exemple : Catalogue · Commande · Paiement · Support Les frontières métier doivent exister dans les deux approches. Monolithe modulaire Un cycle de livraison commun Des modules internes par domaine Une exploitation plus lisible Microservices Des cycles de vie séparés Des contrats entre services Une exploitation distribuée complexité contenue autonomie "premium" à justifier

Le bon réflexe n’est pas de choisir entre monolithe et microservices comme entre passé et futur. C’est de construire un monolithe modulaire, organisé par domaines, puis de distribuer uniquement ce qui a une vraie raison technique ou organisationnelle d’être autonome.

Comme nous l’avons vu, l’architecture microservices n’est pas une architecture adaptée à toutes les équipes, elle l’est aux très grandes équipes qui par nature ne souhaitent plus de vision globale sur leur plateforme. C’est un outil puissant pour des organisations complexes et c’est tout.

Pour le reste, la vraie question est de commencer par faire ce qui manque vraiment : clarifier les domaines, maîtriser les dépendances, fiabiliser le déploiement et garder l’architecture au service du produit.

Partager cet article
Robin Komiwes
CTO

Nos publications similaires

Tech

Repenser l'organisation du code et des environnements à l'ère de l'IA

Robin Komiwes
Tech

Déployer des tests automatisés dans une équipe existante

Robin Komiwes
Tech

Tests automatisés : ce qu'ils apportent vraiment et leurs limites

Robin Komiwes

Vous avez un
produit en tête ?

Construisons-le ensemble.