CQRS (Command Query Responsibility Segregation) est un modèle d'architecture de plus en plus utilisé dans les systèmes backend modernes, notamment lorsqu'il s'agit d'optimiser la performance et la scalabilité. À la différence de l'approche CRUD classique, CQRS propose de séparer les opérations d'écriture et de lecture des données au lieu de les traiter de manière uniforme.
Qu'est-ce que CQRS en termes simples ?
CQRS (Command Query Responsibility Segregation) consiste à diviser le système en deux parties :
- Commandes (Command) : responsables de la modification des données
- Requêtes (Query) : responsables de la récupération des données
L'idée principale : les opérations de lecture et d'écriture doivent reposer sur des modèles de données distincts.
Dans une architecture CRUD classique, un seul modèle est responsable de la création, la mise à jour et la lecture des données. C'est simple, mais cela finit par engendrer des limitations.
Avec CQRS :
- Les commandes ne renvoient aucune donnée (elles modifient seulement l'état)
- Les requêtes ne modifient rien (elles se contentent de lire)
Exemples :
- Créer une commande client = Command
- Obtenir la liste des commandes = Query
Cette séparation permet de :
- Simplifier la logique métier côté écriture
- Accélérer la lecture des données
- Faire évoluer le système plus facilement
On aboutit donc à deux modèles :
- Write model (pour l'écriture)
- Read model (pour la lecture)
Chacun peut être optimisé indépendamment selon les besoins du projet.
Fonctionnement de CQRS
La force de CQRS réside dans la séparation non seulement logique, mais aussi architecturale des opérations d'écriture et de lecture. Le système traite donc commandes et requêtes de façon distincte.
Les Commandes : modifier les données
- Créer un utilisateur
- Passer une commande
- Mettre à jour un profil
- Supprimer une information
Une commande traduit toujours une intention de modification :
- Elle ne renvoie pas de donnée (ou seulement un statut)
- Elle passe par la logique métier
- Elle peut déclencher des validations
Exemple : CreateOrderCommand pour créer une commande. Important : une commande ne doit pas servir à lire des données : elle change uniquement l'état du système.
Les Requêtes : lire les données
Les requêtes sont dédiées à la lecture :
- Elles ne modifient rien
- Elles renvoient le résultat attendu (liste, objet, statistiques...)
- Elles sont optimisées pour la rapidité
Exemple : GetOrdersQuery pour obtenir la liste des commandes.
Dans CQRS, les requêtes peuvent utiliser un modèle de données conçu spécifiquement pour la lecture rapide : base dénormalisée, cache, ou réplica dédié à la lecture.
Séparation des modèles : write model & read model
- Write Model (pour les commandes) :
- Inclut la logique métier complexe
- Structure normalisée
- Orienté intégrité des données
- Read Model (pour les requêtes) :
- Simplifié
- Peut être dénormalisé
- Optimisé pour la vitesse
Exemple e-commerce : le write model stocke commandes, produits, utilisateurs de façon normalisée, tandis que le read model peut contenir des vues prêtes à l'emploi (commandes avec nom du client et total).
Flux de données dans le système
- L'utilisateur envoie une commande (ex : créer une commande)
- Le système traite la commande et enregistre les modifications
- Le read model est ensuite mis à jour (souvent de façon asynchrone)
- Lors de la prochaine requête, l'utilisateur voit les données à jour
Cette asynchronie peut entraîner un léger délai de synchronisation entre les modèles (eventual consistency).
CQRS vs CRUD : quelles différences ?
Fonctionnement du CRUD
Dans le modèle CRUD (Create, Read, Update, Delete) :
- Un seul modèle de données pour toutes les opérations
- Une seule base de données
- Une même couche de logique
Par exemple, la table Users sert à la fois pour l'écriture, la lecture et la mise à jour. Simple et rapide au démarrage.
Limites du CRUD
- Logique métier complexe : le modèle devient difficile à maintenir
- Performance : les besoins en lecture et écriture diffèrent mais partagent le même schéma
- Scalabilité limitée : impossible d'optimiser indépendamment lecture et écriture
- Requêtes lourdes : nécessité de JOIN complexes pour obtenir les données
Comment CQRS répond à ces problèmes ?
- Écriture via commandes et write model
- Lecture via requêtes et read model
Avantages :
- Possibilité d'utiliser différentes bases de données
- Lecture plus rapide (données déjà préparées)
- Logique d'écriture plus claire
- Scalabilité simplifiée
Exemple : l'écriture d'une commande implique des validations, alors que la lecture consiste à afficher rapidement des données agrégées.
Quand la différence est-elle cruciale ?
- Systèmes à haute charge
- Applications centrées sur la lecture (analytique, dashboards)
- Business complexes (finance, e-commerce)
En résumé :
- CRUD : rapide à mettre en œuvre, idéal pour les petits projets
- CQRS : plus complexe, mais adapté à la croissance et à la scalabilité
CQRS n'est pas là pour remplacer CRUD, mais constitue une évolution architecturale pertinente lorsque le modèle classique atteint ses limites.
CQRS en pratique : architecture et organisation
Séparation des bases de données
Dans la version simple, CQRS peut fonctionner sur une seule base avec des modèles différents. Dans une version avancée, on sépare physiquement :
- Une base pour l'écriture (write database)
- Une base pour la lecture (read database)
Cela permet :
- D'optimiser l'écriture pour la transactionnalité et l'intégrité
- D'optimiser la lecture pour la rapidité et la scalabilité
Exemple :
- Base d'écriture : PostgreSQL
- Base de lecture : Elasticsearch ou Redis
Des modèles de données différents
- Write Model : tables normalisées, structure stricte, logique métier complète
- Read Model : données agrégées, peu de relations, vues prêtes à l'emploi
Exemple : au lieu de requêtes JOIN complexes, la read model stocke directement le nom de l'utilisateur, la liste des commandes, le total, etc. Cela accélère considérablement les requêtes.
Asynchronisme et eventual consistency
Dans CQRS, la synchronisation entre modèles est généralement asynchrone :
- Une commande modifie le write model
- Un événement est généré
- Le read model est mis à jour ensuite
Ce mode de fonctionnement implique une cohérence à terme (eventual consistency) : l'utilisateur peut voir temporairement des données non à jour. Il faut donc concevoir le système en tenant compte de ces délais.
Exemple d'architecture CQRS
- API qui reçoit commandes et requêtes
- Les Command Handler traitent les commandes
- Les requêtes interrogent directement le read model
- Les événements mettent à jour la read model
Dans une architecture avancée, on ajoute :
- Broker de messages (Kafka, RabbitMQ)
- Services séparés pour lecture et écriture
- Cache
Il n'est pas nécessaire d'adopter CQRS dans toute l'application d'un coup : il est souvent mis en place progressivement, en commençant par la logique métier.
CQRS et Event Sourcing : quel lien ?
CQRS est souvent associé à Event Sourcing, un autre pattern complémentaire très utilisé dans les architectures complexes.
Qu'est-ce que l'Event Sourcing ?
Dans un système classique, on stocke l'état actuel (ex : " solde du compte = 1000 "). En Event Sourcing, on enregistre toute la séquence des événements :
- Rechargement du compte de 500 €
- Paiement d'une commande de 200 €
- Bonus de 700 € crédité
L'état actuel s'obtient en rejouant tous les événements.
Intégration CQRS + Event Sourcing
- CQRS sépare commandes (écriture) et requêtes (lecture)
- Event Sourcing stocke les changements sous forme d'événements
- La commande arrive dans le système
- Un événement (ex : OrderCreated) est généré
- L'événement est enregistré
- Le read model est mis à jour à partir de la séquence d'événements
Pourquoi utiliser les deux patterns ensemble ?
- Historique complet : possibilité de reconstituer n'importe quel état
- Audit et traçabilité : on sait ce qui s'est passé et quand
- Flexibilité : le read model peut être reconstruit à la volée
- Scalabilité : les événements sont facilement diffusables entre services
Quand ce combo est-il pertinent ?
- Secteurs où l'historique des modifications est crucial (finance, logistique...)
- Systèmes complexes et distribués
- Besoins élevés de scalabilité
Mais attention : cela complexifie fortement l'architecture et requiert de l'expérience.
Dans bien des cas, CQRS seul suffit ; l'Event Sourcing est à réserver aux systèmes nécessitant un suivi poussé des modifications.
Avantages et inconvénients de CQRS
Les points forts
- Scalabilité : lecture et écriture peuvent évoluer indépendamment (ex : ajouter des replicas de lecture sans toucher à l'écriture)
- Haute performance : optimisation du read model pour des requêtes spécifiques (cache, dénormalisation, vues prêtes à l'emploi)
- Flexibilité architecturale : adoption de technologies différentes pour chaque partie (SQL pour écrire, NoSQL pour lire...)
- Logique métier plus propre : le write model reste focalisé sur la cohérence et la validation
Les limites
- Complexité de mise en place : multiplication des composants (commandes, handlers, événements, modèles séparés)
- Eventual consistency : les données ne sont pas toujours synchronisées instantanément
- Débogage plus difficile : l'asynchronisme complique la traque des bugs
- Surdimensionné pour les petits projets : peut alourdir inutilement la conception
CQRS n'est donc ni " meilleur " ni " pire " que CRUD : c'est un outil à utiliser dans le bon contexte.
Quand adopter CQRS ?
- Systèmes à forte charge : dashboards, analytics, marketplaces, réseaux sociaux...
- Logique métier complexe : validation, règles spécifiques, dépendances multiples
- Besoins différenciés en lecture et écriture : écriture exigeante en cohérence, lecture orientée rapidité
- Architectures distribuées et microservices : chaque service gère lecture ou écriture, propagation des données via des événements, scalabilité facilitée
Pour approfondir, découvrez notre article : Microservices vs monolithe : quel choix pour votre architecture en 2025 ?
Quand éviter CQRS ?
- Petits projets
- Logique simple
- Faible charge
- Ressources limitées
Dans ces cas, CQRS risque de compliquer inutilement le développement.
En résumé : si votre architecture CRUD commence à montrer ses limites face à la complexité ou à la montée en charge, il est temps d'envisager CQRS.
Comment mettre en place CQRS dans un projet ?
Mise en place progressive
Inutile de tout refondre d'un coup : commencez par :
- Séparer la logique des commandes et des requêtes
- Créer des handlers dédiés
- Optimiser la lecture avec des DTO ou des vues spécifiques
Même sans infrastructure complexe, CQRS apporte déjà des bénéfices à ce stade.
Séparation logique sans refonte de la base
- Commandes : classes ou méthodes séparées
- Requêtes : services distincts
Exemples :
- CreateOrderCommandHandler
- GetOrdersQueryHandler
Avantages : code structuré, responsabilité claire, maintenance facilitée.
Complexification progressive
Au fil de la croissance du système, il devient pertinent d'ajouter :
- Un read model distinct
- Du caching
- Un traitement asynchrone des événements
- Un message broker
Mais n'introduisez ces éléments que s'ils répondent à un besoin réel.
Erreurs fréquentes lors du déploiement
- Complexité prématurée : CQRS adopté " par précaution " alors que le projet reste simple
- Mauvaise gestion de l'eventual consistency : bugs dus à la latence de synchronisation
- Architecture surdimensionnée : ajout d'Event Sourcing ou de brokers sans justification
- Manque de frontière claire entre commandes et requêtes : perte du sens de CQRS
CQRS doit être vu comme une évolution, pas comme un point de départ. Sa force : il peut être introduit étape par étape.
Conclusion
CQRS est un modèle d'architecture qui sépare lecture et écriture, permettant au système d'être plus rapide, flexible et évolutif. Il est particulièrement adapté aux projets à forte charge ou à logique métier complexe, là où CRUD atteint ses limites.
Mais CQRS n'est pas une solution universelle : sur des systèmes simples, il peut complexifier la maintenance et générer plus d'erreurs. Il doit donc être adopté en connaissance de cause, face à de réels besoins.
En résumé :
- Petit projet : privilégiez CRUD
- Montée en charge ou complexité accrue : considérez CQRS
La bonne pratique : introduisez CQRS de manière progressive, là où il apporte une réelle valeur ajoutée.