Polydesk-logotype
Polydesk.ai — Header

Microservices

L’architecture microservices est un style d’architecture logicielle qui structure une application comme un ensemble de services petits, autonomes et faiblement couplés, chacun responsable d’une fonctionnalité métier spécifique, déployable et scalable indépendamment.

Au lieu d’un monolithe unique qui fait tout, vous avez des dizaines de services spécialisés qui communiquent entre eux via des API. Le service de paiement, le service utilisateur, le service de recommandation, le service d’inférence ML : chacun a son propre code, sa propre base de données, son propre pipeline de déploiement. Vous pouvez mettre à jour, scaler ou redémarrer un service sans toucher aux autres. C’est cette indépendance qui rend les microservices puissants, et c’est aussi ce qui les rend complexes à opérer.

Microservices en un coup d’œil
Catégorie
Architecture logicielle
Opposé
Architecture monolithique
Principe clé
Un service = une responsabilité métier
Communication
REST, gRPC, messaging async (Kafka, RabbitMQ)
Infra typique
Docker + Kubernetes
Patterns
API Gateway, Circuit breaker, Saga, Event sourcing

Monolithe vs microservices

Dans une architecture monolithique, toutes les fonctionnalités partagent le même code, la même base de données et le même processus de déploiement. Un changement dans le module de paiement impose de re-déployer l’application entière. Un pic de trafic sur la recherche impose de scaler toute l’application, même les parties peu sollicitées.

Aspect Monolithe Microservices
Déploiement L’application entière à chaque changement Service par service, indépendamment
Scaling Tout ou rien (scale l’application entière) Par service (scale uniquement ce qui est nécessaire)
Données Base de données partagée Une base par service (polyglot persistence)
Équipes Une équipe, un repo, un déploiement Petites équipes autonomes par service
Techno Un seul stack technique Polyglot (chaque service choisit sa techno)
Complexité initiale Faible Élevée (infra distribuée, networking, monitoring)
Résilience Un bug peut crasher toute l’application Isolation des pannes (un service tombe, les autres continuent)
Debugging Simple (un seul processus) Complexe (tracing distribué nécessaire)
Le monolithe modulaire : le meilleur des deux mondes ? Avant de migrer vers les microservices, considérez le monolithe modulaire : une seule application déployée, mais avec un code strictement séparé en modules cohésifs et faiblement couplés (par domaine métier). Vous obtenez les bénéfices organisationnels des microservices (frontières claires, développement indépendant) sans la complexité d’un système distribué. C’est souvent le bon point de départ avant de découper en vrais microservices si le besoin s’en fait sentir.

Les principes fondamentaux

Responsabilité unique

Chaque microservice gère une seule capacité métier. Le service de paiement ne gère pas les utilisateurs. Le service de recommandation ne gère pas les commandes. Cette séparation simplifie le développement, les tests et la maintenance de chaque service.

Le Domain-Driven Design (DDD) fournit le cadre pour définir les bonnes frontières. Chaque microservice correspond à un « bounded context » : un domaine métier avec ses propres modèles, son propre vocabulaire et ses propres règles. La clé n’est pas de créer les plus petits services possibles, mais des services cohésifs alignés sur une capacité métier identifiable.

Une base de données par service

Chaque microservice possède et gère ses propres données. Pas de base de données partagée. Cette isolation permet à chaque service d’évoluer indépendamment (changer de schéma sans impacter les autres), de choisir la technologie de stockage la plus adaptée (SQL, NoSQL, cache, file d’attente), et de scaler son stockage séparément.

Le corollaire : les données partagées entre services doivent être obtenues via des appels API ou des événements asynchrones, jamais par un accès direct à la base d’un autre service.

Déploiement indépendant

Chaque service a son propre pipeline CI/CD, sa propre image Docker, et peut être déployé sans toucher aux autres services. C’est le bénéfice principal des microservices : les équipes peuvent livrer des fonctionnalités à leur propre rythme, sans coordination de release globale.

API-First design

L’interface (API) de chaque service est définie et documentée avant le code. C’est le contrat entre les services. Les contrats d’API (OpenAPI/Swagger pour REST, protobuf pour gRPC) permettent aux équipes de développer en parallèle contre une interface stable et prévisible.

Patterns de communication

Communication synchrone (REST, gRPC)

Le service A appelle le service B et attend la réponse. Simple et direct. REST (HTTP/JSON) est le choix par défaut pour les API publiques et les communications inter-services simples. gRPC (HTTP/2, protobuf) est préféré pour les communications internes haute performance (latence plus faible, sérialisation binaire, streaming bidirectionnel).

Risque : si le service B est lent ou indisponible, le service A est bloqué. C’est pourquoi les patterns de résilience (circuit breaker, timeout, retry) sont essentiels.

Communication asynchrone (messaging)

Le service A publie un événement sur un bus de messages (Kafka, RabbitMQ, AWS SNS/SQS). Les services intéressés consomment l’événement de manière indépendante. Cette approche découple les services : le producteur ne sait pas (et ne se soucie pas) qui consomme l’événement.

L’asynchrone est privilégié pour les opérations qui ne nécessitent pas une réponse immédiate : notifications, mise à jour de caches, synchronisation de données entre services, déclenchement de pipelines ML.

API Gateway

L’API Gateway est le point d’entrée unique pour les clients externes. Au lieu d’appeler chaque microservice directement, les clients passent par le gateway qui gère le routing, l’authentification, le rate limiting, le logging et le load balancing. Kong, Envoy, NGINX et les API Gateways cloud (AWS API Gateway, GCP API Gateway) sont les solutions courantes.

Patterns de résilience

Circuit breaker

Quand un service distant est défaillant, le circuit breaker coupe les appels vers ce service après un seuil d’erreurs, plutôt que de continuer à envoyer des requêtes qui échouent. Après un délai, il teste si le service est rétabli. Ce pattern empêche les pannes en cascade : un service lent ne bloque pas tous les services qui en dépendent.

Saga pattern

Les transactions qui touchent plusieurs services ne peuvent pas utiliser un COMMIT classique (pas de transaction distribuée simple). Le pattern Saga orchestre une séquence de transactions locales à chaque service, avec des compensations (rollback) si une étape échoue. Exemple : une commande implique le service de paiement, le service de stock, et le service de livraison. Si le paiement réussit mais le stock est insuffisant, le Saga déclenche un remboursement.

Retry avec backoff exponentiel et timeout

Les appels réseau échouent. C’est normal dans un système distribué. Le pattern retry avec backoff exponentiel (1s, 2s, 4s, 8s) ré-essaie automatiquement avec des délais croissants. Le timeout coupe un appel qui prend trop de temps. Sans timeout, un service bloqué peut paralyser l’appelant.

Microservices et IA/ML

L’architecture microservices est particulièrement adaptée aux applications intégrant de l’IA, car les composants ML ont des exigences très différentes des composants applicatifs classiques :

Le service d’inférence ML nécessite des GPU, scale différemment (proportionnel au volume de requêtes ML, pas au trafic web global), et a des cycles de mise à jour spécifiques (nouveau modèle déployé indépendamment du code applicatif).

Le service de RAG (retrieval-augmented generation) a ses propres exigences : base vectorielle, modèle d’embedding, logique de chunking. Il scale en fonction du volume de documents, pas du trafic utilisateur.

Le service de preprocessing transforme les données d’entrée (images, texte, audio) avant de les envoyer au modèle. Il peut être CPU-bound (traitement de texte) ou GPU-bound (preprocessing d’images).

En séparant ces composants en microservices distincts, vous pouvez scaler le GPU ML indépendamment du CPU applicatif, déployer un nouveau modèle sans toucher à l’API, et utiliser des technologies différentes pour chaque service (Python pour le ML, Go ou Rust pour l’API haute performance).

Architecture event-driven et microservices

L’architecture event-driven est un complément naturel des microservices. Au lieu que les services s’appellent directement, ils publient des événements sur un bus de messages. Les autres services réagissent à ces événements de manière asynchrone.

Deux patterns principaux :

Event notification : un service publie un événement léger (« commande créée, ID=123 ») et les consommateurs vont chercher les détails auprès du service émetteur si nécessaire. Simple mais crée un couplage de données.

Event-carried state transfer : l’événement contient toutes les données nécessaires (« commande créée, ID=123, client=X, produits=[A,B], montant=150€ »). Les consommateurs n’ont pas besoin de rappeler le service émetteur. Plus découplé mais les événements sont plus volumineux.

Apache Kafka est le choix dominant pour l’event streaming à grande échelle : haute durabilité, partitionnement, replay des événements. RabbitMQ convient mieux pour le messaging léger avec routing complexe. AWS SNS/SQS offre une solution managée sans infrastructure à gérer.

Dans les applications IA, l’event-driven est particulièrement utile : un événement « document uploadé » déclenche un pipeline d’indexation (chunking, embedding, stockage vectoriel) sans que le service d’upload ait besoin de connaître le pipeline de RAG. Un événement « drift détecté » peut déclencher automatiquement un pipeline de ré-entraînement.

L’infrastructure microservices

Une architecture microservices repose sur un stack d’infrastructure conséquent :

Composant Rôle Outils courants
Conteneurisation Packaging de chaque service Docker, Podman
Orchestration Déploiement, scaling, gestion des conteneurs Kubernetes, ECS
API Gateway Point d’entrée, routing, auth, rate limiting Kong, Envoy, NGINX, AWS API GW
Service mesh Communication inter-services sécurisée, observabilité Istio, Linkerd, Consul Connect
Messaging Communication asynchrone, event-driven Kafka, RabbitMQ, NATS, AWS SQS
Observabilité Logs, métriques, tracing distribué OpenTelemetry, Datadog, Grafana/Prometheus, Jaeger
CI/CD Build, test, deploy automatisés par service GitHub Actions, GitLab CI, ArgoCD (GitOps)
L’observabilité est non négociable Dans un monolithe, debugger signifie lire les logs d’un seul processus. Dans une architecture microservices, une requête traverse potentiellement 5-10 services. Sans tracing distribué (OpenTelemetry, Jaeger, Zipkin), vous ne pouvez pas comprendre d’où vient un problème de latence ou une erreur. L’observabilité n’est pas un « nice-to-have », c’est un prérequis.

Service mesh : la couche réseau intelligente

Un service mesh est une couche d’infrastructure dédiée à la gestion de la communication inter-services. Au lieu de coder la logique de retry, timeout, circuit breaker et chiffrement TLS dans chaque service, le service mesh l’injecte via un proxy sidecar (un conteneur auxiliaire déployé à côté de chaque service).

Les deux service meshes les plus répandus sont Istio (le plus complet, développé par Google/IBM/Lyft) et Linkerd (plus léger, orienté simplicité). Envoy est le proxy data plane utilisé par Istio et d’autres meshes.

Un service mesh fournit : le chiffrement mutual TLS automatique entre tous les services (zero-trust networking), le load balancing intelligent (circuit breaking, rate limiting, retries), l’observabilité (métriques, logs, traces de chaque appel inter-service sans modification du code), et le traffic management (canary deployments, A/B testing au niveau réseau).

Le service mesh ajoute de la complexité et de la latence (chaque appel passe par un proxy). Il n’est justifié que dans les architectures avec un nombre significatif de services (10+) et des exigences de sécurité ou d’observabilité élevées.

Quand utiliser (ou ne pas utiliser) les microservices

Les microservices sont adaptés quand : votre application est suffisamment complexe pour justifier la séparation (10+ développeurs, multiples domaines métier), vous avez besoin de scaler différemment selon les composants (par exemple, GPU pour le ML, CPU pour l’API), vos équipes sont assez grandes pour que chacune possède un ou plusieurs services, et vous avez l’infrastructure et les compétences pour opérer un système distribué.

Les microservices ne sont pas adaptés quand : votre équipe est petite (< 5 développeurs), votre application est simple, vous n'avez pas l'expertise DevOps pour gérer l'infrastructure, ou vous êtes en phase de prototypage rapide. Dans ces cas, un monolithe (éventuellement modulaire) est plus productif.

L’erreur la plus courante : adopter les microservices trop tôt. Commencez avec un monolithe modulaire. Quand un composant a des besoins de scaling, de déploiement ou de technologie qui divergent du reste, extrayez-le en microservice. C’est l’approche pragmatique recommandée par la majorité des architectes logiciels expérimentés.

Erreurs courantes

Le monolithe distribué. Vous avez découpé en microservices, mais les services sont tellement couplés qu’un changement dans l’un impose des changements dans trois autres. Résultat : la complexité d’un système distribué sans les bénéfices de l’indépendance. Signe : vous ne pouvez pas déployer un service sans déployer les autres.

Trop de services trop tôt. Découper une application en 50 microservices au jour 1 est une erreur. Chaque service ajoute de la complexité opérationnelle (monitoring, déploiement, networking). Commencez avec quelques services bien découpés et affinez.

Communication synchrone partout. Si chaque requête traverse 5 services en cascade synchrone, la latence s’accumule et la disponibilité chute (si un service a 99,9% d’uptime, 5 en cascade donnent 99,5%). Utilisez l’asynchrone pour tout ce qui n’a pas besoin d’une réponse immédiate.

Pas de tracing distribué. Sans observabilité, debugger un problème dans une architecture microservices est un cauchemar. Investissez dans OpenTelemetry et un backend de tracing avant de mettre en production.

Base de données partagée. Si vos microservices partagent une base de données, ce ne sont pas des microservices. Vous avez un monolithe distribué avec tous les inconvénients des deux approches.


Questions fréquentes sur les microservices

Quelle est la différence entre microservices et API ?

Un microservice est un service autonome qui implémente une fonctionnalité métier. Une API est l’interface que ce service expose pour communiquer avec d’autres services ou clients. Chaque microservice expose une ou plusieurs API (REST, gRPC, GraphQL), mais une API peut aussi être exposée par un monolithe. L’API est le contrat, le microservice est l’implémentation.

Quelle technologie de communication choisir entre microservices ?

Pour les appels requête/réponse internes à haute performance, gRPC est le choix recommandé (sérialisation binaire, typage fort, streaming). Pour les API publiques ou simples, REST (HTTP/JSON) est universellement compris. Pour les opérations qui ne nécessitent pas de réponse immédiate, la communication asynchrone via messaging (Kafka, RabbitMQ) découple les services et améliore la résilience. En pratique, la plupart des architectures combinent les trois selon les cas d’usage.

Les microservices sont-ils adaptés aux applications IA ?

Oui, particulièrement bien. Les composants ML (inférence, preprocessing, RAG) ont des exigences de scaling et de hardware (GPU) très différentes des composants applicatifs. Les séparer en microservices permet de scaler le serving ML indépendamment, de déployer de nouvelles versions du modèle sans toucher à l’application, et d’utiliser des frameworks différents (Python pour le ML, Go pour l’API). KServe et BentoML facilitent le déploiement de microservices d’inférence sur Kubernetes.

Combien de microservices faut-il ?

Il n’y a pas de nombre magique. Le bon découpage dépend de votre domaine métier, de la taille de vos équipes, et des besoins de scaling. La règle pratique : un microservice par bounded context (domaine métier identifiable). Amazon a des milliers de microservices, mais la plupart des applications n’en ont pas besoin de plus de 5 à 20. Si vous ne savez pas combien en faire, vous en faites probablement trop. Commencez petit.

Comment migrer d’un monolithe vers les microservices ?

L’approche recommandée est le « strangler fig pattern » : au lieu de réécrire le monolithe d’un coup, vous extrayez progressivement des fonctionnalités en microservices. Commencez par le composant qui bénéficierait le plus de l’indépendance (par exemple, le service ML qui a besoin de GPU). Placez un API gateway devant le monolithe, routez les requêtes vers le nouveau microservice pour les fonctionnalités migrées, et vers le monolithe pour le reste. Répétez jusqu’à ce que le monolithe soit suffisamment réduit ou vidé.

Polydesk.ai — Footer