← Retour au blog

De monolithe à microservices : 7 erreurs qui ont coûté 6 mois

Ce qu'on ne vous dit pas sur la migration vers les microservices. Retour d'expérience réel sur une refonte complète.

Pourquoi nous avons migré (et pourquoi nous ne le referions pas pareil)

Mars 2023 : une startup fintech suisse me contacte pour moderniser leur monolithe Rails. 85,000 lignes de code, 4 ans d’existence, scaling difficile.

Décision : migration vers une architecture microservices.
Estimation : 4 mois
Réalité : 10 mois
Surcoût : ~340,000 CHF

Voici les 7 erreurs majeures qui ont fait exploser le planning, et comment les éviter.

Erreur #1 : Trop de services, trop vite

Ce qu’on a fait (mal)

Architecture cible initiale : 23 microservices

auth-service
user-service
profile-service        ← Auraient dû rester ensemble
notification-service
email-service         ← Idem
sms-service          ← Idem
payment-service
invoice-service
transaction-service
...

Résultat

Ce qu’on aurait dû faire

Règle des bounded contexts : 1 microservice = 1 domaine métier clairement isolé

Architecture corrigée : 6 microservices

auth-service          # Authentification/autorisation
user-service          # Users + profiles (même contexte)
communication-service # Email + SMS + notifications
payment-service       # Paiements + invoices + transactions
analytics-service     # Tracking + reporting
admin-service         # Backoffice

Indicateur : si 2 services communiquent de manière synchrone > 10x/seconde, ils devraient être fusionnés.

Erreur #2 : Base de données partagée

Ce qu’on a fait (mal)

┌──────────────┐     ┌──────────────┐
│ User Service │────▶│              │
└──────────────┘     │   Postgres   │◀────┌────────────────┐
┌──────────────┐     │   (shared)   │     │ Payment Service│
│ Auth Service │────▶│              │     └────────────────┘
└──────────────┘     └──────────────┘

Problèmes rencontrés

Ce qu’on aurait dû faire

Database per service

┌──────────────┐     ┌─────────────┐
│ User Service │────▶│ Postgres    │
└──────────────┘     │ (users DB)  │
                     └─────────────┘
┌──────────────┐     ┌─────────────┐
│ Auth Service │────▶│ Redis       │
└──────────────┘     │ (sessions)  │
                     └─────────────┘

Pattern pour dupliquer les données : Event sourcing + CQRS

// User service publie un event
await eventBus.publish('user.created', {"{"}
  userId: user.id,
  email: user.email,
  name: user.name
{"}"});

// Auth service écoute et stocke sa copie
eventBus.subscribe('user.created', async (data) => {"{"}
  await authDB.users.create({"{"}
    id: data.userId,
    email: data.email,
    // Uniquement les champs nécessaires à l'auth
  {"}"});
{"}"});

Erreur #3 : Communication synchrone partout

Ce qu’on a fait (mal)

Scénario : création d’un paiement

// Payment service
async function createPayment(data) {"{"}
  // 1. Vérifier l'user (HTTP call)
  const user = await fetch('http://user-service/users/' + data.userId);
  
  // 2. Vérifier KYC (HTTP call)
  const kyc = await fetch('http://kyc-service/verify/' + data.userId);
  
  // 3. Créer la transaction (HTTP call)
  const transaction = await fetch('http://transaction-service/create', {"{"}
    method: 'POST',
    body: JSON.stringify(data)
  {"}"});
  
  // 4. Envoyer notification (HTTP call)
  await fetch('http://notification-service/send', {"{"}
    method: 'POST',
    body: JSON.stringify({"{"}
      userId: data.userId,
      type: 'payment_created'
    {"}"})
  {"}"});
  
  return transaction;
{"}"}
// Latence totale : 450ms + risque de cascade failure

Problème : si notification-service est down → paiement échoue

Ce qu’on aurait dû faire

Pattern : synchrone uniquement pour les données critiques, asynchrone pour le reste

async function createPayment(data) {"{"}
  // 1. Vérifier user (sync - critique)
  const user = await userService.getUser(data.userId);
  if (!user) throw new Error('User not found');
  
  // 2. Vérifier KYC (sync - critique)
  const isVerified = await kycService.verify(data.userId);
  if (!isVerified) throw new Error('KYC not verified');
  
  // 3. Créer le paiement en DB locale
  const payment = await db.payments.create(data);
  
  // 4. Publier event (async - non bloquant)
  await eventBus.publish('payment.created', payment);
  
  return payment;
{"}"}
// Latence : 120ms
// Notification-service écoute l'event et envoie en background

Erreur #4 : Pas de circuit breaker

Le problème

Scénario réel (août 2023)

La solution

Circuit breaker pattern

import CircuitBreaker from 'opossum';

const kycBreaker = new CircuitBreaker(kycService.verify, {"{"}
  timeout: 3000,           // Timeout après 3s
  errorThresholdPercentage: 50,  // Ouvre si > 50% d'erreurs
  resetTimeout: 30000      // Réessaye après 30s
{"}"});

kycBreaker.fallback(() => {"{"}
  // Fallback si circuit ouvert
  return {"{"}
    verified: false,
    reason: 'KYC service unavailable'
  {"}"};
{"}"});

// Utilisation
const result = await kycBreaker.fire(userId);

Avec Istio (service mesh)

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: kyc-service
spec:
  host: kyc-service
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 50
        maxRequestsPerConnection: 2
    outlierDetection:
      consecutiveErrors: 5
      interval: 30s
      baseEjectionTime: 30s

Erreur #5 : Logs dispersés, debugging impossible

Le problème

Bug en production : un paiement échoue

Où chercher les logs ?

payment-service logs → "Error calling transaction-service"
transaction-service logs → "Invalid user_id"
user-service logs → "User deleted by admin"
admin-service logs → ...

45 minutes pour reconstituer le flow.

La solution

Distributed tracing + correlation ID

// API Gateway génère un correlation ID
app.use((req, res, next) => {"{"}
  req.correlationId = req.headers['x-correlation-id'] || uuidv4();
  res.setHeader('x-correlation-id', req.correlationId);
  next();
{"}"});

// Chaque service propage le correlation ID
async function callService(url, data, context) {"{"}
  return fetch(url, {"{"}
    headers: {"{"}
      'x-correlation-id': context.correlationId
    {"}"},
    body: JSON.stringify(data)
  {"}"});
{"}"}

// Logs structurés
logger.info('Payment created', {"{"}
  correlationId: req.correlationId,
  userId: payment.userId,
  amount: payment.amount,
  service: 'payment-service'
{"}"});

Stack complète

Requête Elasticsearch

{"{"}
  "query": {"{"}
    "match": {"{"}
      "correlationId": "abc-123-def-456"
    {"}"}
  {"}"}
{"}"}
// Tous les logs de tous les services pour cette requête

Erreur #6 : Pas de stratégie de rollback

Le problème

Déploiement payment-service v2.0

La solution

1. API versioning

// v1 (deprecated mais toujours disponible)
app.post('/v1/payments', oldPaymentHandler);

// v2 (nouvelle version)
app.post('/v2/payments', newPaymentHandler);

// Header-based (alternative)
app.post('/payments', (req, res) => {"{"}
  const version = req.headers['api-version'] || 'v1';
  if (version === 'v2') return newPaymentHandler(req, res);
  return oldPaymentHandler(req, res);
{"}"});

2. Blue-Green deployment

# Kubernetes
apiVersion: v1
kind: Service
metadata:
  name: payment-service
spec:
  selector:
    app: payment-service
    version: v2  # Switch instantané v1 ↔ v2

3. Feature flags

if (featureFlags.isEnabled('new-payment-flow', userId)) {"{"}
  return newPaymentFlow(data);
{"}"}
return oldPaymentFlow(data);

Erreur #7 : Sous-estimer la complexité DevOps

Monolithe (1 app)

Microservices (6 services)

Budget DevOps : multiplié par 4.

Quand migrer vers les microservices ?

✅ Vous devriez envisager les microservices si :

❌ Restez monolithe si :

Alternative : monolithe modulaire (modules découplés dans 1 app).

Checklist avant de migrer

Business
[ ] ROI justifié (pas juste "c'est moderne")
[ ] Équipe prête (DevOps solide)

Architecture
[ ] Bounded contexts identifiés
[ ] Stratégie de communication définie
[ ] Database strategy claire

Infrastructure
[ ] Orchestration (Kubernetes)
[ ] Service mesh envisagé
[ ] Observability (logs, traces, metrics)

Équipe
[ ] Compétences microservices
[ ] Culture DevOps mature
[ ] Processes de déploiement rodés

Vous envisagez une migration vers les microservices ? Contactez-moi pour un audit d’architecture.