Architecture de l'événement
Architecture d'événements (EDA)
1) Qu'est-ce qu'un événement et pourquoi EDA
L'événement est un fait immuable qui s'est déjà produit dans le domaine (« PlayerVerified », « PaymentCaptured »). L'EDA construit des intégrations autour de la publication de ces faits et de leurs réactions :- la faible connectivité des services,
- faire évoluer les consommateurs indépendamment,
- repli/réaménagement des projections,
- un audit transparent.
L'EDA n'annule pas les API synchrones - il les complète en introduisant des dépendances croisées dans la couche asynchrone.
2) Types d'événements
Domaines : faits commerciaux significatifs (OrderPlaced, BonusGranted).
Intégration : « snapshots « /modifications pour les systèmes externes (UserUpdated, WalletBalanceChanged).
Technique : cycle de vie et télémétrie (Heartbeat, PipelineFailed).
Commandes (pas d'événements, mais à côté) : Instructions de CapturePayment.
Recommandation : événements de domaine - primaires ; l'intégration est formée par des projections destinées à des consommateurs spécifiques.
3) Contrats et schémas d'événements
Схема: Avro/Protobuf/JSON Schema + Schema Registry; stratégie d'interopérabilité : 'BACKWARD' pour l'évolution des consommateurs, 'FULL'sur des sujets critiques.
CloudEvents (id, source, type, time, subject, dataconttype) - titres uniformes.
Métadonnées obligatoires : 'event _ id' (ULID/UUID), 'occurred _ at', 'producer', 'schema _ version', 'correlation _ id '/' causation _ id', 'idempotency _ key'.
Versioning : add-only champs, interdiction des rebaptisations/déchirures sémantiques ; nouveaux types - nouveaux thèmes/types.
json
{
"type":"record","name":"PaymentCaptured","namespace":"events.v1",
"fields":[
{"name":"event_id","type":"string"},
{"name":"occurred_at","type":{"type":"long","logicalType":"timestamp-micros"}},
{"name":"payment_id","type":"string"},
{"name":"amount","type":{"type":"bytes","logicalType":"decimal","precision":18,"scale":2}},
{"name":"currency","type":"string"},
{"name":"player_id","type":"string"}
]
}
4) Livraison, ordre et cohérence
At-least-once comme défaut de paiement → l'idempotence des manipulateurs est nécessaire.
Ordre : garanti à l'intérieur d'un lot (Kafka) ou d'une file d'attente (RabbitMQ), mais peut être perturbé par des retraits ; la clé d'événement doit refléter une pastille de domaine d'ordre (par exemple "player _ id').
Cohérence : pour l'argent/les crédits - uniquement par le biais des revues/sagas/compensations ; éviter la LWW.
Modèle de lecture : les projections et les caches peuvent être eventual - montrez « en cours de mise à jour »... et utilisez des stratégies RNOT pour des chemins rigoureux.
5) Outbox/Inbox и CDC
Outbox : le service écrit un fait dans sa base de données et dans la table outbox dans une transaction → le worker publie dans le pneu.
Inbox : le consommateur enregistre 'event _ id' avec le résultat du traitement pour le dédupit.
CDC (Change Data Capture) : flux de modifications de la base de données (binlog/WAL) au bus pour construire des intégrations sans modifier l'application.
Idempotency : traitement par 'idempotency _ key '/' event _ id', ne pas changer le monde extérieur avant la fixation.
6) CQRS и Event Sourcing
CQRS : nous partageons le modèle write et les projections read ; les projections sont construites à partir d'événements et peuvent être en retard.
Event Sourcing : état de l'agrégat = convolution de ses événements. Avantages : audit complet/repli ; inconvénients : complexité des migrations/schémas/snapshots.
Pratique : ES - pas partout, mais là où l'histoire et la rémunération sont importantes ; CQRS - presque toujours en EDA.
7) Sagas : orchestration et chorégraphie
Orchestration : le coordinateur envoie les commandes et attend les événements-réponses ; commode pour les processus complexes (KYC→Deposit→Bonus).
Chorégraphie : les services réagissent aux événements des uns et des autres ; c'est plus facile, mais plus difficile à tracer.
Définissez toujours les compensations et les dédouanements des étapes.
8) Conception de topologies (Kafka/RabbitMQ)
Kafka
Topic per domain event : 'payments. captured. v1`, `players. verified. v1`.
Clé de lot : 'player _ id '/' wallet _ id' - où l'ordre est important.
`replication. factor=3`, `min. insync. replicas = 2 ', producer' acks = all '.
Retraite : par temps (par exemple 7-90 jours) et/ou compaction (dernier état par clé).
Tops pour retry et DLQ avec backoff.
RabbitMQ
Exchanges: `topic`/`direct`, routing key `payments. captured. v1`.
Pour un fan-out large - 'topic' + plusieurs files d'attente ; pour RPC/commandes - files d'attente séparées.
Queues Quorum pour HA ; TTL + dead-letter exchange pour les retraits.
9) Observabilité et SLO EDA
SLI/SLO:- Latency end-to-end (occurred_at → traité) : p50/p95/p99.
- Lag/age : le retard des consommateurs (Kafka consumer lag, Rabbit backlog age).
- Publication/traitement Throughput.
- DLQ-rate et proportion de répétitions.
- Succès des opérations commerciales (par exemple, « dépôt confirmé ≤ 5c »).
- Corrélation des événements via 'trace _ id '/' correlation _ id' (OTel).
- Instances (exemplars) des métriques de piste →.
- Dashboards « Producer→Broker→Consumer » avec des alertes burn-rate.
10) Replay, Retensh et Backfill
Replay pour reconstruire les projections/corriger les bugs : roulez vers une nouvelle projection/neumspace, puis changez de lecture.
Rétention : exigences juridiques/commerciales (RGPD/PCI) ; champs sensibles - chiffrer et/ou tokeniser.
Backfill : thèmes/files d'attente jetables, limites RPS claires pour ne pas étouffer la prod.
11) Sécurité et conformité
TLS en transit, mTLS pour les clients internes.
Autorisation : per-topic/per-exchange ACL ; multitenancy via namespace/vhost.
PII : minimiser les champs dans l'événement ; envelope métadonnées séparément, charges utiles à crypter si nécessaire.
Audit de l'accès aux événements, interdiction des clés « tout puissant ».
Politiques de retrait et droit de suppression (GDPR) : Stockez les références de données ou les événements tombstone et suppression dans les projections.
12) Tests en EDA
Tests de contrat : les consommateurs valident leurs attentes en matière de schémas (consommation-conduite).
Tests de replay : lancer l'échantillonnage historique via le nouveau gestionnaire/version du schéma.
Scénarios chaos : retard/perte de courtier, chute de nœuds, retard du consommateur → SLO restent dans le cadre.
Smoke en CI : un court bout à bout sur des thèmes temporels.
13) Migration des « intégrations CRUD → EDA »
1. Identifiez les faits de domaine.
2. Intégrez outbox dans vos services source.
3. Publiez les événements de domaine minimum et connectez les projections 1-2.
4. Désactivez progressivement les intégrations synchrones ponctuelles en les remplaçant par des abonnements.
5. Entrez Schema Registry et la politique de compatibilité.
6. Étendez les événements avec des champs add-only ; tranches - seulement à travers de nouveaux types.
14) Anti-modèles
Événements = « API DTO » (trop gras, dépendant du modèle interne) - cassent les consommateurs.
L'absence de Schema Registry et de compatibilité est une intégration « fragile ».
La publication à partir du code et l'enregistrement dans la base de données ne sont pas atomiques (pas d'outbox) - vous perdez des événements.
« Exactly-once partout » - prix élevé sans profit ; meilleure at-least-once + idempotence.
Une clé de lot « universelle » → un lot chaud.
Replace directement dans la projection - brise les SLO en ligne.
15) Chèque de mise en œuvre (0-45 jours)
0-10 jours
Définir les événements de domaine et leurs clés (granules d'ordre).
Déployer Schema Registry et approuver la stratégie de compatibilité.
Ajouter outbox/inbox à 1-2 services ; minimum CloudEvents-envelope.
11-25 jours
Entrez retry/DLQ, backoff, idempotence des gestionnaires.
Dashboards : lag/age/end-to-end ; burn-rate alerte.
Documentation des événements (catalogue), owner's et processus de ronflement des schémas.
26-45 jours
Un repli/réarrangement de la première projection ; runbook de relais et backfill.
Politiques de sécurité (TLS, ACL, PII), rétentions, procédures RGPD.
Jours de jeu et de chaos réguliers pour le courtier et les consommateurs.
16) Métriques de maturité
Replay/Backfill est possible sans downtime ; il y a un runbook éprouvé 'et
100 % des événements de domaine sont décrits par des schémas et enregistrés.
Outbox/inbox couvre tous les vendeurs/consumers de Tier-0/1.
SLO : p95 fin-à-fin latitude et consommation lag dans les objectifs ≥ 99 %.
Versioning : nouveaux champs - pas de tranches ; les vieux consommateurs ne tombent pas.
Sécurité : TLS + mTLS, ACL per topic, journaux d'accès, politique PII/rétention.
17) Mini-extraits
Kafka Producteur (publication fiable, idées) :properties acks=all enable.idempotence=true max.in.flight.requests.per.connection=1 compression.type=zstd linger.ms=5
Processeur de consommation (idempotence, pseudo-code) :
python if inbox.contains(event_id): return # дедуп process(event) # побочные эффекты детерминированы inbox.commit(event_id) # atomically with side-effect commit_offset()
RabbitMQ Retry via DLX (idée) :
- `queue: tasks` → on nack → DLX `tasks. retry. 1m '(TTL = 60s) → retour à' tasks '; ci-après « 5m/15m ».
18) Conclusion
L'EDA transforme l'intégration en un flux de faits commerciaux avec des contrats clairs et une cohérence gérée. Construisez les fondations : schémas + registre, outbox/inbox, clés d'ordre, processeurs idempotent, SLO et observabilité, rétention et repli sécurisés. Les événements deviendront alors votre « source de vérité » pour l'échelle, les analystes et les nouvelles fiches - sans liens fragiles et migrations nocturnes.