GH GambleHub

Event Sourcing : les bases

Qu'est-ce que Event Sourcing

Event Sourcing (ES) est un moyen de stocker l'état des objets de domaine non pas sous la forme d'une « chaîne courante », mais comme un journal d'événements immuable décrivant tout ce qui s'est passé. L'état actuel de l'agrégat est la convolution (replay) de ses événements, et toutes les vues à lire sont construites comme des projections sur ce journal.

Principales enquêtes :
  • L'histoire est la « source primaire de la vérité », l'état est la projection de l'histoire.
  • N'importe quel état peut être reproduit, vérifié et expliqué (audit).
  • L'ajout de nouvelles vues et analyses ne nécessite pas de migrations des anciens « snapshots » - il suffit de perdre des événements.

Termes de base

L'agrégat est une unité de domaine de cohérence avec des invariants clairs (Order, Payment, UserBalance).
L'événement est un fait immuable qui s'est produit dans le passé ('payment. authorized`, `order. shipped`).
Event Store est un journal append-only qui fournit l'ordre des événements dans l'agrégat.
La version de l'agrégat est le numéro du dernier événement appliqué (pour la concurrence optimiste).
Snapshot est un moulage périodique de l'état pour accélérer la convolution.
La projection (modèle de lecture) est une vue matérialisée pour la lecture/recherche/rapport (souvent asynchrone).

Comment cela fonctionne (flux de commandes → d'événements → de projections)

1. Le client envoie une commande (CapturePayment, PlaceOrder).
2. L'agrégat valide les invariants et, si tout va bien, génère des événements.
3. Les événements sont ajoutés atomiquement à l'Event Store avec vérification de la version (concurrence optimiste).
4. Les processeurs de projection s'abonnent au flux d'événements et mettent à jour les modèles de lecture.
5. Lors du chargement de l'unité pour la commande suivante, l'état est rétabli : snapshot (le cas échéant) → les événements après snapshot.

Conception d'événements

Attributs obligatoires (noyau)

json
{
"event_id": "uuid",
"event_type": "payment. authorized. v1",
"aggregate_type": "Payment",
"aggregate_id": "pay_123",
"aggregate_version": 5,
"occurred_at": "2025-10-31T10:42:03Z",
"payload": { "amount": 1000, "currency": "EUR", "method": "card" },
"meta": { "trace_id": "t-abc", "actor": "user_42" }
}
Recommandations :
  • Nom : 'domain. action. v{major}`.
  • Additivité : les nouveaux champs sont optionnels, sans changer le sens des anciens.
  • Minimalisme : uniquement les faits, sans duplication des données facilement récupérables.

Contrats et schémas

Fixez les schémas (Avro/JSON Schema/Protobuf) et vérifiez la compatibilité avec CI.
Pour les changements « cassants », une nouvelle version majeure de l'événement et une publication parallèle « v1 »/« v2 » pour la période de migration.

Accès concurrentiel : Concurrence optimiste

Règle : l'écriture de nouveaux événements n'est possible que si 'expected _ version = = current_version'.

Pseudo-code :
pseudo load: snapshot(state, version), then apply events > version new_events = aggregate. handle(command)
append_to_store(aggregate_id, expected_version=current_version, events=new_events)
//if someone has already written an event between load and append, the operation is rejected -> retray with reload

C'est ainsi que nous garantissons l'intégrité des invariants sans transactions distribuées.

Snapshots (accélération de la convolution)

Faites un snapshot tous les N événements ou par minuterie.
Храните `snapshot_state`, `aggregate_id`, `version`, `created_at`.
Vérifiez toujours et rattrapez les événements après le snapshot (ne vous fiez pas seulement au moulage).
Enlevez les snapshots de sorte qu'ils puissent être reconfigurés à partir de la loge (ne gardez pas les champs « magiques »).

Projections et CQRS

ES est naturellement combiné avec CQRS :
  • Modèle Write = agrégats + Event Store.
  • Modèles de lecture = projections mises à jour par des événements (Redis maps, OpenSearch pour la recherche, ClickHouse/OLAP pour les rapports).
  • Les projections sont idempotentes : le traitement répété du même 'event _ id'ne change pas le résultat.

Évolution des schémas et interopérabilité

Additive-first : ajouter des champs ; ne pas changer les types/sémantiques.
Pour les changements complexes : libérez de nouveaux types d'événements et écrivez des migreurs de projections.
Maintenez le double enregistrement ('v1' + 'v2') pendant une période transitoire et filmez 'v1' lorsque toutes les projections sont prêtes.

Sécurité, PII et « droit à l'oubli »

L'histoire contient souvent des données sensibles. Approches :
  • Réduisez les PII dans les événements (identifiants au lieu des données, détails dans les côtés protégés).
  • Crypto-effacement : Chiffrez les champs et détruisez la clé lorsque vous demandez la suppression (l'événement reste, mais les données ne sont pas disponibles).
  • Événements de révision : 'user. piiredacted. v1 'avec remplacement des champs sensibles dans les projections (l'histoire conserve le fait de l'édition).
  • Stratégies de rétractation : pour certains domaines, une partie des événements peut être archivée dans le stockage WORM.

Performances et évolutivité

Lot : l'ordre est important à l'intérieur de l'agrégat - le lot par 'aggregate _ id'.
Démarrage à froid : snapshots + convolution périodique « densification ».
Batch-Append : regroupez les événements en une seule transaction.
Backpressure et DLQ pour processeurs de projection ; mesurer la larme (heure et nombre de messages).
Indexation Event Store : accès rapide par '(aggregate_type, aggregate_id)' et par heure.

Tests

Tests de spécification pour les agrégats : script « commandes → événements attendus ».
Tests de projection : alimentez le flux d'événements et vérifiez l'état/les indices matérialisés.
Tests de replayabilité : recréez les projections « à partir de zéro » sur le stand - assurez-vous que le résultat est le même.
Chaos/latitude : injecter les retards et les prises, vérifier l'idempotence.

Exemples de domaines

1) Paiements

Événements : 'payment. initiated`, `payment. authorized`, `payment. captured`, `payment. refunded`.
Invariants : on ne peut pas « capture » sans « autorisé » ; les montants ne sont pas négatifs ; la monnaie est inchangée.
Projections : « carte de paiement » (KV), recherche de transaction (OpenSearch), reporting (OLAP).

2) Commandes (e-commerce)

Événements : 'order. placed`, `order. paid`, `order. packed`, `order. shipped`, `order. delivered`.
Invariants : transitions d'état par diagramme d'état ; l'annulation est possible jusqu'à « shipped ».
Projections : liste des commandes de l'utilisateur, SLA-dashboards par statut.

3) Bilans (Finances/iGaming)

Événements : 'balance. deposited`, `balance. debited`, `balance. credited`, `balance. adjusted`.
Invariant rigide : l'équilibre ne s'éloigne pas <0 ; les commandes sont idempotentes par 'opération _ id'.
Les opérations critiques sont lues directement à partir de l'agrégat (cohérence stricte), l'UI à partir de la projection (eventual).

Structure type Event Store (option avec OBD)

events

`event_id (PK)`, `aggregate_type`, `aggregate_id`, `version`, `occurred_at`, `event_type`, `payload`, `meta`

Index : '(aggregate_type, aggregate_id, version)'.

snapshots

`aggregate_type`, `aggregate_id`, `version`, `state`, `created_at`

Index : '(aggregate_type, aggregate_id)'.

consumers_offsets

'consumer _ id', 'event _ id '/' position', 'updated _ at' (pour les projections et retlay).

Questions fréquentes (FAQ)

Est-il obligatoire d'utiliser ES partout ?
Non. L'ES est utile lorsque l'audit, les invariants complexes, la reproductibilité et les différentes représentations des données sont importants. Pour un simple CRUD, c'est redondant.

Qu'en est-il des demandes d'état actuel ?
Soit lisez à partir de la projection (rapide, eventual), soit à partir de l'agrégat (plus cher, mais strictement). Les opérations critiques utilisent généralement la deuxième voie.

Ai-je besoin d'un courtier Kafka/stream ?
Event Store est la source de la vérité ; le courtier est pratique pour diffuser des événements aux projecteurs et aux systèmes externes.

Que faire du « droit à l'oubli » ?
Minimisez les PII, cryptez les champs sensibles et appliquez l'effacement/édition crypto dans les projections.

Comment migrer les anciennes données ?
Écrivez un script de génération d'événements rétrospective (« re-haistori ») ou commencez par « état-tel » et publiez les événements uniquement pour de nouveaux changements.

Anti-modèles

Event Sourcing « par habitude » : complique un système sans avantage de domaine.
Événements Fat : payload's gonflés avec PII et doublures - freins et problèmes de complication.
Manque de concurrence optimiste : perte d'invariants en course.
Projections non reproductibles : pas de repli/snapshots → fictions manuelles.
Les CDC bruts comme événements de domaine : fuite de schémas OBD et connectivité rigide.
Mélange d'événements internes et d'intégration : publiez une « vitrine » stabilisée vers l'extérieur.

Chèque-liste pour la production

  • Les agrégats, invariants et événements (titres, versions, schémas) sont définis.
  • Event Store fournit l'ordre dans l'agrégat et la concurrence optimiste.
  • Les snapshots et le plan pour les relocaliser sont inclus.
  • Les projections sont idempotentes, il y a le DLQ et les métriques de la lagune.
  • Les schémas sont validés sur CI, la politique de version est documentée.
  • PII est minimisé, les champs sont cryptés, il y a une stratégie d'oubli.
  • Le relais des projections a été vérifié sur le stand ; il existe un plan de reprise après sinistre.
  • Dashboards : vitesse de l'Append, lag des projections, erreurs d'application, proportion de retraits.

Résultat

Event Sourcing fait de l'histoire du système un artefact de première classe : nous enregistrons les faits, en reproduisant l'état et en construisant librement toutes les représentations. Cela donne l'audit, la résilience au changement et la flexibilité de l'analyse - sous réserve de la discipline dans les schémas, le contrôle de la concurrence et le travail compétent avec les données sensibles.

Contact

Prendre contact

Contactez-nous pour toute question ou demande d’assistance.Nous sommes toujours prêts à vous aider !

Commencer l’intégration

L’Email est obligatoire. Telegram ou WhatsApp — optionnels.

Votre nom optionnel
Email optionnel
Objet optionnel
Message optionnel
Telegram optionnel
@
Si vous indiquez Telegram — nous vous répondrons aussi là-bas.
WhatsApp optionnel
Format : +code pays et numéro (ex. +33XXXXXXXXX).

En cliquant sur ce bouton, vous acceptez le traitement de vos données.