CQRS und Lese-/Schreibtrennung
Was ist CQRS
CQRS (Command Query Responsibility Segregation) ist ein architektonischer Ansatz, der das Datenmodell von den Komponenten trennt, die für Schreiben (commands) und Lesen (queries) verantwortlich sind.
Die Idee: Der Zustandsänderungsprozess wird für gültige Invarianten und Transaktionen optimiert und das Lesen für schnelle, gezielte Projektionen und Skalierungen.
Warum es notwendig ist
Leseleistung: materialisierte Projektionen für bestimmte Szenarien (Bänder, Berichte, Kataloge).
Kritische Wegstabilität: Die Aufnahme ist von „schweren“ Joins und Aggregaten isoliert.
Freie Speicherauswahl: OLTP zum Schreiben, OLAP/Cache/Suchmaschinen zum Lesen.
Beschleunigte Evolution: Fügen Sie neue Ansichten hinzu, ohne das Risiko einzugehen, Transaktionen zu „brechen“.
Beobachtbarkeit und Audit (insbesondere in Verbindung mit Event Sourcing): Es ist einfacher, einen Zustand wiederherzustellen und neu zu spielen.
Wann anzuwenden (und wann nicht)
Geeignet, wenn:- Lesungen mit unterschiedlichen Datenschnitten und komplexer Aggregation setzen sich durch.
- Der kritische Aufzeichnungspfad muss subtil und vorhersehbar sein.
- Sie benötigen verschiedene SLO/SLAs zum Lesen und Schreiben.
- Es ist erforderlich, die Domänenlogik der Aufzeichnung von den Analyse-/Suchanforderungen zu isolieren.
- Die Domäne ist einfach, die Last ist niedrig; CRUD kommt zurecht.
- Eine starke Konsistenz zwischen Lesen und Schreiben ist für alle Szenarien obligatorisch.
- Das Team ist unerfahren und die operative Komplexität nicht akzeptabel.
Grundbegriffe
Befehl: Absicht, den Status zu ändern ('CreateOrder', 'CapturePayment'). Prüft Invarianten.
Abfrage: Ruft Daten ab ('GetOrderById', 'ListUserTransactions'). Ohne Nebenwirkungen.
Aufzeichnungsmodell: Aggregate/Invarianten/Transaktionen; Speicher - Relational/Key-Value/Event Log.
Lesemodell (Projektion): materialisierte Tabellen/Indizes/Cache, asynchron synchronisiert.
Konsistenz: oft eventual zwischen Schreiben und Lesen; kritische Pfade - durch direktes Lesen aus dem Schreibmodell.
Architektur (Skelett)
1. Write-Service: nimmt Befehle entgegen, validiert Invarianten, erfasst Änderungen (DB oder Ereignisse).
2. Outbox/CDC: garantierte Veröffentlichung von Änderungen.
3. Projektionsprozessoren: Hören Sie Ereignisse/CDC und aktualisieren Sie Lesemodelle.
4. Read-Service: dient Queries aus materialisierten Ansichten/Caches/Suchen.
5. Sagas/Orchestrierung: Sie koordinieren aggregatübergreifende Prozesse.
6. Beobachtbarkeit: Projektionsfehler, Erfolgsquote, DLQ.
Entwurf eines Aufzeichnungsmodells
Aggregate: klare Transaktionsgrenzen (z.B. 'Order', 'Payment', 'UserBalance').
Invarianten: Formalisieren (Geldbeträge ≥ 0, Einzigartigkeit, Grenzen).
Die Befehle sind idempotent nach Schlüssel (z.B. 'idempotency _ key').
Transaktionen sind in der Reichweite minimal; externe Nebenwirkungen - durch outbox.
Beispiel für einen Befehl (Pseudo-JSON)
json
{
"command": "CapturePayment",
"payment_id": "pay_123",
"amount": 1000,
"currency": "EUR",
"idempotency_key": "k-789",
"trace_id": "t-abc"
}
Gestaltung des Lesemodells
Gehen Sie von Anfragen aus: Welche Bildschirme/Berichte werden benötigt?
Denormalisierung ist zulässig: Das Read-Modell ist ein „optimierter Cache“.
Mehrere Projektionen für verschiedene Aufgaben: Suche (OpenSearch), Berichte (Säulenspeicher), Karten (KV/Redis).
TTL und Reassembly: Projektionen müssen sich von der Quelle erholen können (Replikate von Ereignissen/Schnappschüssen).
Konsistenz und UX
Eventual consistency: Die Schnittstelle kann alte Daten kurz anzeigen.
UX-Muster: „Daten werden aktualisiert“..., optimistische UI, Timing-Indikatoren, Blockieren gefährlicher Aktivitäten bis zur Bestätigung.
Für Transaktionen, die eine starke Konsistenz erfordern (z. B. das Anzeigen eines genauen Saldos vor der Abschreibung), lesen Sie direkt aus dem Schreibmodell.
CQRS und Event Sourcing (optional)
Event Sourcing (ES) speichert Ereignisse und der Aggregatzustand ist das Ergebnis ihrer Faltung.
Das CQRS + ES-Bündel bietet ein perfektes Audit und eine einfache Nachrüstung von Projektionen, erhöht jedoch die Komplexität.
Alternative: herkömmliche OLTP-DB + Outbox/CDC → Projektion.
Replikation: Outbox und CDC
Outbox (in einer Transaktion): Domänenänderungen aufzeichnen + Ereignis in Outbox aufzeichnen; Kneipe liefert an den Reifen.
CDC: Lesen aus dem DB-Protokoll (Debezium usw.) → Umwandlung in Domänenereignisse.
Garantien: Standardmäßig at-least-once, müssen Verbraucher und Projektionen idempotent sein.
Speicherauswahl
Write: relational (PostgreSQL/MySQL) für Transaktionen; KV/Document - wo Invarianten einfach sind.
Read:- KV/Redis - Karten und schnelle Schlüssellesungen;
- Suche (OpenSearch/Elasticsearch) - Suche/Filter/Facetten;
- Spalten (ClickHouse/BigQuery) - Berichte;
- Cache auf dem CDN - öffentliche Verzeichnisse/Inhalte.
Integrationsmuster
API-Schicht: separate Endpunkte/Dienste für 'commands' und 'queries'.
Idempotenz: Operationsschlüssel im Header/Körper; Speicherung von Recent-Keys mit TTL.
Sagas/Orchestrierung: Auszeiten, Kompensationen, Wiederholbarkeit der Schritte.
Backpressure: Begrenzung der Parallelität von Projektionsprozessoren.
Beobachtungsstand
Write-Metriken: p95/99 Befehlslatenz, Anteil erfolgreicher Transaktionen, Validierungsfehler.
Metriken lesen: p95/99 Abfragen, Hit-Rate des Cache, Last auf dem Suchcluster.
Projektionslag (Zeit und Nachrichten), DLQ-Rate, Prozentsatz der Deduplizierungen.
Trace: „trace _ id“ durchläuft den Befehl → outbox → die Projektion → query.
Sicherheit und Compliance
Rechtetrennung: verschiedene Scopes/Rollen zum Schreiben und Lesen; Prinzip der geringsten Privilegien.
PII/PCI: in Projektionen minimieren; At-Rest/In-Flight-Verschlüsselung; Maskieren.
Audit: Commit Team, Schauspieler, Ergebnis, 'trace _ id'; WORM-Archive für kritische Domains (Payments, KYC).
Prüfung
Vertragstests: für Befehle (Fehler, Invarianten) und Abfragen (Formate/Filter).
Projektionstests: Reichen Sie eine Reihe von Ereignissen/CDC ein und überprüfen Sie das resultierende Lesemodell.
Chaos/Latenz: Injizieren von Verzögerungen in Projektionsprozessoren; UX-Prüfung bei Verzögerung.
Replayability: Reassemblieren von Projektionen auf einem Snap-Shot/Log-Stand.
Migration und Evolution
Neue Felder - additiv im Ereignis/CDC; Read-Modelle werden neu zusammengestellt.
Double Write (Dual-Write) beim Redesign von Schaltungen; Wir halten die alten Projektionen bis zum Umschalten.
Versionierung: 'v1 '/' v2' von Ereignissen und Endpunkten, Sunset-Plan.
Feature flags: Eingabe neuer Queries/Projektionen über den Kanarienvogel.
Antimuster
CQRS „um der Mode willen“ in einfachen CRUD-Diensten.
Starre synchrone Schreibabhängigkeit des Lesens (tötet Isolation und Robustheit).
Ein Index für alles: Mischen Sie heterogene Abfragen in einem einzigen Read-Store.
Es gibt keine Idempotenz bei Projektionen → Doppelungen und Divergenzen.
Nicht wiederherstellbare Projektionen (keine Replikate/Snapshots).
Beispiele für Domains
Zahlungen (Online-Service)
Schreiben: 'Authorize', 'Capture', 'Refund' in der Transaktionsdatenbank; outbox veröffentlicht „payment“.
Read:- Redis „Zahlungskarte“ für UI;
- ClickHouse für Berichte;
- OpenSearch, um Transaktionen zu finden.
- Kritischer Pfad: Autorisierung ≤ 800 ms p95; Lesekonsistenz für UI - eventual (bis zu 2-3 s).
KYC
Schreiben: Startbefehle/Statusupdate; Speicherung der PII in einer sicheren Datenbank.
Read: vereinfachte Projektion von Status ohne PII; PII wird bei Bedarf punktuell hochgezogen.
Sicherheit: Verschiedene Scopes, um den Status zu lesen und auf Dokumente zuzugreifen.
Bilanzen (iGaming/Finanzen)
Schreiben: „UserBalance“ -Aggregat mit atomaren Inkrementen/Dekrementen; idempotente Schlüssel für die Operation.
Lesen: Cache für „schnelles Gleichgewicht“; für die Abbuchung - direktes Lesen aus dem Schreiben (strikte Konsistenz).
Saga: Ein-/Auszahlungen werden durch Ereignisse koordiniert, bei Ausfällen durch Kompensationen.
Checkliste Umsetzung
- Die Aggregate und Invarianten des Write-Modells sind hervorgehoben.
- Schlüsselfragen werden identifiziert und Projektionen für sie entworfen.
- Outbox/CDC und idempotente Projektionsprozessoren konfiguriert.
- Es gibt einen Plan, Projektionen neu zusammenzusetzen (Snapshot/Replay).
- SLO: Befehlslatenz, Projektionsfehler, Verfügbarkeit einzeln lesen/schreiben.
- Die Zugriffsrechte sind getrennt und die Datenverschlüsselung ist implementiert.
- Alerts on DLQ/Lag/Deduplizierungsfehler.
- Tests: Verträge, Projektionen, Chaos, Repliken.
FAQ
Ist Event Sourcing für CQRS obligatorisch?
Nein. Sie können auf einer normalen DB + Outbox/CDC aufbauen.
Wie umgehen mit Nicht-Synchronisierung?
Explizit UX gestalten, den Lag der Projektionen messen, kritische Operationen aus dem Schreiben lesen lassen.
Kann ich Write und Read im selben Service halten?
Ja, physische Trennung - optional; Die logische Aufteilung der Verantwortung ist obligatorisch.
Was ist mit Transaktionen zwischen Aggregaten?
Durch Sagen und Ereignisse; Vermeiden Sie verteilte Transaktionen, wenn möglich.
Ergebnis
CQRS entfesselt die Hände: ein schlanker, zuverlässiger Schreibpfad mit klaren Invarianten und schnellen, gezielten Lesungen aus materialisierten Projektionen. Das steigert die Produktivität, vereinfacht die Evolution und macht das System belastbarer - wenn Konsistenz, Beobachtbarkeit und Migrationen diszipliniert gehandhabt werden.