Ereignisarchitektur
Ereignisarchitektur (EDA)
1) Was ist ein Ereignis und warum EDA
Ein Ereignis ist eine unveränderliche Tatsache, die bereits in einer Domäne aufgetreten ist („PlayerVerified“, „PaymentCaptured“). Die EDA baut Integrationen rund um die Veröffentlichung dieser Fakten und Reaktionen darauf auf:- schwache Konnektivität der Dienste,
- Skalierung der Verbraucher unabhängig,
- Replikation/Restrukturierung von Projektionen,
- Ein transparentes Audit.
EDA hebt synchrone APIs nicht auf - es ergänzt sie, indem es dienstübergreifende Abhängigkeiten in eine asynchrone Schicht bringt.
2) Arten von Ereignissen
Domain: wichtige geschäftliche Fakten (OrderPlaced, BonusGranted).
Integration: „Snapshots „/Änderungen für externe Systeme (UserUpdated, WalletBalanceChanged).
Technisch: Lebenszyklus und Telemetrie (Heartbeat, PipelineFailed).
Befehle (nicht Ereignisse, aber in der Nähe): Anweisungen „machen X“ (CapturePayment).
Empfehlung: Domain-Events - primär; Integration wird durch Projektionen für bestimmte Verbraucher gebildet.
3) Veranstaltungsverträge und -pläne
Схема: Avro/Protobuf/JSON Schema + Schema Registry; Interoperabilitätsstrategie: „BACKWARD“ für die Verbraucherentwicklung, „FULL“ für kritische Themen.
CloudEvents (id, source, type, time, subject, datacontenttype) - einheitliche Titel.
Erforderliche Metadaten: 'event _ id' (ULID/UUID), 'occurred _ at', 'producer', 'schema _ version', 'correlation _ id '/' causation _ id', 'idempotency _ key'.
Versionierung: Add-Only-Felder, Verbot von Umbenennungen/semantischen Unterbrechungen; Neue Typen - Neue Themen/Typen.
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) Lieferung, Bestellung und Konsistenz
At-least-once als Standard → erfordert Idempotenz der Verarbeiter.
Ordnung: innerhalb der Partei (Kafka) oder Warteschlange (RabbitMQ) garantiert, kann aber bei Retrays gestört werden; Der Ereignisschlüssel muss das Ordnungsgranulat der Domäne widerspiegeln (z. B. 'player _ id').
Konsistenz: für Geld/Kredite - nur durch Zeitschriften/Saga/Entschädigung; Vermeiden Sie LWW.
Lesemodell: Projektionen und Caches können eventual sein - zeigen Sie „Aktualisierung im Gange“... und verwenden Sie RNOT-Strategien für strenge Pfade.
5) Outbox/Inbox и CDC
Outbox: Der Dienst schreibt die Tatsache in seine Datenbank und in die Outbox-Tabelle in einer einzigen Transaktion → der Worker veröffentlicht sie im Bus.
Inbox: Der Verbraucher speichert 'event _ id' mit dem Verarbeitungsergebnis für Deduplex.
CDC (Change Data Capture): Änderungsfluss von der DB (Binlog/WAL) zum Bus, um Integrationen ohne Anwendungsänderungen aufzubauen.
Idempotency: Verarbeitung durch 'idempotency _ key '/' event _ id', keine Veränderung der Außenwelt vor der Fixierung.
6) CQRS и Event Sourcing
CQRS: Wir teilen das Write-Modell und die Read-Projektionen; Projektionen werden aus Ereignissen konstruiert und können zurückbleiben.
Event Sourcing: Aggregatstatus = Rollup seiner Ereignisse. Vorteile: vollständiges Audit/Replays; Nachteile: Komplexität der Migrationen/Schemata/Snapshots.
Praxis: ES - nicht überall, sondern dort, wo Geschichte und Entschädigung wichtig sind; CQRS - fast immer in EDA.
7) Sagas: Orchestrierung und Choreographie
Orchestrierung: Der Koordinator sendet Befehle und wartet auf Ereignis-Antworten; Geeignet für komplexe Prozesse (KYC→Deposit→Bonus).
Choreographie: Die Gottesdienste reagieren auf die Ereignisse des anderen; einfacher, aber schwieriger zu verfolgen.
Definieren Sie immer Entschädigungen und Fristen für Schritte.
8) Gestaltung von Topologien (Kafka/RabbitMQ)
Kafka
Topic per domain event: 'payments. captured. v1`, `players. verified. v1`.
Partitionierungsschlüssel: 'player _ id '/' wallet _ id' - wo die Reihenfolge wichtig ist.
`replication. factor=3`, `min. insync. replicas = 2', producer 'acks = all'.
Retention: nach Zeit (z.B. 7-90 Tage) und/oder Compaction (letzter Zustand nach Schlüssel).
Topics für Retry und DLQ mit Backoff.
RabbitMQ
Exchanges: `topic`/`direct`, routing key `payments. captured. v1`.
Für ein breites Fan-out - 'topic' + mehrere Warteschlangen; für RPCs/Befehle separate Warteschlangen.
Quorum Queues für HA; TTL + Dead-Letter-Austausch für Retrays.
9) Beobachtbarkeit und SLO EDA
SLI/SLO:- Ende-zu-Ende-Latenz (occurred_at → verarbeitet): p50/p95/p99.
- Lag/Alter: Verbraucherrückstand (Kafka-Verbraucherlag, Rabbit-Backlog-Alter).
- Throughput Veröffentlichung/Verarbeitung.
- DLQ-Rate und Wiederholungsanteil.
- Erfolg des Geschäftsbetriebs (z.B. „Einzahlung bestätigt ≤ 5c“).
- Korrelation der Ereignisse über 'trace _ id '/' correlation _ id' (OTel).
- Instanzen (exemplars) aus den Metriken der → Spur.
- Dashboards „Producer→Broker→Consumer“ mit Burn-Rate-Alerts.
10) Replik, Retention und Backfill
Replays zum Neuaufbau von Projektionen/Bugfixes: Fahren Sie in eine neue Projektion/Neuplatzierung und schalten Sie dann das Lesen um.
Retention: rechtliche/geschäftliche Anforderungen (DSGVO/PCI); sensible Felder - verschlüsseln und/oder tokenisieren.
Backfill: einmalige Themen/Warteschlangen, klare RPS-Grenzen, um den Prod nicht zu strangulieren.
11) Sicherheit und Compliance
TLS in-transit, mTLS für interne Kunden.
Berechtigung: per-topic/per-exchange ACL; multitenancy über namespace/vhost.
PII: Minimierung der Felder im Ereignis; envelope Metadaten separat, Nutzlasten bei Bedarf verschlüsseln.
Prüfung des Zugriffs auf Ereignisse, Verbot von „alles möglichen“ Schlüsseln.
Richtlinien retenschna und Recht auf Löschung (DSGVO): Entweder Datenverweise oder Tombstone-Ereignisse und Löschung in Projektionen speichern.
12) Prüfung in EDA
Vertragstests: Verbraucher validieren ihre Erwartungen an Systeme (consumer-driven).
Replay-Tests: Führen Sie eine historische Stichprobe durch einen neuen Handler/eine neue Version des Schemas.
Chaos-Szenarien: Verzögerung/Verlust des Brokers, fallende Knoten, Verbraucherrückstand → SLOs bleiben im Rahmen.
Rauch im CI: eine kurze End-to-End-Pipeline zu temporären Themen.
13) Migration von „CRUD-Integrationen → EDA“
1. Identifizieren Sie Domain-Fakten.
2. Implementieren Sie outbox in die Quelldienste.
3. Veröffentlichen Sie minimale Domänenereignisse und verbinden Sie 1-2 Projektionen.
4. Deaktivieren Sie schrittweise punktsynchrone Integrationen, indem Sie sie durch Abonnements ersetzen.
5. Geben Sie Schema Registry und die Kompatibilitätsrichtlinie ein.
6. Erweitern Sie Add-Only-Ereignisse mit Feldern; Brüche - nur durch neue Typen.
14) Anti-Muster
Ereignisse = „DTO API“ (zu fett, abhängig vom internen Modell) - brechen die Verbraucher.
Fehlende Schema Registry und Kompatibilität - „fragile“ Integrationen.
Das Veröffentlichen aus dem Code und das Schreiben in die Datenbank ist nicht atomar (kein Outbox) - Sie verlieren Ereignisse.
„Exactly-once everywhere“ - hoher Preis ohne Nutzen; besser at-least-once + idempotence.
Ein „universeller“ Partitionierungsschlüssel → eine heiße Partei.
Replay direkt in die Prod-Projektion - bricht Online-SLOs.
15) Implementierung Checkliste (0-45 Tage)
0-10 Tage
Definieren Sie Domänenereignisse und ihre Schlüssel (Pellets der Reihenfolge).
Erweitern Sie Schema Registry und genehmigen Sie die Kompatibilitätsstrategie.
Fügen Sie Outbox/Inbox zu 1-2 Diensten hinzu; Minimum CloudEvents-envelope.
11-25 Tage
Retry/DLQ, Backoff, Idempotenz der Handler eingeben.
Dashboards: lag/age/end-to-end; burn-rate alert.
Dokumentation der Ereignisse (Katalog), Eigentümer's und Prozesse Revue Schaltungen.
26-45 Tage
Replikation/Restrukturierung der ersten Projektion; runbook replica und backfill.
Sicherheitsrichtlinien (TLS, ACL, PII), Retention, DSGVO-Verfahren.
Regelmäßige Chaos-und Spieltage für Broker und Verbraucher.
16) Reifegradkennzahlen
100% der Domain-Events sind schematisch beschrieben und registriert.
Outbox/Inbox deckt alle Tier-0/1-Produzenten/-Konsumenten ab.
SLO: p95 Ende-zu-Ende-Latenz und Verbraucher lag innerhalb der Ziele ≥ 99%.
Replays/Backfill sind ohne Downtime machbar; es gibt verifizierte runbooks' und.
Versionierung: neue Felder - keine Abbrüche; Die alten Konsumenten fallen nicht.
Sicherheit: TLS + mTLS, ACL pro Thema, Zugriffsprotokolle, PII/Retention Policy.
17) Mini-Schnipsel
Kafka Producer (zuverlässige Publikation, Ideen):properties acks=all enable.idempotence=true max.in.flight.requests.per.connection=1 compression.type=zstd linger.ms=5
Consumer-Handler (Idempotenz, Pseudocode):
python if inbox.contains(event_id): return # дедуп process(event) # побочные эффекты детерминированы inbox.commit(event_id) # atomically with side-effect commit_offset()
RabbitMQ Retry via DLX (Idee):
- `queue: tasks` → on nack → DLX `tasks. retry. 1m'(TTL = 60s) → Rückkehr zu „Aufgaben“; ferner „5m/15m“.
18) Schlussfolgerung
EDA verwandelt Integrationen in eine Flut von geschäftlichen Fakten mit klaren Verträgen und überschaubarer Konsistenz. Bauen Sie die Grundlage auf: Schemata + Registrierung, Outbox/Inbox, Auftragsschlüssel, idempotente Handler, SLO und Beobachtbarkeit, sichere Retention und Replays. Dann werden die Ereignisse zu Ihrer „Quelle der Wahrheit“ für Skalierung, Analyse und neues Verständnis - ohne fragile Verbindungen und nächtliche Migrationen.