Stocker les causes plutôt que les effets. Un pattern de persistance où l’état est reconstitué à partir d’un journal d’événements immuables — Time Travel, Audit complet, et projections multiples inclus.
« Le solde de votre compte bancaire n’est pas une vérité : c’est un calcul. La vérité, ce sont les transactions. » L’Event Sourcing applique ce principe à l’ensemble d’un système : ne jamais stocker l’état courant directement — stocker les événements qui l’ont produit. L’état n’est que la somme des causes passées, recalculable à volonté, à n’importe quel instant.
01
La métaphore : le grand livre de comptabilité
Imagine la comptabilité d’une entreprise. Il y a deux façons de tenir les comptes :
Approche CRUD classique — Tu as un fichier avec le solde actuel. Hier tu avais 1 000 €, aujourd’hui tu notes 1 350 €. Mais pourquoi ? D’où viennent ces 350 € ? Impossible à savoir. Tu ne peux pas auditer, remonter dans le temps, ou détecter une erreur de saisie.
Approche Event Sourcing — Tu tiens un grand livre où chaque ligne est une entrée immuable : dépôt +500 €, achat -200 €, virement +1 050 €. Le solde actuel de 1 350 € n’est que la somme de tous les événements. Tu peux reconstruire l’état à n’importe quel moment, auditer chaque décision, rejouer l’histoire.
C’est exactement l’Event Sourcing : les événements sont la vérité. L’état est un dérivé.
⛔ Approche CRUD — UPDATE balance = 1350
L'historique des mutations est perdu
Impossible d'auditer "qui a modifié quoi et pourquoi"
Pas de time travel ni de replay
Une seule représentation de l'état
Correction impossible sans trace
✅ Approche Event Sourcing — APPEND event
Historique complet et immuable des causes
Audit natif, traçabilité totale
Time travel : état à n'importe quel point
Projections multiples et reconstruibles
Correction via événement compensatoire
02
Les concepts fondamentaux
📋
Domain Event
Événement Domaine
Un fait métier passé, immuable. Nommé au passé composé : MoneyDeposited, AccountClosed. Contient toutes les données nécessaires pour reconstituer ce qui s'est passé.
ImmuableFait passéAppend-only
« Un événement n'est pas "dépose de l'argent" — c'est "de l'argent A ÉTÉ déposé". »
🏛️
Event Store
Journal d'Événements
Base de données append-only qui persiste tous les événements. Comme Git : on n'écrase jamais l'histoire, on ajoute des commits. Organisée en streams par agrégat.
Append-onlyStreamsOptimistic Lock
« EventStoreDB, PostgreSQL append-only, ou Apache Kafka selon les besoins. »
🧮
Aggregate + Rehydration
Reconstitution d'état
L'état courant de l'agrégat est reconstruit en replaying tous les événements passés. C'est un Array.reduce() appliqué à l'histoire : fold(events) → state.
Fold / ReducePure functionDeterministe
📊
Projection / Read Model
Vue Matérialisée
Représentation optimisée pour la lecture, construite en écoutant les événements. On peut avoir autant de projections que de besoins : soldes, historique, détection de fraude, analytics…
CQRS ReadReconstruibleOptimisé query
📸
Snapshot
Instantané d'optimisation
Capture de l'état à un moment précis pour éviter de rejouer des milliers d'événements à chaque chargement. Optionnel — on rejoue uniquement les événements postérieurs au snapshot.
OptimisationOptionnelPeriodicite
⏰
Time Travel
Voyage dans le temps
Reconstituer l'état d'un agrégat à n'importe quel instant passé en rejouant uniquement les événements antérieurs à ce point. Outil de débogage métier inestimable.
DebugAuditReplay partiel
03
L’Event Store — le grand livre immuable
Chaque agrégat possède son propre stream d’événements. Les événements sont ordonnés, versionnés et ne sont jamais modifiés ni supprimés. La version est utilisée pour l’Optimistic Concurrency Control — si deux processus tentent d’écrire en même temps avec la même version, le second est rejeté.
temps →
VERSION 1 · 2024-01-10
AccountCreated
id: acc-42
owner: Alice
balance: 0 EUR
VERSION 2 · 2024-01-12
MoneyDeposited
accountId: acc-42
amount: 500 EUR
desc: Salaire
VERSION 3 · 2024-01-15
MoneyWithdrawn
accountId: acc-42
amount: 200 EUR
reason: Achat ligne
VERSION 4 · 2024-01-20
MoneyDeposited
accountId: acc-42
amount: 1050 EUR
desc: Virement
VERSION 5 · 2024-03-01
AccountClosed
accountId: acc-42
reason: Customer req.
finalBalance: 1350 EUR
🔒 Jamais modifié · Jamais supprimé · Append-only
La reconstitution de l’état est un simple fold (équivalent à Array.reduce()) sur la séquence des événements :
balance: 0 initial
→+500
balance: 500
→-200
balance: 300
→+1050
balance: 1350 état courant
La règle du fold :etat courant = events.reduce(applyEvent, initialState) Le même ensemble d’événements produit toujours le même état — la fonction applyEvent est pure et déterministe. C’est le coeur mathématique de l’Event Sourcing.
04
Vue d’ensemble de l’architecture
L’Event Sourcing s’articule autour de quatre zones distinctes : les commandes (intentions), l’agrégat (validation + émission), l’Event Store (persistance immuable), et les projections (lecture optimisée).
AccountBalanceProjectionTransactionHistoryProjectionFraudDetectionProjectionAnalyticsDashboardProjectionrebuild() — reconstruire depuis zéro
Principe clé : Chaque projection écoute les mêmes événements et construit sa propre représentation optimisée pour son besoin de lecture. On peut supprimer et reconstruire n’importe quelle projection à tout moment en rejouant l’intégralité de l’Event Store — sans perte de données.
05
Implémentation OOP — TypeScript / NestJS
L’approche orientée objet organise le code autour de l’agrégat qui encapsule à la fois la logique de validation des commandes, l’émission d’événements, et l’application des événements à l’état interne.
L’Event Sourcing est naturellement fonctionnel : un fold est une opération purement fonctionnelle. Avec fp-ts, on modélise les commandes comme des fonctions State → Either<Error, Event[]>, ce qui garantit une gestion d’erreur explicite et composable via le Railway Oriented Programming.
Railway Oriented Programming : chaque décision retourne un Either<Error, Event[]>. Les erreurs sont des valeurs typées, pas des exceptions. La fonction processDeposit compose validation et émission d’événements dans un pipeline fonctionnel clair et testable.
07
Les Projections — construire les Read Models
Les projections sont l’une des forces majeures de l’Event Sourcing. Chaque projection écoute le flux d’événements et maintient sa propre représentation optimisée pour un besoin de lecture spécifique. On peut en avoir autant que nécessaire — et on peut toutes les supprimer et reconstruire en rejouant l’Event Store.
Un événement doit être un fait métier, pas une opération technique.
❌ BalanceFieldUpdated
✅ MoneyDeposited
⚠️
Piège 02
Schéma d'événement immuable
Un événement persisté est gravé dans le marbre. Utiliser un Upcaster pour migrer les anciens formats vers de nouveaux schémas.
« On ne modifie pas l'histoire. On l'interprète différemment. »
⚠️
Piège 03
Pas de Snapshots = lenteur
Sans snapshots, rejouer 50 000 événements à chaque requête est prohibitif. Implémenter des snapshots périodiques pour les agrégats à longue durée de vie.
🔀
Confusion 01
ES ≠ Event-Driven Architecture
L'Event Sourcing est un pattern de persistance. L'EDA est un pattern de communication. Ils se combinent souvent mais sont distincts et indépendants.
🔀
Confusion 02
ES ≠ CQRS obligatoire
L'Event Sourcing se marie bien avec CQRS mais ne l'implique pas. On peut faire de l'Event Sourcing sans CQRS, et du CQRS sans Event Sourcing.
💾
Choix tech
L'Event Store n'est pas SQL
Pas une table SQL ordinaire : EventStoreDB (natif), Apache Kafka (distribué), ou une table PostgreSQL append-only avec version et SERIAL.
10
Quand utiliser l’Event Sourcing ?
Critère
Description
Verdict
Audit obligatoire
Finance, santé, logistique, conformité réglementaire — traçabilité totale requise
✓ Idéal
Time travel métier
Besoin de reconstituer l'état à un instant passé pour débogage ou litige
✓ Idéal
Plusieurs Read Models
Analytics, reporting, dashboard, alertes — chaque besoin a sa projection
✓ Idéal
Workflow complexe
Processus métier multi-étapes avec corrections, annulations, compensations
✓ Recommandé
Intégration DDD
Agrégats riches, logique métier forte, frontières de contexte claires
✓ Synergie
CRUD simple
Blog, catalogue produit, gestion de contenu sans exigence d'audit
✗ Over-engineering
Équipe sans expérience
Pas de formation DDD / Event Sourcing dans l'équipe