GH GambleHub

CQRS et séparation lecture/écriture

Qu'est-ce que le CQRS

CQRS (Command Query Responsibility Segregation) est une approche architecturale qui sépare le modèle de données et les composants responsables de l'écriture (commands) et de la lecture (queries).
L'idée : le processus de changement d'état est optimisé pour les invariants et les transactions valides, et la lecture pour les projections et les mises à l'échelle rapides et ciblées.

💡 Les commandes modifient l'état et renvoient le résultat de l'opération. Les demandes sont seulement lues et n'ont pas d'effets secondaires.

Pourquoi c'est nécessaire

Performances de lecture : projections matérialisées pour des scénarios spécifiques (bandes, rapports, catalogues).
Stabilité de la voie critique : l'enregistrement est isolé des joines et des agrégats « lourds ».
Liberté de sélection de stockage : OLTP pour l'écriture, OLAP/cache/moteurs de recherche pour la lecture.
Évolution accélérée : ajoutez de nouvelles vues sans risquer de « casser » les transactions.
Observation et audit (en particulier en liaison avec Event Sourcing) : il est plus facile de récupérer et de rejouer la condition.


Quand appliquer (et quand pas)

Convient si :
  • Les lectures avec différentes tranches de données et l'agrégation complexe prévalent.
  • Le chemin d'enregistrement critique doit être subtil et prévisible.
  • Vous avez besoin de SLO/SLA différents pour lire et écrire.
  • Il faut isoler la logique de domaine de l'enregistrement des besoins analytiques/de recherche.
Ne convient pas si :
  • Le domaine est simple, la charge est faible ; Le CRUD s'en occupe.
  • Une forte cohérence entre lecture et écriture est indispensable pour tous les scénarios.
  • L'équipe est inexpérimentée et la complexité opérationnelle est inacceptable.

Concepts de base

Commande (Command) : Intention de modifier l'état (CreateOrder, CapturePayment). Vérifie les invariants.
Requête (Query) : Récupère les données ('GetOrderById', 'ListUserTransactions'). Pas d'effets secondaires.
Modèle d'enregistrement : agrégats/invariants/transactions ; stockage - relationnel/clé-valeur/journal d'événements.
Modèle de lecture (projection) : les tables/index/cache matérialisées, sont synchronisées de manière asynchrone.
Cohérence : souvent un événement entre l'enregistrement et la lecture ; les chemins critiques sont par lecture directe à partir du modèle write.


Architecture (squelette)

1. Service d'écriture : accepte les commandes, valide les invariants, enregistre les changements (OBD ou événements).
2. Outbox/CDC : Publication garantie des changements.
3. Processeurs de projection : Écoutez les événements/CDC et mettez à jour les modèles de lecture.
4. Service de lecture : sert queries à partir de représentations matérialisées/cache/recherche.
5. Saga/orchestration : coordonne les processus d'agrégats croisés.
6. Observabilité : Projection, pourcentage d'applications réussies, DLQ.


Conception du modèle d'enregistrement

Agrégats : limites de transaction claires (par exemple, 'Order', 'Payment', 'UserBalance').
Invariants : formaliser (sommes d'argent ≥ 0, unicité, limites).
Les commandes sont idempotentes par clé (par exemple, 'idempotency _ key').
Les transactions sont de portée minimale ; effets secondaires externes - via outbox.

Exemple de commande (pseudo-JSON)

json
{
"command": "CapturePayment",
"payment_id": "pay_123",
"amount": 1000,
"currency": "EUR",
"idempotency_key": "k-789",
"trace_id": "t-abc"
}

Concevoir un modèle de lecture

Éloignez-vous des demandes : quels écrans/rapports sont nécessaires ?
La dénormalisation est valide : le modèle de lecture est « cache optimisé ».
Plusieurs projections pour différentes tâches : recherche (OpenSearch), rapports (stockage de colonnes), cartes (KV/Redis).
TTL et réassemblage : les projections doivent pouvoir être récupérées à partir de la source (repli d'événements/snapshots).


Cohérence et UX

Consistency eventual : l'interface peut afficher brièvement les anciennes données.
Modèles UX : « données mises à jour »..., UI optimiste, indicateurs de synchronisation, blocage des actions dangereuses avant confirmation.
Pour les opérations qui nécessitent une forte cohérence (par exemple, l'affichage d'un solde précis avant d'être débité), lisez directement à partir du modèle write.


CQRS et Event Sourcing (facultatif)

Event Sourcing (ES) stocke les événements et l'état de l'agrégat est le résultat de leur convolution.
Le lien CQRS + ES permet un audit parfait et un recalage facile des projections, mais augmente la complexité.
Alternative : OLTP-OBD + outbox/CDC habituel → projection.


Réplication : Outbox et CDC

Outbox (en une seule transaction) : enregistrement des modifications de domaine + enregistrement de l'événement dans outbox ; le pablisher livre dans le pneu.
CDC : Lecture à partir du journal OBD (Debezium, etc.) → transformation en événements de domaine.
Garanties : par défaut at-least-once, les consommateurs et les projections doivent être idempotentes.


Sélection du stockage

Write : relationnelle (PostgreSQL/MySQL) pour les transactions ; KV/Document - où les invariants sont simples.

Read:
  • KV/Redis - cartes et lectures clés rapides ;
  • Recherche (OpenSearch/Elasticsearch) : Recherche/filtres/facettes ;
  • Colonnes (ClickHouse/BigQuery) - rapports ;
  • Cache sur CDN - catalogues/contenus publics.

Modèles d'intégration

Couche API : endpoints/services distincts pour 'commands'et' queries '.
Idempotence : clé d'opération dans le titre/corps ; stockage des clés recent avec TTL.
Saga/orchestration : temporisation, compensation, répétabilité des étapes.
Backpressure : limitation du parallélisme des processeurs de projection.


Observabilité

Métriques write : p95/99 latence des commandes, proportion de transactions réussies, erreurs de validation.
Métriques de lecture : p95/99 requêtes, hit-rate cache, charge sur le cluster de recherche.
Lag de projections (temps et messages), taux DLQ, pourcentage de déduplication.
Tracing : 'trace _ id' passe par la commande → outbox → la projection → query.


Sécurité et conformité

Séparation des droits : différents rôles/scopes pour l'écriture et la lecture ; le principe du moindre privilège.
PII/PCI : minimiser dans les projections ; Cryptage at-rest/in-flight ; masquage.
Audit : enregistrer l'équipe, l'acteur, le résultat, 'trace _ id' ; Archives WORM pour les domaines critiques (paiements, KYC).


Tests

Tests de contrat : pour les commandes (erreurs, invariants) et queries (formats/filtres).
Tests de projection : soumettez une série d'événements/CDC et vérifiez le modèle de lecture final.
Chaos/latency : injection de retards dans les processeurs de projection ; Vérification UX en cas de retard.
Replayability : recadrage des projections sur le stand de snapshots/logs.


Migrations et évolution

Nouveaux champs - additif dans l'événement/CDC ; les modèles de lecture sont revus.
Double écriture (dual-write) lors de la refonte des schémas ; les vieilles projections sont conservées jusqu'au basculement.
Versioning : 'v1 '/' v2' événements et endpoints, plan Sunset.
Feature flags : entrez de nouvelles queries/projections par canari.


Anti-modèles

CQRS « pour la mode » dans les services CRUD simples.
Forte dépendance synchrone de lecture à l'écriture (tue l'isolation et la stabilité).
Un index par tout : le mélange de requêtes hétérogènes dans un seul magasin de lecture.
Il n'y a pas d'idempotence dans les projections → les prises et les divergences.
Projections non réparables (pas de relais/snapshots).


Exemples de domaines

Paiements (service en ligne)

Write : 'Authorize', 'Capture', 'Refund' dans une base de données transactionnelle ; outbox publie « payment. ».

Read:
  • Redis « carte de paiement » pour l'IU ;
  • ClickHouse pour les rapports ;
  • OpenSearch pour la recherche de transactions.
  • Voie critique : autorisation ≤ 800 ms p95 ; cohérence de lecture pour l'IU - eventual (jusqu'à 2-3 s).

KYC

Write : commandes de démarrage/mise à jour du statut ; stockage du PII dans une OBD protégée.
Read : projection facile des statuts sans PII ; Le PII est serré ponctuellement si nécessaire.
Sécurité : différents scopes pour la lecture du statut et l'accès aux documents.

Bilan (iGaming/Finance)

Write : agrégat 'UserBalance' avec incréments/décréments atomiques ; Clés idempotentes pour l'opération.
Lire : cache pour « équilibre rapide » ; pour le déclassement - lecture directe à partir de write (cohérence stricte).
Saga : les dépôts/conclusions sont coordonnées par les événements, en cas de défaillance - les compensations.


Chèque de mise en œuvre

  • Les agrégats et invariants du modèle write sont mis en évidence.
  • Des queries clés ont été identifiées et des projections ont été conçues en dessous.
  • outbox/CDC et processeurs de projection idempotent configurés.
  • Il existe un plan de reconfiguration des projections (snapshot/replay).
  • SLO : latence des commandes, latence des projections, disponibilité de lecture/écriture individuelle.
  • Les droits d'accès sont séparés et le cryptage des données est mis en œuvre.
  • Alertes sur DLQ/lag/échecs de déduplication.
  • Tests : contrats, projections, chaos, repli.

FAQ

Event Sourcing est-il obligatoire pour le CQRS ?
Non. Vous pouvez construire sur une base de données + outbox/CDC normale.

Comment lutter contre la dissynchronisation ?
Concevoir clairement l'UX, mesurer les projections, laisser les opérations critiques lire à partir de write.

Puis-je garder l'écriture et la lecture dans le même service ?
Oui, la séparation physique est facultative ; le partage logique des responsabilités est obligatoire.

Qu'en est-il des transactions entre agrégats ?
À travers des sagas et des événements ; éviter les transactions distribuées, si possible.


Total

CQRS se détache les mains : un chemin d'écriture fin et fiable avec des invariants clairs et des lectures rapides et ciblées à partir de projections matérialisées. Cela améliore la productivité, simplifie l'évolution et rend le système plus résistant aux charges - si vous gérez la cohérence, l'observation et les migrations de manière disciplinée.

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.