Event-Driven Architecture

Comment concevoir des systèmes réactifs où les composants communiquent au moyen d'événements, en obtenant découplage, scalabilité et traitement asynchrone.

Quel problème résout-il

Dans une architecture traditionnelle fondée sur des appels synchrones (request-response), les services sont fortement couplés : l’émetteur doit connaître le récepteur, attendre sa réponse et gérer ses défaillances. Cela engendre plusieurs problèmes :

  • Couplage temporel : L’émetteur est bloqué jusqu’à ce que le récepteur réponde
  • Couplage de connaissance : L’émetteur doit savoir qui traite sa requête
  • Scalabilité limitée : Ajouter un nouveau consommateur exige de modifier l’émetteur
  • Fragilité : Si le récepteur est hors service, l’émetteur échoue également
Communication synchrone (couplée):
  Order Service ──► Inventory Service (espera respuesta)
                ──► Payment Service (espera respuesta)
                ──► Notification Service (espera respuesta)
                ──► Analytics Service (espera respuesta)
  
  Order Service conoce a TODOS los servicios
  Si uno falla, el pedido falla
  Agregar un servicio = modificar Order Service

Comment ça fonctionne

L’architecture orientée événements (EDA) inverse le modèle de communication. Plutôt qu’un service appelle directement les autres, il publie un événement qui décrit quelque chose qui s’est produit. Les services intéressés s’abonnent à ces événements et réagissent de manière indépendante.

Communication par événements (découplée):
  Order Service ──evento──► Event Bus
                            "OrderCreated"

                    ┌───────────┼───────────┐
                    ▼           ▼           ▼
              Inventory    Payment    Notification
              Service      Service    Service
  
  Order Service NO conoce a los consumidores
  Cada servicio reacciona de forma independiente
  Agregar un servicio = suscribirse al evento

Concepts clés

ConceptDescriptionExemple
ÉvénementEnregistrement immuable de quelque chose qui s’est produitOrderCreated, PaymentProcessed
ProducteurService qui publie des événementsOrder Service publie OrderCreated
ConsommateurService qui réagit aux événementsInventory Service écoute OrderCreated
Event Bus/BrokerInfrastructure qui transporte les événementsKafka, RabbitMQ, Amazon SNS/SQS
Topic/QueueCanal par lequel circulent les événementsorders.created, payments.processed

Types d’événements

Domain Events (événements de domaine) : Ils représentent des faits métier.

{
  "type": "OrderCreated",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "orderId": "ORD-001",
    "customerId": "USR-123",
    "items": [{"productId": "P001", "quantity": 2}],
    "total": 150.00
  }
}

Integration Events (événements d’intégration) : Ils communiquent les changements entre bounded contexts.

{
  "type": "InventoryReserved",
  "source": "inventory-service",
  "data": {
    "orderId": "ORD-001",
    "reservationId": "RES-456",
    "status": "confirmed"
  }
}

Modèles de livraison

Pub/Sub (Publish-Subscribe) : Un événement est livré à tous les abonnés.

Productor ──► Topic ──► Consumidor A
                   ──► Consumidor B
                   ──► Consumidor C

Event Streaming : Les événements sont stockés dans un log persistant et les consommateurs les lisent à leur propre rythme.

Productor ──► Log persistente (Kafka)
              [evento1, evento2, evento3, ...]
              
  Consumidor A: leyendo evento 3 (al día)
  Consumidor B: leyendo evento 1 (atrasado, procesando)
  Consumidor C: nuevo, empieza desde evento 1

Garanties de livraison

GarantieDescriptionUsage
At-most-onceL’événement est livré au maximum une fois (il peut être perdu)Métriques, analytics
At-least-onceL’événement est livré au moins une fois (il peut être dupliqué)La plupart des cas (avec idempotence)
Exactly-onceL’événement est livré exactement une foisTransactions financières (difficile à obtenir)

Avantages

  • Découplage : Producteurs et consommateurs ne se connaissent pas entre eux
  • Scalabilité : Ajouter des consommateurs n’exige pas de modifier les producteurs
  • Résilience : Si un consommateur est hors service, les événements s’accumulent et sont traités à son retour
  • Traitement asynchrone : Le producteur n’attend pas que les consommateurs aient terminé
  • Audit naturel : Les événements forment un log de tout ce qui s’est produit dans le système
  • Extensibilité : De nouvelles fonctionnalités s’ajoutent en s’abonnant aux événements existants

Trade-offs / Inconvénients

  • Cohérence à terme : Les données ne sont pas mises à jour instantanément dans tous les services
  • Complexité du débogage : Retracer le flux d’un événement à travers de multiples services est difficile
  • Ordonnancement : Garantir l’ordre de traitement des événements exige une conception soigneuse
  • Doublons : Les consommateurs doivent être idempotents pour gérer les événements dupliqués
  • Infrastructure supplémentaire : Vous avez besoin d’un broker de messagerie (Kafka, RabbitMQ) et de son exploitation
  • Tests complexes : Tester des flux asynchrones est plus difficile que tester des appels synchrones

Quand l’utiliser

  • Systèmes où plusieurs services doivent réagir au même événement
  • Lorsque le traitement peut être asynchrone (vous n’avez pas besoin d’une réponse immédiate)
  • Systèmes avec des exigences élevées de scalabilité et de throughput
  • Lorsque vous devez découpler des services pour qu’ils évoluent de manière indépendante
  • Flux métier complexes comportant plusieurs étapes (commandes, paiements, expéditions)
  • Lorsque vous avez besoin d’un log d’audit de tout ce qui se passe dans le système

Quand l’éviter

  • Opérations qui exigent une réponse synchrone immédiate (requêtes d’utilisateur)
  • Systèmes simples avec peu de services et des flux linéaires
  • Lorsque la cohérence forte est une exigence non négociable
  • Équipes sans expérience des systèmes distribués et du débogage asynchrone
  • Lorsque vous ne pouvez pas justifier l’infrastructure d’un broker de messagerie

Technologies et implémentations courantes

CatégorieOptions
Event StreamingApache Kafka, Amazon Kinesis, Azure Event Hubs
Message BrokersRabbitMQ, Amazon SQS/SNS, Google Pub/Sub, NATS
Event StoreEventStoreDB, Kafka (comme event store)
FrameworksSpring Cloud Stream, MassTransit (.NET), Axon Framework
ObservabilitéOpenTelemetry, Jaeger, Zipkin (tracing distribué)

Relation avec d’autres patterns

  • Saga Pattern : Utilise des événements pour coordonner les transactions distribuées entre services
  • Outbox Pattern : Garantit la publication fiable des événements en même temps que les changements dans la base de données
  • CQRS : Sépare les lectures et les écritures en utilisant des événements pour synchroniser les modèles
  • Idempotence : Les consommateurs doivent être idempotents pour gérer les événements dupliqués
  • Circuit Breaker : Protège les consommateurs de la surcharge lors des pics d’événements

Prochaines étapes

L’architecture orientée événements est le fondement de la communication dans les microservices modernes. Pour protéger l’intégration avec des systèmes externes qui ne parlent pas votre langage d’événements, explorez l’Anti-Corruption Layer. Pour garantir que les consommateurs gèrent correctement les doublons, consultez le pattern d’Idempotence.