CQRS și separarea citire/scriere
Ce este CQRS
CQRS (Command Query Responsibility Segregation) este o abordare arhitecturală care separă modelul de date și componentele responsabile pentru scriere (comenzi) și citire (interogări).
Ideea: procesul de schimbare a statului este optimizat pentru invarianții și tranzacțiile valide și citirea pentru proiecții și scalări rapide și direcționate.
key> Comenzile schimbă starea și returnează rezultatul operațiunii. Cererile sunt doar citite și nu au efecte secundare.
De ce ai nevoie de ea
Citiți performanța: proiecții materializate pentru scenarii specifice (benzi, rapoarte, cataloage).
Stabilitatea căii critice: înregistrarea izolată de „grele” se alătură și agregate.
Libertatea de selecție a stocării: OLTP pentru scris, OLAP/cache/motoare de căutare pentru citire.
Evoluție accelerată: Adăugați noi opinii fără riscul de a „rupe” tranzacțiile.
Observabilitate și audit (în special împreună cu Event Sourcing): este mai ușor să recuperați și să reluați starea.
Când se aplică (și când nu)
Potrivit dacă:- Prevalează citirile cu felii de date diferite și agregarea complexă.
- Calea critică de înregistrare trebuie să fie subtilă și previzibilă.
- Pentru citire și scriere sunt necesare diferite SLO/SLA.
- Este necesară izolarea logicii de scriere a domeniului de nevoile analitice/de căutare.
- Domeniul este simplu, sarcina este scăzută; CRUD se descurcă.
- Coerența puternică între citire și scriere este obligatorie pentru toate scenariile.
- Echipa este lipsită de experiență și complexitatea operațională este inacceptabilă.
Concepte de bază
Command-Intenționează să schimbe starea ('CreateOrder', 'CapturePayment'). Verifică invarianţii.
Interogare-recuperează date ('GetOrderById',' ListUserTransactions '). Fără efecte secundare.
Model de înregistrare: agregate/invarianți/tranzacții; stocare - relational/key-value/event log.
Citiți (proiecție) model: tabele materializate/indici/cache, sincronizate asincron.
Coerență: Adesea, eventual între înregistrare și lectură; căi critice - prin citirea directă din modelul de scriere.
Arhitectură (schelet)
1. Write-service: acceptă comenzi, validează invarianți, captează modificări (baze de date sau evenimente).
2. Outbox/CDC: publicarea garantată a faptului modificărilor.
3. Procesoare de proiecție: Ascultați evenimente/CDC și actualizați modele de citire.
4. Read-service: servește interogări din vizualizări/cache-uri/căutări materializate.
5. Sagas/orchestrație: coordonează procesele cross-agregate.
6. Observabilitate: decalaj de proiecție, procent de aplicații de succes, DLQ.
Proiectarea unui model de înregistrare
Agregate: limite de tranzacție clare (de exemplu, „Comandă”, „Plată”, „UserBalance”).
Invarianți: formalizați (sume monetare ≥ 0, unicitate, limite).
Comenzile sunt idempotente prin cheie (de exemplu, „idempotency _ key”).
Tranzacțiile au un domeniu de aplicare minim; efecte secundare externe - prin outbox.
Exemplu de comandă (Pseudo-JSON)
json
{
"command": "CapturePayment",
"payment_id": "pay_123",
"amount": 1000,
"currency": "EUR",
"idempotency_key": "k-789",
"trace_id": "t-abc"
}
Proiectarea unui model de citire
Porniți de la întrebări: ce ecrane/rapoarte sunt necesare?
Denormalizarea este acceptabilă: read-model - „cache optimizat”.
Mai multe proiecții pentru diferite sarcini: căutare (OpenSearch), rapoarte (columnar storage), carduri (KV/Redis).
TTL și reasamblare: proiecțiile trebuie să poată fi recuperate de la sursă (reluarea evenimentului/instantanee).
Consistență și UX
O eventuală consecvență: interfața poate afișa date vechi pentru o perioadă scurtă de timp.
Modele UX: „datele sunt actualizate”..., UI optimist, indicatori de sincronizare, blocarea acțiunilor periculoase până la confirmare.
Pentru operațiunile care necesită consistență puternică (de exemplu, care arată un echilibru precis înainte de a scrie), citiți direct din modelul de scriere.
CQRS și Event Sourcing (opțional)
Event Sourcing (ES) stochează evenimente, iar starea agregatului este rezultatul convoluției acestora.
Pachetul CQRS + ES oferă un audit ideal și o reasamblare ușoară a proiecțiilor, dar crește complexitatea.
Alternativă: bază de date OLTP regulată + outbox/CDC → proiecții.
Replicare: Outbox și CDC
Outbox (într-o singură tranzacție): scrierea schimbărilor de domeniu + scrierea unui eveniment în outbox; editorul livrează anvelopei.
CDC: citirea din jurnalul de baze de date (Debezium etc.) → transformarea în evenimente de domeniu.
Garanții: în mod implicit, cel puțin o dată, consumatorii și proiecțiile trebuie să fie idempotente.
Selecție de stocare
Scrieți: relațional (PostgreSQL/MySQL) pentru tranzacții; KV/Document - în cazul în care invarianții sunt simple.
Citeşte:- KV/Redis - carduri și lecturi rapide de chei;
- Căutare (OpenSearch/Elasticsearch) - căutare/filtre/fațete;
- Coloana (ClickHouse/BigQuery) - rapoarte;
- Cache pe CDN - directoare publice/conținut.
Modele de integrare
strat API: puncte finale/servicii separate pentru 'comenzi' și 'interogări'.
Idempotența: cheia operațiunii în antet/corp; stocarea cheilor recente cu TTL.
Sagas/orchestrație: timeout, compensații, repetabilitate pas.
Backpressure-Limitează paralelismul procesoarelor de proiecție.
Observabilitate
Scrieți valori: latențele comenzii p95/99, procentajul tranzacțiilor reușite, erorile de validare.
Citiți valorile: p95/99 cereri, cache-ul hit-rate, încărcați pe clusterul de căutare.
Decalaj de proiecție (timp și mesaje), rată DLQ, procent de eliminare a duplicatelor.
Urmărire: 'trace _ id' trece prin comanda → outbox → proiecția interogării →.
Siguranță și conformitate
Separarea drepturilor: diferite scopuri/roluri pentru scriere și lectură; principiul celui mai mic privilegiu.
PII/PCI: minimizați în proiecții; criptarea în repaus/în timpul zborului; mascare.
Audit: Remediați echipa, actorul, rezultatul, 'trace _ id'; Arhive WORM pentru domenii critice (plăți, KYC).
Testare
Teste de contract: pentru comenzi (erori, invarianți) și interogări (formate/filtre).
Teste de proiecție: trimiteți o serie de evenimente/CDC și verificați modelul final de citire.
Haos/latență: injectarea latenței în procesoarele de proiecție; Verificare UX la lag.
Replayability: reasamblarea proiecțiilor pe suport din instantanee/jurnal.
Migrații și evoluție
Noi domenii - aditiv în eveniment/CDC; citi modele sunt reconstruite.
Scriere dublă la reproiectarea circuitelor; țineți proiecțiile vechi până la comutare.
Versioning: „v1 ”/„ v2” evenimente și puncte finale, Sunset-plan.
Feature flags: introducerea de noi interogări/proiecții de-a lungul canarului.
Anti-modele
CQRS „de dragul modei” în servicii simple CRUD.
Dependența sincronă de citire-scriere (ucide izolarea și persistența).
Un indice pentru toți: amestecarea interogărilor eterogene într-un singur magazin de citire.
Proiecțiile nu au idempotență → duplicate și discrepanțe.
Proiecții nerecuperabile (fără reluare/instantanee).
Exemple de domenii
Plăți (serviciu online)
Scrieți: „Autorizare”, „Captură”, „Rambursare” în baza de date tranzacțională; outbox publică plata. '.
Citeşte:- Redis „card de plată” pentru UI;
- ClickHouse pentru raportare;
- OpenSearch pentru a căuta tranzacții.
- Cale critică: autorizare ≤ 800 ms p95; citiți consistența pentru UI - eventual (până la 2-3 s).
KYC
Scrieți: comenzi pentru a începe/actualiza starea; Stocare PII într-o bază de date securizată.
Citește: proiecție ușoară a stărilor fără PII; PII este înăsprită, dacă este necesar.
Securitate: diferite scopuri privind citirea stării și accesarea documentelor.
Bilanțuri (iGaming/Finance)
Scrieți: „UserBalance” agregat cu incremente/decremente atomice; chei idempotente pentru chirurgie.
Citește: cache pentru „echilibru rapid”; pentru write-off - citire directă din scriere (consistență strictă).
Saga: depozitele/concluziile sunt coordonate de evenimente, în caz de eșecuri - compensații.
Lista de verificare a implementării
- Agregatele și invarianții modelului de scriere sunt evidențiați.
- Interogările cheie sunt definite și proiecțiile proiectate pentru ele.
- Outbox/CDC și procesoarele de proiecție idempotente sunt configurate.
- Există un plan de instantaneu/reluare.
- SLO: latență comandă, lag proiecție, citire/scriere disponibilitate separat.
- Drepturile de acces separate și criptarea datelor implementate.
- DLQ alerte/lag/defecțiuni de eliminare a duplicatelor.
- Teste: Contracte, Proiecții, Haos, Replay.
ÎNTREBĂRI FRECVENTE
Este Event Sourcing obligatoriu pentru CQRS?
Nu, nu este. Puteți construi pe o bază de date obișnuită + outbox/CDC.
Cum să se ocupe de desincronizare?
Proiectați în mod explicit UX, măsurați decalajul de proiecție, lăsați operațiile critice să citească din scriere.
Este posibil să păstrați atât scrie și citit în același serviciu?
Da, separarea fizică este opțională; o împărțire logică a responsabilităților este obligatorie.
Cum rămâne cu tranzacțiile între agregate?
Prin saga și evenimente; Evitați tranzacțiile distribuite, dacă este posibil.
Rezultat
CQRS dezleagă mâinile: o cale de scriere subțire, fiabilă, cu invarianți clari și lecturi rapide, direcționate din proiecții materializate. Acest lucru crește productivitatea, simplifică evoluția și face sistemul mai rezistent la stres - dacă consecvența, observabilitatea și migrațiile sunt disciplinate.