Défis d'Architecture

Des défis avancés qui combinent plusieurs concepts — conçois des solutions complètes pour des scénarios complexes du monde réel.

Objectif

Les défis de cette section combinent plusieurs concepts d’architecture dans des scénarios complexes. Contrairement aux laboratoires précédents (qui se concentrent sur un seul sujet), tu devras ici intégrer des patterns, de la résilience, de la sécurité et des opérations dans des solutions complètes.

Chaque défi possède un niveau de difficulté et une durée estimée. Essaie de les résoudre avant de consulter les indices.


Défi 1 : Migration sans interruption de service

Niveau : Intermédiaire | Durée estimée : 30-45 minutes

Scénario

Ton entreprise possède un monolithe qui gère l’authentification, le catalogue et les commandes. Tu dois extraire le service de commandes sous forme de microservice sans interruption de service. Le monolithe traite 500 commandes par heure et ne peut pas être arrêté.

Exigences

  1. Zéro interruption de service pendant la migration
  2. Capacité de rollback à tout moment
  3. Les données historiques des commandes doivent être disponibles dans le nouveau service
  4. Les rapports existants doivent continuer à fonctionner

Ta mission

Conçois un plan de migration étape par étape. Prends en compte :

  • Comment migrer les données sans arrêter le monolithe ?
  • Comment rediriger le trafic progressivement ?
  • Comment vérifier que le nouveau service fonctionne correctement ?
  • Quel est ton plan de rollback ?

Indices

Indice 1 : Pattern de migration

Utilise le pattern Strangler Fig :

  1. Crée le nouveau service de commandes
  2. Redirige d’abord les lectures (moins de risque)
  3. Redirige ensuite les écritures
  4. Enfin, supprime le code des commandes du monolithe
Indice 2 : Migration des données

Utilise le Change Data Capture (CDC) :

  1. Fais un dump initial des commandes historiques vers le nouveau service
  2. Configure le CDC pour répliquer les changements en temps réel du monolithe vers le nouveau service
  3. Lorsque les deux sont synchronisés, redirige le trafic
Indice 3 : Vérification

Utilise le pattern Shadow Traffic :

  1. Envoie une copie de chaque requête au nouveau service (sans impacter l’utilisateur)
  2. Compare les réponses du monolithe et du nouveau service
  3. Lorsque les réponses coïncident à 99,9 %, redirige le trafic réel

Solution de référence

Voir la solution complète

Phase 1 — Préparation (semaine 1-2) :

  • Créer le nouveau service de commandes avec la même API
  • Configurer le CDC depuis la table des commandes du monolithe
  • Migrer les données historiques avec un script batch

Phase 2 — Vérification (semaine 3) :

  • Activer le shadow traffic : chaque requête vers le monolithe est dupliquée vers le nouveau service
  • Comparer automatiquement les réponses et enregistrer les divergences
  • Corriger les bugs jusqu’à ce que les réponses coïncident

Phase 3 — Migration progressive (semaine 4) :

  • Rediriger 10 % du trafic de lecture vers le nouveau service
  • Surveiller la latence, les erreurs et la cohérence des données
  • Augmenter progressivement : 25 % → 50 % → 75 % → 100 %

Phase 4 — Écritures (semaine 5) :

  • Rediriger les écritures vers le nouveau service
  • Le nouveau service publie des événements que le monolithe consomme (pour les rapports legacy)
  • Surveiller intensivement pendant 48 heures

Phase 5 — Nettoyage (semaine 6) :

  • Supprimer le code des commandes du monolithe
  • Désactiver le CDC
  • Mettre à jour les rapports pour utiliser la nouvelle source de données

Rollback à n’importe quelle phase : Rétablir la configuration du proxy/load balancer pour envoyer le trafic vers le monolithe.


Défi 2 : Système de paiement multi-fournisseur

Niveau : Avancé | Durée estimée : 45-60 minutes

Scénario

Conçois un service de paiement qui prend en charge plusieurs fournisseurs (Stripe, PayPal, virement bancaire) avec les exigences suivantes :

  1. Idempotence : un paiement ne peut pas être traité deux fois
  2. Résilience : si un fournisseur échoue, essayer automatiquement avec un autre
  3. Audit : chaque tentative de paiement doit être enregistrée
  4. Réconciliation : pouvoir vérifier que les débits correspondent aux commandes
  5. PCI compliance : ne pas stocker de données de carte dans ton système

Ta mission

Conçois :

  1. L’API du service de paiement
  2. Le flux interne de traitement
  3. Le modèle de données
  4. Les stratégies de résilience
  5. Le flux de réconciliation

Indices

Indice 1 : Idempotence

Utilise une idempotency key générée par le client :

POST /payments
Headers: Idempotency-Key: uuid-unique-per-payment
Body: { orderId, amount, currency, provider }

Avant de traiter, vérifie s’il existe déjà un paiement avec cette clé. S’il existe, retourne le résultat précédent sans le traiter à nouveau.

Indice 2 : Modèle de données
Payment {
  id: UUID
  idempotencyKey: string (unique)
  orderId: string
  amount: decimal
  currency: string
  status: PENDING | PROCESSING | COMPLETED | FAILED | REFUNDED
  provider: STRIPE | PAYPAL | BANK_TRANSFER
  providerTransactionId: string?
  attempts: PaymentAttempt[]
  createdAt: timestamp
  updatedAt: timestamp
}

PaymentAttempt {
  id: UUID
  paymentId: UUID
  provider: string
  status: SUCCESS | FAILED | TIMEOUT
  errorCode: string?
  errorMessage: string?
  responseTime: number
  attemptedAt: timestamp
}
Indice 3 : Stratégie de fallback

Définis un ordre de priorité des fournisseurs par type de paiement :

  1. Carte de crédit : Stripe → PayPal
  2. Carte de débit : Stripe → PayPal
  3. Virement : uniquement banque

Si le fournisseur principal échoue avec une erreur récupérable (timeout, 503), essayer avec le suivant. S’il échoue avec une erreur non récupérable (carte refusée, fonds insuffisants), ne pas réessayer.


Défi 3 : Conçois un système de notifications

Niveau : Intermédiaire | Durée estimée : 30-45 minutes

Scénario

Conçois un service de notifications qui prend en charge :

  • Canaux : email, push, SMS, in-app
  • Priorités : critique (immédiate), haute (< 1 min), normale (< 5 min), basse (batch quotidien)
  • Préférences : les utilisateurs peuvent choisir quels canaux recevoir par type de notification
  • Rate limiting : ne pas envoyer plus de 10 notifications par heure par utilisateur
  • Templates : les notifications utilisent des templates avec des variables dynamiques

Ta mission

  1. Définis l’API pour envoyer des notifications
  2. Conçois le flux de traitement interne
  3. Définis comment tu gères les préférences de l’utilisateur
  4. Conçois la stratégie de rate limiting
  5. Définis ce qui se passe lorsqu’un canal échoue

Considérations

  • Utilises-tu une file par priorité ou une file avec priorités ?
  • Comment gères-tu le batch quotidien des notifications de basse priorité ?
  • Que se passe-t-il si le service d’email est en panne mais que celui de push fonctionne ?
  • Comment évites-tu d’envoyer deux fois la même notification ?
Voir l'architecture suggérée
[API de Notifications]


[File de priorité]
    ├── File CRITICAL (traitement immédiat, workers dédiés)
    ├── File HIGH (traitement < 1 min)
    ├── File NORMAL (traitement < 5 min)
    └── File LOW (accumulation pour batch)


[Notification Processor]

    ├── Vérifier les préférences de l'utilisateur
    ├── Appliquer le rate limiting
    ├── Résoudre le template avec les variables


[Channel Router]
    ├──► [Email Provider] (SendGrid/SES)
    ├──► [Push Provider] (Firebase/APNs)
    ├──► [SMS Provider] (Twilio)
    └──► [In-App Store] (WebSocket/DB)

Rate limiting : Utiliser Redis avec un compteur par utilisateur avec un TTL de 1 heure. Avant d’envoyer, vérifier le compteur. Si la limite est dépassée, mettre en file d’attente pour la période suivante.

Déduplication : Utiliser une clé composée (userId + notificationType + entityId) avec un TTL pour éviter les doublons.


Défi 4 : Observabilité end-to-end

Niveau : Avancé | Durée estimée : 45-60 minutes

Scénario

Tu as 8 microservices en production. Un utilisateur signale que « parfois le checkout prend beaucoup de temps ». Tu n’arrives pas à reproduire le problème de manière consistante. Conçois une stratégie d’observabilité qui te permette de diagnostiquer ce type de problèmes.

Ta mission

  1. Quelles métriques collecterais-tu de chaque service ?
  2. Comment implémenterais-tu le distributed tracing ?
  3. Quelles alertes configurerais-tu ?
  4. Comment corrélerais-tu les logs entre les services ?
  5. Quel dashboard créerais-tu pour l’équipe de garde ?

Critères de réussite

Ta conception doit permettre :

  • D’identifier quel service provoque la latence en moins de 5 minutes
  • De voir le trace complet d’une requête de l’utilisateur
  • De détecter les dégradations avant que les utilisateurs ne les signalent
  • De comprendre l’impact d’un deploy sur la latence du système
Voir la stratégie suggérée

Métriques (par service) :

  • Latence : p50, p90, p95, p99 par endpoint
  • Taux d’erreurs : 4xx et 5xx par endpoint
  • Throughput : requêtes par seconde
  • Saturation : CPU, mémoire, connexions de DB, pool de threads

Distributed tracing :

  • Générer un traceId dans l’API Gateway
  • Le propager via le header X-Trace-Id à tous les services
  • Chaque service enregistre des spans avec début, fin et metadata
  • Utiliser OpenTelemetry comme standard

Alertes :

  • Latence p99 > 2x du baseline pendant plus de 5 minutes
  • Taux d’erreurs 5xx > 1 % pendant plus de 2 minutes
  • File de messages qui grandit pendant plus de 10 minutes
  • CPU > 80 % pendant plus de 5 minutes

Dashboard de garde :

  • Vue générale : health de tous les services (vert/jaune/rouge)
  • Vue de flux : latence par étape du checkout
  • Vue des traces : les 10 traces les plus lents de la dernière heure
  • Vue des erreurs : erreurs regroupées par type et par service

Réflexion

Après avoir complété les défis :

  1. Quel a été le défi le plus difficile ? Pourquoi ?
  2. Dans quels domaines sens-tu que tu as besoin de plus de pratique ?
  3. As-tu pu réutiliser des patterns des sections précédentes ?
  4. Quel défi ressemble le plus à un problème réel que tu rencontres dans ton travail ?

Les défis sont conçus pour simuler la complexité réelle de l’architecture logicielle. Il n’y a pas de solutions parfaites — il y a des solutions bien raisonnées.