Aller au contenu principal
Application web & SaaS

Event-Driven Architecture

L'Event-Driven Architecture (EDA), ou architecture pilotée par les événements, est un style d'architecture logicielle dans lequel les composants du système communiquent en produisant et en consommant des événements. Un événement est un fait qui s'est produit : « une commande a été passée », « un paiement a été reçu », « un utilisateur s'est inscrit ». C'est un fait immuable, daté, qui décrit un changement d'état dans le système.

Contrairement à l'architecture classique requête/réponse où le service A appelle directement le service B et attend sa réponse, en EDA, le service A émet un événement et continue son travail. Les services intéressés par cet événement le consomment de manière autonome, à leur propre rythme. Le producteur ne connaît pas les consommateurs, et les consommateurs ne connaissent pas le producteur.

C'est un découplage radical qui change fondamentalement la façon dont on conçoit des systèmes distribués.

Pourquoi l'EDA est devenue incontournable

Les limites de l'architecture synchrone

Dans une architecture classique basée sur des appels REST synchrones entre microservices, chaque service dépend directement des services qu'il appelle. Si le service de paiement est lent ou indisponible, le service de commande est bloqué. La latence s'accumule à chaque appel en cascade. Le couplage temporel est fort : tous les services doivent être disponibles en même temps.

Voici les problèmes concrets :

  • Couplage fort : le service de commande doit connaître l'adresse, le contrat API et les spécificités du service de paiement, du service d'inventaire, du service de notification. Il est couplé à tous.
  • Cascade de pannes : si le service de notification tombe, la création de commande échoue — alors que l'envoi d'email n'est pas critique pour valider une commande.
  • Scalabilité limitée : le service le plus lent de la chaîne devient le goulot d'étranglement de tout le système.
  • Rigidité : ajouter un nouveau comportement (ex. : envoyer un SMS en plus de l'email) nécessite de modifier le service de commande. Chaque nouveau besoin crée une dépendance supplémentaire.

L'événement comme solution

L'EDA résout ces problèmes en inversant le flux de communication. Au lieu que le service de commande appelle le service de notification, il émet un événement OrderPlaced. Le service de notification, le service d'inventaire, le service de facturation — tous ceux qui sont intéressés — consomment cet événement de manière indépendante.

Les bénéfices sont immédiats :

  • Découplage : le producteur ne connaît pas les consommateurs. Ajoutez un nouveau consommateur sans modifier le producteur.
  • Résilience : si le service de notification est en panne, l'événement attend dans la file. Il sera traité quand le service reviendra. La commande n'est pas impactée.
  • Scalabilité indépendante : chaque consommateur scale selon ses propres besoins. Le service de notification peut avoir 2 instances pendant que le service de facturation en a 10.
  • Extensibilité : ajouter un nouveau comportement = ajouter un nouveau consommateur. Zéro modification du code existant. Open/Closed Principle à l'échelle de l'architecture.

Les concepts fondamentaux

Qu'est-ce qu'un événement ?

Un événement est un message immuable qui décrit un fait passé. Il contient :

  • Un type : OrderPlaced, PaymentReceived, UserRegistered. Nommé au passé car c'est un fait accompli.
  • Un identifiant unique : UUID pour la déduplication et le tracking.
  • Un timestamp : quand l'événement s'est produit.
  • Des données : les informations pertinentes sur l'événement (l'ID de la commande, le montant, le client, etc.).
  • Des métadonnées : correlation ID pour le tracing distribué, version du schéma, source.

👉 Un événement n'est pas une commande. La commande (PlaceOrder) exprime une intention — elle peut être refusée. L'événement (OrderPlaced) exprime un fait — il s'est déjà produit, il est irrévocable.

Les trois types d'événements

1. Domain Events (événements de domaine)

Ils décrivent un fait métier significatif dans un bounded context (au sens du DDD). Exemples : OrderPlaced, InvoicePaid, ProductOutOfStock. Ils sont émis par les agrégats du domaine et consommés au sein du même contexte ou par d'autres contextes.

2. Integration Events (événements d'intégration)

Ils permettent la communication entre bounded contexts ou entre microservices. Ils contiennent un sous-ensemble des données, pensé pour les consommateurs externes. Ils ont un schéma versionné et un contrat explicite.

3. Event Notifications

Des événements légers qui signalent qu'un fait s'est produit sans inclure toutes les données. Le consommateur doit rappeler le producteur via une API pour obtenir les détails. Utile pour les événements fréquents où la plupart des consommateurs n'ont pas besoin de toutes les données.

Les topologies de l'EDA

Mediator Topology (orchestration)

Un composant central (médiateur/orchestrateur) reçoit les événements initiaux et coordonne les étapes de traitement. Il sait quelles étapes exécuter et dans quel ordre. AWS Step Functions, Netflix Conductor, Temporal — ce sont des orchestrateurs.

Avantages : flux de traitement explicite, facile à comprendre, gestion d'erreurs centralisée.

Inconvénients : le médiateur peut devenir un single point of failure, couplage centralisé.

Broker Topology (chorégraphie)

Pas de coordinateur central. Chaque composant réagit aux événements et peut en émettre de nouveaux. Le flux émerge de la collaboration décentralisée des composants. Apache Kafka, RabbitMQ, Amazon EventBridge — ce sont des brokers.

Avantages : découplage maximal, pas de point central de défaillance, extensibilité naturelle.

Inconvénients : flux difficile à visualiser, debugging complexe, risque de cycles d'événements.

💡 En pratique, la plupart des systèmes combinent les deux : chorégraphie entre bounded contexts, orchestration au sein d'un workflow complexe.

Les technologies de l'EDA

Les message brokers

Apache Kafka

Le roi de l'event streaming. Kafka est un journal distribué (distributed log) qui stocke les événements de manière durable et ordonnée. Caractéristiques clés :

  • Rétention durable : les événements sont stockés sur disque, pas supprimés après consommation. Vous pouvez relire l'historique.
  • Partitionnement : les topics sont divisés en partitions pour le parallélisme. Chaque partition maintient l'ordre.
  • Consumer groups : plusieurs instances d'un même service consomment en parallèle, chaque partition assignée à une seule instance.
  • Throughput massif : des millions de messages par seconde. LinkedIn traite des trillions d'événements par jour avec Kafka.

Kafka est idéal pour l'event sourcing, le streaming de données, la réplication inter-services. C'est le choix par défaut pour les architectures à grande échelle.

RabbitMQ

Un message broker traditionnel basé sur le protocole AMQP. Contrairement à Kafka, les messages sont supprimés après consommation (modèle queue classique). Points forts :

  • Routing flexible : exchanges (direct, topic, fanout, headers) permettent un routage sophistiqué des messages.
  • Protocole standard : AMQP est un standard ouvert, pas de vendor lock-in.
  • Simplicité : plus simple à opérer que Kafka pour des volumes modérés.

Amazon EventBridge

Un bus d'événements serverless d'AWS. Pas d'infrastructure à gérer, facturation à l'événement. Idéal pour les architectures serverless. Supporte des règles de filtrage puissantes et l'intégration native avec tous les services AWS.

Redis Streams

Une alternative légère pour les cas d'usage simples. Redis Streams offre un log de messages avec consumer groups, directement dans Redis. Parfait pour le streaming intra-application sans la complexité de Kafka.

Kafka vs RabbitMQ : quel choix ?

  • Kafka si : volume élevé, besoin de relire des événements, event sourcing, streaming temps réel, analytics.
  • RabbitMQ si : routage complexe, volumes modérés, task queues, protocole standard nécessaire, simplicité opérationnelle.
  • EventBridge si : architecture serverless sur AWS, pas de volume critique, intégration services AWS.

Patterns essentiels de l'EDA

Event Sourcing

Au lieu de stocker l'état courant d'une entité (une ligne dans une table), on stocke la séquence complète des événements qui ont produit cet état. L'état courant est obtenu en rejouant les événements depuis le début (ou depuis un snapshot).

Exemple pour un compte bancaire :

  • AccountOpened { balance: 0 }
  • MoneyDeposited { amount: 1000 }
  • MoneyWithdrawn { amount: 200 }
  • MoneyDeposited { amount: 500 }
  • → État courant : balance = 1300

Avantages de l'event sourcing :

  • Audit trail complet : chaque changement est tracé. Indispensable en finance, santé, conformité.
  • Debugging : vous pouvez rejouer les événements pour reproduire un bug exact.
  • Temporal queries : « quel était l'état du compte au 15 janvier ? » — rejouez les événements jusqu'à cette date.
  • Séparation lecture/écriture : les événements alimentent des projections optimisées pour la lecture (CQRS).

📌 L'event sourcing est puissant mais complexe. Ne l'adoptez pas par défaut — utilisez-le quand l'historique des changements a une valeur métier réelle.

CQRS (Command Query Responsibility Segregation)

CQRS sépare le modèle d'écriture (commands) du modèle de lecture (queries). En EDA, les commandes produisent des événements qui mettent à jour des projections de lecture optimisées.

Exemple : quand une commande est passée, l'événement OrderPlaced est stocké dans l'event store (modèle d'écriture) et met à jour simultanément :

  • Une projection « liste des commandes client » (table dénormalisée pour l'affichage).
  • Une projection « tableau de bord vendeur » (agrégations pour le reporting).
  • Une projection « recherche commandes » (index Elasticsearch pour la recherche full-text).

Chaque projection est optimisée pour son cas d'usage de lecture. La lecture n'impacte jamais l'écriture, et vice versa.

Saga Pattern

Les transactions distribuées en microservices ne peuvent pas utiliser les transactions ACID classiques. Le pattern Saga décompose une transaction longue en étapes, chacune avec sa compensation en cas d'échec.

Exemple d'une saga de commande :

  1. Réserver le stock → événement StockReserved.
  2. Débiter le paiement → événement PaymentDebited.
  3. Confirmer la commande → événement OrderConfirmed.

Si le paiement échoue à l'étape 2, une compensation est déclenchée :

  1. Libérer le stock réservé → événement StockReleased.
  2. Notifier le client de l'échec.

Les sagas peuvent être orchestrées (un orchestrateur central pilote les étapes) ou chorégraphiées (chaque service écoute les événements et agit).

Outbox Pattern

Un problème classique en EDA : comment garantir qu'un changement en base de données ET la publication d'un événement sont atomiques ? Si vous sauvegardez en base et que le broker Kafka est indisponible, votre événement est perdu. Si vous publiez l'événement et que la base crash, vous avez un événement sans donnée correspondante.

Le pattern Outbox résout ce problème :

  1. Dans la même transaction base de données, vous sauvegardez l'entité ET insérez l'événement dans une table outbox.
  2. Un processus séparé (CDC — Change Data Capture, via Debezium par exemple) lit la table outbox et publie les événements vers Kafka.
  3. L'événement est marqué comme publié dans la table outbox.

Résultat : atomicité garantie entre changement d'état et publication d'événement. C'est un pattern indispensable pour les systèmes critiques.

Défis et pièges de l'EDA

La cohérence éventuelle (eventual consistency)

En EDA, les données ne sont pas immédiatement cohérentes entre les services. Quand un événement est émis, il faut du temps pour qu'il soit consommé et traité par tous les services. Pendant cet intervalle, les services ont des vues différentes de la réalité.

C'est un changement de paradigme pour les développeurs habitués aux transactions ACID. Concrètement :

  • Un utilisateur passe une commande et voit immédiatement « commande en cours de traitement ».
  • L'inventaire est mis à jour quelques secondes plus tard.
  • Le tableau de bord admin affiche la commande après un délai de propagation.

Ce n'est pas un bug, c'est le fonctionnement normal. Mais il faut que l'UX soit conçue pour cela : états intermédiaires explicites, feedback optimiste, mécanismes de réconciliation.

L'idempotence : non négociable

En EDA, un événement peut être livré plusieurs fois (at-least-once delivery). Votre consommateur doit être idempotent : traiter le même événement deux fois ne doit pas produire d'effet de bord. Stratégies :

  • Clé d'idempotence : stocker les IDs des événements déjà traités et ignorer les doublons.
  • Opérations naturellement idempotentes : SET balance = 1300 est idempotent, SET balance = balance + 500 ne l'est pas.
  • Upsert : « créer ou mettre à jour » plutôt que « créer » seul.

L'ordonnancement des événements

Les événements peuvent arriver dans le désordre, surtout dans les systèmes distribués. Kafka garantit l'ordre au sein d'une partition (avec la même clé de partitionnement), mais pas entre partitions. RabbitMQ ne garantit pas l'ordre du tout en mode concurrent.

Solutions :

  • Partitionnement par clé métier : tous les événements d'une même commande vont dans la même partition Kafka.
  • Versioning des événements : chaque événement porte un numéro de version. Le consommateur détecte les trous et attend.
  • Conception tolérante au désordre : quand c'est possible, concevez vos consommateurs pour qu'ils fonctionnent quel que soit l'ordre d'arrivée.

Le schema evolution

Les événements sont des contrats entre services. Quand le format d'un événement change, il faut que les anciens consommateurs continuent de fonctionner. Stratégies :

  • Schema Registry (Confluent, AWS Glue) : valide les schémas et assure la compatibilité ascendante.
  • Ajout de champs uniquement : ne jamais supprimer ou renommer un champ, uniquement en ajouter.
  • Versioning explicite : OrderPlaced.v1, OrderPlaced.v2 avec des périodes de transition.

Debugging et observabilité

Quand un bug survient dans un système event-driven, le tracer est plus complexe que dans un système synchrone. La requête ne suit pas un chemin linéaire — elle se disperse à travers plusieurs services via des événements asynchrones.

Les outils essentiels :

  • Correlation ID : un identifiant unique propagé dans tous les événements d'un même flux. Indispensable pour reconstituer le parcours.
  • Tracing distribué : Jaeger, Zipkin, Datadog APM pour visualiser le flux complet.
  • Event catalog : un registre documentant tous les événements, leurs producteurs, leurs consommateurs et leurs schémas.
  • Dead Letter Queues : les événements qui échouent sont routés vers une file spéciale pour analyse.

EDA et microservices

L'EDA est le compagnon naturel des microservices. Là où les microservices définissent les frontières entre services, l'EDA définit comment ces services communiquent. La combinaison résout le problème du couplage distribué qui est le défaut principal des architectures microservices à appels REST synchrones.

Cependant, l'EDA n'est pas réservée aux microservices. Même dans un monolithe, les événements de domaine permettent de découpler les modules internes. C'est d'ailleurs une excellente étape intermédiaire : commencez par un monolithe modulaire avec des événements internes, puis extrayez les modules en microservices quand le besoin se confirme.

Quand adopter l'EDA

Bons cas d'usage :

  • Systèmes avec beaucoup de traitements asynchrones (notifications, calculs, synchronisations).
  • Systèmes nécessitant une scalabilité indépendante par composant.
  • Systèmes avec des intégrations multiples (plusieurs services réagissent au même événement).
  • Systèmes où l'audit trail est critique (finance, santé, conformité).
  • Systèmes temps réel (dashboards live, alerting, IoT).

Mauvais cas d'usage :

  • Applications simples avec peu de composants et peu de logique asynchrone.
  • Cas nécessitant une cohérence forte et immédiate (transactions financières atomiques).
  • Équipes sans expérience des systèmes distribués — la courbe d'apprentissage est réelle.

EDA dans la pratique : exemples concrets

Exemple 1 : Plateforme e-commerce

Prenons une plateforme web e-commerce. Quand un client passe une commande, en architecture synchrone classique, le service de commande appelle séquentiellement : vérification du stock, débit du paiement, envoi de l'email de confirmation, mise à jour du tableau de bord vendeur, déclenchement de la préparation logistique. Si l'envoi d'email échoue, toute la commande est bloquée.

En architecture event-driven, le service de commande émet un seul événement OrderPlaced. Cinq consommateurs indépendants le traitent :

  • Le service d'inventaire réserve le stock.
  • Le service de paiement débite le client.
  • Le service de notification envoie l'email.
  • Le service analytics met à jour les dashboards.
  • Le service logistique prépare l'expédition.

Si le service de notification est en panne, les quatre autres continuent normalement. L'email sera envoyé quand le service reviendra — l'événement attend dans la file.

Exemple 2 : Application de gestion RH

Un collaborateur soumet une demande de congés. L'événement LeaveRequested est émis. Le service de workflow notifie le manager. Le manager approuve : événement LeaveApproved. Le service de planning met à jour les disponibilités. Le service de paie ajuste le compteur de congés. Le service de notification informe le collaborateur. Chaque service est autonome, testable indépendamment, et déployable séparément.

Exemple 3 : IoT et monitoring

Des capteurs envoient des mesures toutes les secondes. Chaque mesure est un événement dans un topic Kafka. Un consommateur alimente un dashboard temps réel. Un autre calcule des moyennes mobiles pour la détection d'anomalies. Un troisième stocke les données brutes dans un data lake pour l'analyse historique. Le throughput est énorme, mais chaque consommateur traite les événements à son propre rythme.

Implémenter l'EDA pas à pas

Commencer petit : événements intra-application

Vous n'avez pas besoin de Kafka pour adopter l'EDA. Commencez avec des événements internes à votre application :

En Node.js, utilisez le module EventEmitter natif ou une librairie comme EventEmitter2. En NestJS, le module @nestjs/cqrs fournit un bus d'événements intégré. En PHP/Laravel, les Events et Listeners sont natifs au framework. En Python/Django, les signals offrent un mécanisme similaire.

Ces événements in-process sont synchrones ou asynchrones (via une queue Redis/Bull), mais restent dans le même processus. C'est suffisant pour découpler les modules d'un monolithe et c'est une première étape vers une architecture event-driven complète.

Évoluer vers des événements distribués

Quand les besoins grandissent — plusieurs services, plusieurs équipes, besoin de résilience — vous passez aux événements distribués :

  1. Introduisez un broker : RabbitMQ pour commencer (simple à opérer), Kafka quand les volumes ou les besoins de rétention l'exigent.
  2. Définissez un schéma d'événement : utilisez un format standard (CloudEvents), un schema registry (Confluent, AWS Glue), et une convention de nommage claire.
  3. Implémentez le pattern Outbox : pour garantir la cohérence entre votre base de données et votre broker.
  4. Ajoutez l'observabilité : correlation IDs dans tous les événements, tracing distribué, dashboards sur le lag des consommateurs.

Gouvernance des événements

À mesure que le nombre d'événements et de services grandit, la gouvernance devient critique :

  • Event catalog : un registre centralisé de tous les événements, avec leurs producteurs, consommateurs, schémas et documentation. Des outils comme EventCatalog (open source) aident à maintenir cette documentation.
  • Ownership : chaque événement a un propriétaire (une équipe). Le propriétaire est responsable du schéma, de la compatibilité ascendante, et de la documentation.
  • Conventions de nommage : <BoundedContext>.<Entity>.<Action> — par exemple Order.Payment.Received, Inventory.Stock.Reserved.
  • Versioning : politique claire de versioning et de dépréciation. Les consommateurs doivent avoir le temps de migrer vers les nouvelles versions.

Chez Yield Studio

Chez Yield Studio, l'architecture event-driven est un pilier de notre approche pour les systèmes complexes et distribués.

En pratique, nous l'utilisons sur plusieurs niveaux :

  • Événements de domaine dans les monolithes : même sur des applications Node.js ou PHP monolithiques, nous utilisons des événements internes pour découpler les modules. Un module de commande émet un événement, le module de notification le consomme. Le jour où ces modules doivent devenir des services séparés, la transition est naturelle.
  • Traitements asynchrones : pour les SaaS que nous développons, les tâches lourdes (génération de PDF, envoi d'emails en masse, calculs de données) sont traitées via des files de messages — SQS, Bull (Redis), ou Kafka selon l'échelle.
  • Intégrations temps réel : pour les applications mobiles nécessitant des mises à jour en direct, nous utilisons des architectures event-driven avec WebSockets côté frontend et des événements côté backend.
  • Architecture serverless event-driven : nos déploiements serverless (Lambda, Cloud Functions) sont naturellement event-driven. Les fonctions réagissent à des événements S3, SQS, ou API Gateway.

Nous combinons l'EDA avec l'architecture hexagonale : le domaine émet des événements, les adaptateurs les publient vers le broker approprié. Cette combinaison nous permet de livrer des systèmes à la fois bien structurés en interne et bien découplés entre eux, avec une CI/CD fluide et des DORA metrics que nous suivons de près via notre pratique DevOps.

Vous aimerez aussi

Top 10 des agences de développement web en France (2026)

Top 10 des agences de développement web en France (2026)

CyrilleCyrille15 min
Architecture Hexagonale : construire une application Web & Mobile moderne, maintenable et orientée métier

Architecture Hexagonale : construire une application Web & Mobile moderne, maintenable et orientée métier

JulienJulien4 min
Pourquoi choisir un logiciel sur-mesure plutôt qu’un SaaS ?

Pourquoi choisir un logiciel sur-mesure plutôt qu’un SaaS ?

CyrilleCyrille9 min

Un projet ambitieux ?
Construisons-le ensemble

Nos experts vous accompagnent de la stratégie produit au déploiement technique.

Découvrir notre offre Application web & SaaS
Nous contacter