Craftmanship · Fiche 11

Architecture Microservices

Décomposer une application en services autonomes : stratégies de découpage, patterns de communication et patterns essentiels (API Gateway, Circuit Breaker, Saga).

L’architecture microservices décompose une application en services autonomes et indépendants.

Qu’est-ce qu’une architecture microservices ?

C’est une approche architecturale qui structure une application comme un ensemble de services faiblement couplés.

🏗️ Définition

Principe fondamental : décomposer une application monolithique en plusieurs services indépendants, chacun responsable d’une fonctionnalité métier spécifique.

Caractéristiques clés :

  • Services autonomes et déployables indépendamment
  • Communication via APIs (HTTP/REST, gRPC, messaging)
  • Chaque service a sa propre base de données
  • Équipes organisées autour des services
  • Décentralisation des données et de la gouvernance

📊 Comparaison monolithe vs microservices

MONOLITHE                     MICROSERVICES
┌─────────────────────────┐   ┌─────┐ ┌─────┐ ┌─────┐
│                         │   │User │ │Order│ │Pay  │
│    Application          │   │Mgmt │ │Mgmt │ │Mgmt │
│                         │   └──┬──┘ └──┬──┘ └──┬──┘
│  ┌─────┐ ┌─────┐ ┌───┐  │      │       │       │
│  │User │ │Order│ │Pay│  │   ┌──▼──┐ ┌──▼──┐ ┌──▼──┐
│  │Mgmt │ │Mgmt │ │Mgt│  │   │ DB  │ │ DB  │ │ DB  │
│  └─────┘ └─────┘ └───┘  │   │User │ │Order│ │Pay  │
│                         │   └─────┘ └─────┘ └─────┘
│      ┌─────────────┐    │
│      │  Database   │    │   ✅ Indépendance technologique
│      │             │    │   ✅ Scalabilité granulaire
│      └─────────────┘    │   ✅ Déploiement indépendant
└─────────────────────────┘   ✅ Résilience isolée

Comment faire la séparation des services ?

Plusieurs stratégies existent pour décomposer un monolithe en microservices.

Séparation fonctionnelle (recommandée)

🎯 Par domaine métier (DDD) : chaque service correspond à un Bounded Context.

Exemple e-commerce :

  • Service Catalogue (produits, catégories)
  • Service Commandes (panier, checkout)
  • Service Utilisateurs (profils, authentification)
  • Service Paiements (transactions, facturation)

👥 Par capacité métier : organisé autour des capacités de l’organisation.

Exemple banque :

  • Service Comptes (gestion des comptes)
  • Service Crédits (prêts, hypothèques)
  • Service Investissements (portefeuilles)

📊 Par flux de données : suivre le flux des données métier. Principe : minimiser les communications inter-services.

Séparation technique (à éviter)

❌ Par couche technique : séparation UI, Business Logic, Data Access.

Problèmes :

  • Couplage fort entre services
  • Les changements métier impactent plusieurs services
  • Difficile à maintenir et faire évoluer

⚠️ Par technologie : service Java, service Node.js, service Python. Problème : ne respecte pas les frontières métier.

💡 Critères de bonne séparation

  • Cohésion forte : fonctionnalités liées dans le même service
  • Couplage faible : minimum d’interactions entre services
  • Autonomie : l’équipe peut développer/déployer indépendamment
  • Responsabilité unique : un service = un domaine métier
  • Données privées : chaque service possède ses données
  • Évolutivité : peut évoluer sans impacter les autres

Avantages des microservices

🚀 Agilité et développement

  • Déploiement indépendant : chaque service peut être déployé séparément
  • Équipes autonomes : équipes spécialisées par domaine métier
  • Cycles de release rapides : livraisons fréquentes et indépendantes
  • Innovation technologique : choix de stack technique par service

⚡ Performance et scalabilité

  • Scalabilité granulaire : scaler uniquement les services nécessaires
  • Résilience : isolation des pannes, fault tolerance
  • Optimisation ciblée : performance adaptée par service
  • Distribution géographique : services déployés près des utilisateurs

🔧 Maintenance et évolution

  • Codebase plus petite : plus facile à comprendre et maintenir
  • Refactoring localisé : changements isolés dans un service
  • Remplacement progressif : migration service par service

👥 Organisation

  • Ownership claire : responsabilité définie par équipe
  • Spécialisation : expertise métier par équipe
  • Parallélisation : développement simultané de plusieurs services

Défis et complexités des microservices

🌐 Complexité opérationnelle

  • 🔧 Gestion d’infrastructure : multiplication des services à déployer et monitorer. Impact : besoin d’automatisation (Docker, Kubernetes, CI/CD).
  • 📊 Monitoring distribué : traçabilité des requêtes à travers plusieurs services. Solution : distributed tracing, logging centralisé.
  • 🔄 Gestion des versions : compatibilité entre versions de services. Approche : versioning d’APIs, backward compatibility.

🔗 Complexité de communication

  • 🌐 Latence réseau : communications inter-services plus lentes. Mitigation : caching, async messaging, optimisation réseau.
  • 🔄 Transactions distribuées : pas de transactions ACID globales. Pattern : Saga Pattern, eventual consistency.
  • 🔍 Service discovery : comment les services se trouvent-ils ? Outils : Consul, Eureka, Kubernetes DNS.

💾 Gestion des données

  • 🗄️ Consistance des données : données distribuées, pas de cohérence immédiate. Approche : eventual consistency, CQRS.
  • 🔄 Synchronisation : maintenir la cohérence entre services. Pattern : event sourcing, domain events.

👥 Défis organisationnels

  • 🎯 Coordination d’équipes : communication entre équipes autonomes. Solution : API contracts, documentation, governance.
  • 📈 Courbe d’apprentissage : compétences DevOps, distributed systems. Besoin : formation, expertise technique avancée.

Patterns de communication

Comment les microservices communiquent-ils entre eux ?

🔄 Communication synchrone

🌐 HTTP/REST : communication via APIs REST.

GET /api/users/123
POST /api/orders
{
  "userId": 123,
  "items": [{"productId": 456, "quantity": 2}]
}

Avantages : simple, standard, debugging facile.

⚡ gRPC : communication haute performance avec Protocol Buffers.

service UserService {
  rpc GetUser(UserRequest) returns (UserResponse);
  rpc CreateUser(CreateUserRequest) returns (UserResponse);
}

Avantages : performance, type safety, streaming.

📨 Communication asynchrone

📬 Message queues : communication via files de messages.

// Producer
await queue.send('order.created', {
  orderId: '123',
  userId: '456',
  amount: 99.99
});

// Consumer
queue.subscribe('order.created', (message) => {
  // Process order
});

Outils : RabbitMQ, Apache Kafka, AWS SQS.

🎯 Event-driven : communication via événements métier.

// Event
{
  "eventType": "UserRegistered",
  "userId": "123",
  "email": "user@example.com",
  "timestamp": "2024-01-01T10:00:00Z"
}

// Multiple services can react to this event

Avantages : découplage, scalabilité, résilience.

🎯 Quand utiliser quoi ?

Communication synchrone :

  • Requêtes en temps réel
  • Validation immédiate
  • Opérations critiques
  • Interactions utilisateur

Communication asynchrone :

  • Traitement en arrière-plan
  • Notifications
  • Intégrations externes
  • Événements métier

Patterns architecturaux essentiels

API Gateway

Point d’entrée unique pour toutes les requêtes clients vers les microservices.

Responsabilités :

  • Routage des requêtes
  • Authentification/autorisation
  • Rate limiting
  • Monitoring et logging
  • Transformation de données
┌─────────────┐
│   Client    │
└──────┬──────┘

┌──────▼──────┐
│ API Gateway │ ◄─── Single Entry Point
└──────┬──────┘

   ┌───▼───┐┌───────┐┌───────┐
   │Service││Service││Service│
   │   A   ││   B   ││   C   │
   └───────┘└───────┘└───────┘

Outils : Kong, Zuul, AWS API Gateway

Service Discovery

Mécanisme permettant aux services de se localiser dynamiquement.

Approches :

  • Client-side : le client interroge le registry
  • Server-side : load balancer + registry
  • Service mesh : l’infrastructure gère la découverte
┌─────────────┐
│   Service   │
│ Discovery   │ ◄─── Registry
│   (Consul)  │
└──────┬──────┘

   ┌───▼───┐┌───────┐┌───────┐
   │Service││Service││Service│
   │   A   ││   B   ││   C   │
   └───┬───┘└───┬───┘└───┬───┘
       │        │        │
       └────────┼────────┘

         Register/Discover

Outils : Consul, Eureka, etcd

Circuit Breaker

Prévient les cascades de pannes en isolant les services défaillants.

États :

  • Closed : les requêtes passent normalement
  • Open : requêtes bloquées, retour immédiat
  • Half-Open : test de récupération
// Exemple avec Hystrix/Resilience4j
@CircuitBreaker(name = "userService")
public User getUser(String userId) {
    return userServiceClient.getUser(userId);
}
# Configuration
circuitbreaker:
  instances:
    userService:
      failure-rate-threshold: 50
      wait-duration-in-open-state: 30s
      sliding-window-size: 10

À retenir : le cycle des états est CLOSED → OPEN → HALF_OPEN → CLOSED.

Saga Pattern

Gère les transactions distribuées via une séquence de transactions locales.

Types :

  • Choreography : les services se coordonnent via des événements
  • Orchestration : un orchestrateur central pilote la saga

Compensation : annulation en cas d’échec.

// Saga Orchestration - Commande E-commerce
1. Reserve Inventory    ✅
2. Process Payment      ✅
3. Create Shipment      ❌ FAIL

// Compensation (rollback)
3. Cancel Shipment      ✅
2. Refund Payment       ✅
1. Release Inventory    ✅

// Choreography - Event-driven
OrderCreated → ReserveInventory
InventoryReserved → ProcessPayment
PaymentProcessed → CreateShipment

Distributed Tracing avec X-Correlation-ID

Mécanisme permettant de suivre une requête à travers tous les microservices impliqués.

Principe :

  • Propager un identifiant unique (X-Correlation-ID) à travers tous les services
  • Chaque service ajoute cet ID dans ses logs
  • Permet de reconstituer le parcours complet d’une requête
  • Essentiel pour le debugging et l’analyse de performance
// 1. Service Gateway - Génération de l'ID
@Component
public class CorrelationIdFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        String correlationId = UUID.randomUUID().toString();
        MDC.put("X-Correlation-ID", correlationId);
        ((HttpServletResponse) response).addHeader("X-Correlation-ID", correlationId);

        // Propagation vers le service suivant
        chain.doFilter(request, response);
    }
}

// 2. Service B - Propagation
@Component
public class TracingInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                                        ClientHttpRequestExecution execution) throws IOException {
        String correlationId = MDC.get("X-Correlation-ID");
        request.getHeaders().set("X-Correlation-ID", correlationId);

        // Log avec l'ID de corrélation
        log.info("Processing request {}", correlationId);

        return execution.execute(request, body);
    }
}

💡 Patterns complémentaires

  • CQRS : séparation Command/Query
  • Event Sourcing : stockage des événements
  • Bulkhead : isolation des ressources
  • Retry Pattern : tentatives automatiques
  • Timeout : limitation du temps d’attente
  • Health Check : monitoring de santé

✅ Recommandations pour réussir

Conseils pratiques pour une migration réussie vers les microservices.

🎯 Stratégie de migration

  • Commencer par un monolithe : comprendre le domaine avant de découper
  • Migration progressive : Strangler Fig Pattern, service par service
  • Automatisation first : CI/CD, monitoring, infrastructure as code

⚠️ Pièges à éviter

  • Distributed monolith : services trop couplés, déploiement synchrone
  • Microservices trop petits : overhead de communication trop important
  • Ignorer la culture DevOps : équipes pas prêtes pour l’autonomie

Piège d’entretien : le « distributed monolith » — des services qui doivent être déployés ensemble et s’appellent en cascade — cumule les inconvénients des deux mondes : la complexité des microservices sans leur indépendance.

Interro écrite

8 questions pour valider la fiche. Une seule réponse par question.

Question 1/8Quelle caractéristique est fondamentale dans une architecture microservices ?