Simulateur d'Événements

Concevez et tracez des flux d'événements entre microservices — de la publication à la consommation et aux réactions en chaîne.

Objectif

Dans ce laboratoire, vous allez concevoir des flux d’événements entre microservices, tracer leur parcours complet et anticiper les problèmes qui peuvent survenir dans une architecture event-driven.

Les événements sont le système nerveux d’une architecture distribuée. Comprendre comment ils circulent est fondamental pour concevoir des systèmes découplés et résilients.

Concepts préalables

Avant de commencer, assurez-vous de bien comprendre la différence entre :

ConceptDescriptionExemple
CommandeRequête pour faire quelque choseCreateOrder
ÉvénementNotification de quelque chose qui s’est déjà produitOrderCreated
QueryRequête d’informationGetOrderStatus

Les événements sont des faits immuables — quelque chose qui s’est déjà produit et qui ne peut pas être annulé. Les commandes sont des intentions — elles peuvent être rejetées.


Exercice 1 : Concevoir un flux d’événements

Scénario

Un utilisateur achète un produit sur votre e-commerce. Concevez le flux complet d’événements depuis le clic sur « Acheter » jusqu’à la réception de la confirmation.

Services impliqués

  • Service de Commandes : crée et gère les commandes
  • Service d’Inventaire : réserve et décrémente le stock
  • Service de Paiements : traite l’encaissement
  • Service de Notifications : envoie des emails et des notifications push
  • Service d’Analytics : enregistre les métriques métier

Votre tâche

Complétez le diagramme d’événements :

Usuario hace clic en "Comprar"


[Servicio de Órdenes]
    │ publica: OrderCreated { orderId, userId, items, total }

    ├──► [Servicio de Inventario]
    │       consume: OrderCreated
    │       acción: reservar stock
    │       publica: _______________

    ├──► [Servicio de Pagos]
    │       consume: _______________
    │       acción: procesar cobro
    │       publica: _______________

    ├──► [Servicio de Notificaciones]
    │       consume: _______________
    │       acción: _______________

    └──► [Servicio de Analytics]
            consume: _______________
            acción: _______________

Solution

Voir le flux complet
Usuario hace clic en "Comprar"


[Servicio de Órdenes]
    │ publica: OrderCreated { orderId, userId, items, total }

    ├──► [Servicio de Inventario]
    │       consume: OrderCreated
    │       acción: reservar stock para los items
    │       publica: StockReserved { orderId, items }
    │              o StockInsufficient { orderId, items }

    ├──► [Servicio de Pagos]
    │       consume: StockReserved
    │       acción: procesar cobro con el proveedor
    │       publica: PaymentCompleted { orderId, transactionId }
    │              o PaymentFailed { orderId, reason }

    ├──► [Servicio de Notificaciones]
    │       consume: PaymentCompleted
    │       acción: enviar email de confirmación al usuario
    │       consume: PaymentFailed
    │       acción: enviar email de fallo al usuario

    └──► [Servicio de Analytics]
            consume: OrderCreated, StockReserved, PaymentCompleted
            acción: registrar métricas de conversión y revenue

Exercice 2 : Gérer le chemin malheureux

Scénario

Que se passe-t-il lorsque quelque chose échoue au milieu du flux ? Concevez les compensations.

Cas 1 : Le stock a été réservé mais le paiement a échoué

OrderCreated → StockReserved → PaymentFailed


                              ¿Qué pasa ahora?

Votre tâche : Quel événement le service de paiements doit-il publier ? Qui le consomme ? Quelle action prend-il ?

Voir la solution
PaymentFailed { orderId, reason }

    ├──► [Servicio de Inventario]
    │       consume: PaymentFailed
    │       acción: liberar stock reservado
    │       publica: StockReleased { orderId, items }

    ├──► [Servicio de Órdenes]
    │       consume: PaymentFailed
    │       acción: marcar orden como fallida
    │       publica: OrderCancelled { orderId, reason }

    └──► [Servicio de Notificaciones]
            consume: OrderCancelled
            acción: notificar al usuario que el pago falló

C’est le pattern Saga par chorégraphie : chaque service réagit aux événements et exécute des compensations lorsque quelque chose échoue. Il n’y a pas d’orchestrateur central.

Cas 2 : Le service d’inventaire est hors service

OrderCreated → [Inventario: CAÍDO] → ???

Votre tâche : Que devient l’événement OrderCreated ? Est-il perdu ? Est-il réessayé ?

Voir la solution

Si vous utilisez un message broker avec des files d’attente durables (RabbitMQ, Kafka) :

  1. L’événement OrderCreated reste dans la file d’attente en attente
  2. Lorsque le service d’inventaire revient, il consomme les événements en attente
  3. Le traitement se poursuit normalement (avec du délai)

Point clé : La file d’attente agit comme un buffer temporaire. L’événement n’est pas perdu tant que la file est durable et que les messages sont persistants.

À considérer : Si le service reste hors service longtemps, la file grandit. Vous avez besoin de :

  • Alertes sur la taille de la file
  • TTL sur les messages pour éviter de traiter des événements trop anciens
  • Logique de compensation si l’événement n’est plus pertinent

Exercice 3 : Ordonnancement des événements

Le problème

Deux événements arrivent au service d’inventaire :

  1. StockUpdated { productId: "ABC", quantity: 100 } (timestamp : 10:00:01)
  2. StockUpdated { productId: "ABC", quantity: 50 } (timestamp : 10:00:00)

L’événement 2 a été publié en premier mais est arrivé après. Si vous traitez dans l’ordre d’arrivée, le stock reste à 50 (incorrect — il devrait être à 100).

Votre tâche

Comment garantissez-vous le bon ordre ? Évaluez ces stratégies :

StratégieAvantagesInconvénients
Trier par timestampSimpleHorloges non synchronisées entre services
Numéro de séquenceOrdre garantiNécessite une coordination entre les publishers
Partition par keyOrdre au sein de la partitionLimite le parallélisme
Version optimisteNe nécessite pas d’ordre globalNécessite une logique de résolution de conflits
Voir la recommandation

La stratégie la plus pratique dans la plupart des cas est la partition par key :

  • Dans Kafka : utiliser productId comme partition key
  • Tous les événements d’un même produit vont à la même partition
  • Au sein d’une partition, l’ordre est garanti
  • Différents produits sont traités en parallèle

Pour les cas où vous avez besoin d’un ordre global, utilisez un numéro de séquence généré par le publisher et rejetez les événements dont la séquence est inférieure à la dernière traitée.


Exercice 4 : Concevez votre propre flux

Votre tâche

Choisissez l’un de ces scénarios et concevez le flux complet d’événements :

Option A : Système de réservations d’hôtel

  • Services : Réservations, Chambres, Paiements, Notifications
  • Flux : l’utilisateur réserve → vérifier la disponibilité → encaisser → confirmer

Option B : Plateforme de livraison

  • Services : Commandes, Restaurants, Livreurs, Suivi, Notifications
  • Flux : l’utilisateur commande → le restaurant accepte → livreur assigné → livraison

Option C : Système d’abonnements

  • Services : Utilisateurs, Abonnements, Paiements, Accès, Notifications
  • Flux : l’utilisateur s’abonne → prélèvement récurrent → renouvellement/annulation

Pour chacun, définissez :

  1. Les événements principaux (chemin heureux)
  2. Les événements de compensation (chemin malheureux)
  3. Quel service publie et qui consomme chaque événement
  4. Comment vous gérez les échecs à chaque étape

Réflexion

  1. De combien d’événements avez-vous eu besoin pour un flux « simple » ?
  2. À quel point a-t-il été difficile de concevoir les compensations ?
  3. Préféreriez-vous un orchestrateur central (Saga orchestrée) ou une chorégraphie ?
  4. Comment déboggeriez-vous un problème dans un flux de plus de 5 événements ?

Les systèmes event-driven sont puissants mais complexes. La clé est de concevoir les flux avant d’écrire du code — et de toujours penser à ce qui se passe quand quelque chose échoue.