Monolithe vs Microservices

Comparaison entre l'architecture monolithique et les microservices : avantages, inconvénients, quand utiliser chacune et considérations de migration.

Deux approches architecturales distinctes

Lorsque tu conçois un système, l’une des premières décisions consiste à choisir comment organiser le code et les processus. Les deux extrémités du spectre sont l’architecture monolithique et les microservices. Aucune n’est intrinsèquement meilleure ; chacune résout des problèmes différents dans des contextes différents.

Qu’est-ce qu’un monolithe ?

Un monolithe est une application qui regroupe plusieurs capacités du système au sein d’une unité de déploiement unique.

Cela ne signifie pas nécessairement qu’il est mal conçu. Un monolithe peut être bien structuré en interne, avec des modules, des couches et des frontières claires. L’important est que, du point de vue opérationnel, il se déploie comme une seule application.

Structure typique

mi-aplicacion/
├── src/
│   ├── controllers/    # Endpoints HTTP
│   ├── services/       # Lógica de negocio
│   ├── models/         # Modelos de datos
│   ├── repositories/   # Acceso a base de datos
│   └── utils/          # Utilidades compartidas
├── database/
│   └── migrations/
└── package.json

Que sont les microservices ?

Les microservices découpent le système en services plus petits, chacun responsable d’un domaine ou d’une capacité précise.

Chaque service possède généralement :

  • Sa propre logique métier
  • Son déploiement indépendant
  • Sa persistance ou son accès aux données propre
  • Ses contrats d’entrée et de sortie

L’idée n’est pas de « créer de petits services pour le plaisir », mais de définir des frontières qui permettent de faire évoluer des parties du système avec un couplage moindre.

Structure typique

sistema/
├── servicio-usuarios/
│   ├── src/
│   ├── Dockerfile
│   └── package.json
├── servicio-pedidos/
│   ├── src/
│   ├── Dockerfile
│   └── package.json
├── servicio-pagos/
│   ├── src/
│   ├── Dockerfile
│   └── package.json
└── api-gateway/
    ├── src/
    └── package.json

Avantages du monolithe

Simplicité initiale

Un projet unique est généralement plus facile à démarrer, à déployer et à déboguer. Tu n’as pas besoin de résoudre dès le premier jour des questions comme la messagerie, le traçage distribué ou les déploiements coordonnés.

Moindre complexité opérationnelle

Tu n’as pas besoin d’orchestration de conteneurs, de service discovery, de supervision distribuée ni de gestion de la cohérence à terme dès le départ.

Plus facile pour les petites équipes

Lorsque l’équipe est réduite (2 à 8 personnes), il est souvent plus efficace de travailler sur une base de code unique et bien organisée.

Transactions ACID

Les opérations de base de données sont simples car tout partage la même connexion. Tu n’as pas besoin de patrons comme Saga ou Outbox pour maintenir la cohérence.

Plus simple à tester de bout en bout

L’intégration entre modules se fait en mémoire ou au sein d’un même processus, ce qui réduit les frictions lors du test de flux complets.

Latence interne moindre

Les appels entre modules sont des appels de fonction en mémoire, et non des appels réseau. Cela élimine la latence inhérente à la communication entre services.

Inconvénients du monolithe

Couplage croissant

En l’absence de discipline interne, le système peut devenir difficile à maintenir. Avec le temps, les modules ont tendance à s’entremêler et les dépendances deviennent difficiles à retracer.

Déploiement unique

Modifier une partie oblige à déployer toute l’application. Un changement dans un module nécessite de redéployer l’ensemble du système, ce qui augmente le risque.

Mise à l’échelle moins granulaire

Tu ne peux pas mettre à l’échelle uniquement la partie qui reçoit le plus de charge. Tu dois mettre à l’échelle toute l’application même si un seul module a besoin de plus de ressources.

Moindre autonomie organisationnelle

Avec plusieurs équipes, coordonner les changements peut devenir coûteux. Toute l’équipe doit utiliser le même langage et le même framework.

Avantages des microservices

Séparation par domaines

Cela permet d’isoler les capacités métier et leurs règles. Chaque service encapsule un bounded context avec son propre modèle de données.

Déploiements indépendants

Chaque service peut évoluer à son rythme sans affecter les autres. Une équipe peut déployer son service sans se coordonner avec les autres équipes.

Scalabilité plus granulaire

Tu peux mettre à l’échelle uniquement ce qui en a réellement besoin. Si le service de recherche reçoit 10x plus de trafic que celui de facturation, tu ne mets à l’échelle que la recherche.

Équipes plus autonomes

Chaque équipe peut être responsable d’un bounded context ou d’un domaine complet, en prenant ses décisions techniques de manière indépendante.

Isolation des pannes

Si un service tombe, les autres peuvent continuer à fonctionner (avec une conception adéquate). Cela améliore la résilience globale du système.

Flexibilité technologique

Dans certains cas, cela permet de choisir des technologies différentes selon le service. Un service peut utiliser Node.js tandis qu’un autre utilise Go, selon les besoins.

Inconvénients des microservices

Complexité opérationnelle

Des problèmes apparaissent qui n’existent pas dans un monolithe :

  • Découverte de services
  • Observabilité distribuée
  • Réessais et idempotence
  • Pannes réseau
  • Versionnage des contrats
  • Orchestration de conteneurs

Latence réseau

Chaque appel entre services passe par le réseau, ce qui ajoute de la latence. Un flux qui, dans un monolithe, est un appel de fonction peut, en microservices, impliquer plusieurs sauts réseau.

Cohérence plus complexe

Les transactions distribuées ne sont pas triviales. Tu dois accepter la cohérence à terme dans de nombreux cas et utiliser des patrons comme Saga pour coordonner les opérations entre services.

Débogage difficile

Retracer une erreur à travers plusieurs services nécessite des outils de tracing distribué comme Jaeger ou Zipkin. Sans eux, diagnostiquer les problèmes est extrêmement difficile.

Plus d’effort de gouvernance

Il faut définir des standards techniques, l’ownership et les règles d’intégration. Sans gouvernance, chaque équipe peut prendre des décisions incompatibles.

Duplication de code

La logique commune peut finir par être dupliquée entre les services si elle n’est pas correctement gérée avec des shared kernels ou des bibliothèques partagées.

Davantage d’infrastructure

Tu as besoin de pipelines de CI/CD, de déploiement automatisé, de supervision et d’une exploitation plus matures dès le départ.

Comparaison directe

CritèreMonolitheMicroservices
Complexité initialeFaibleÉlevée
ScalabilitéVerticale (plus de ressources)Horizontale (plus d’instances)
DéploiementTout ensembleIndépendant par service
Cohérence des donnéesACID natifCohérence à terme
Taille d’équipe idéale2 à 8 personnesPlusieurs équipes (8+)
Time to market initialRapideLent
Coût opérationnelFaibleÉlevé
Autonomie des équipesLimitéeÉlevée
Isolation des pannesFaibleÉlevée
Flexibilité technologiqueUne seule stackPlusieurs stacks possibles

Laquelle est la meilleure ?

La bonne question n’est pas « laquelle est la meilleure en général », mais :

  • Laquelle répond le mieux à mes besoins actuels ?
  • Laquelle puis-je exploiter avec l’équipe dont je dispose ?
  • Quel niveau de complexité métier dois-je gérer ?
  • Ai-je besoin de déploiements réellement indépendants ?
  • Ai-je la maturité technique pour la soutenir ?

Choisis le monolithe lorsque

  • Tu démarres un nouveau projet et que le domaine n’est pas encore complètement défini.
  • Ton équipe est petite (moins de 8 personnes).
  • Tu dois arriver rapidement sur le marché (MVP).
  • Le système ne nécessite pas de mise à l’échelle différenciée par module.
  • Tu n’as pas d’expérience dans l’exploitation d’une infrastructure distribuée.

Choisis les microservices lorsque

  • Le système a grandi et les équipes se marchent sur les pieds lorsqu’elles font des changements.
  • Tu dois mettre à l’échelle des modules spécifiques de manière indépendante.
  • Tu disposes d’équipes autonomes capables d’être propriétaires de services complets.
  • Le domaine métier a des frontières claires entre les contextes (bounded contexts).
  • Tu disposes de l’infrastructure et de l’expérience pour exploiter des systèmes distribués.

La voie intermédiaire : le monolithe modulaire

Dans de nombreux cas, un monolithe modulaire est une excellente solution de départ. Et, dans certains contextes, il peut le rester pendant longtemps.

C’est un monolithe doté de frontières internes bien définies entre les modules :

mi-aplicacion/
├── modules/
│   ├── usuarios/
│   │   ├── controllers/
│   │   ├── services/
│   │   └── models/
│   ├── pedidos/
│   │   ├── controllers/
│   │   ├── services/
│   │   └── models/
│   └── pagos/
│       ├── controllers/
│       ├── services/
│       └── models/
└── shared/
    └── utils/

Chaque module possède sa propre structure interne et communique avec les autres modules via des interfaces bien définies. Cela te procure de nombreux bénéfices des microservices (faible couplage, forte cohésion) sans la complexité opérationnelle.

Évolution réaliste

Une erreur courante consiste à vouloir démarrer avec des microservices trop tôt. Cela crée généralement plus de complexité technique que de valeur réelle.

Une évolution plus saine consiste souvent à :

  1. Commencer avec un système simple
  2. Bien le modulariser
  3. Identifier les domaines et les frontières réels
  4. Séparer uniquement lorsqu’il existe une pression réelle pour le faire

Autrement dit : d’abord comprendre le domaine, puis décider de la forme architecturale.

Patron Strangler Fig

Si tu décides de migrer d’un monolithe vers des microservices, ne réécris pas tout d’un coup. Extrais les fonctionnalités progressivement :

  1. Identifie un module aux frontières claires.
  2. Crée un nouveau service qui réplique cette fonctionnalité.
  3. Redirige le trafic vers le nouveau service.
  4. Supprime le code du monolithe une fois le service stabilisé.

Erreurs courantes lors de la migration

  • Microservices trop petits : si un service ne peut pas fonctionner sans appeler constamment les autres, il est probablement trop petit.
  • Base de données partagée : si deux services partagent la même base de données, ils ne sont pas réellement indépendants.
  • Migrer sans automatisation : sans CI/CD, supervision et orchestration, les microservices deviennent ingérables.

Résumé

Monolithe et microservices ne sont pas une question de « bien vs mal ». Ce sont des stratégies différentes pour organiser un système. Le monolithe offre généralement une simplicité initiale ; les microservices offrent séparation et autonomie au prix d’une plus grande complexité opérationnelle.

La meilleure architecture est celle qui résout ton problème actuel sans créer de problèmes futurs inutiles. Commence simple, mesure, et fais évoluer lorsque les données le justifient.