Logiciel 15.07.2026

CQRS : séparer lecture et écriture quand CRUD commence à tirer sur les coutures

Serge
CQRS sépare lecture et écriture
INDEX +

CQRS, pour Command Query Responsibility Segregation, sépare ce qui modifie l’état d’une application de ce qui le lit. Le principe est simple. Son effet, lui, est plus profond : il change la façon de penser les modèles de données, les performances, la sécurité et l’évolution du code. Bien utilisé, CQRS clarifie les applications complexes. Mal utilisé, il ajoute de la complexité sans gain réel.

Le principe de CQRS : deux chemins pour deux responsabilités

Dans une architecture CRUD classique, un même modèle sert souvent à créer, lire, mettre à jour et supprimer des données. C’est pratique au départ. Une entité, un repository, quelques endpoints, et l’application avance vite. Le problème arrive quand les besoins de lecture et d’écriture divergent. Une page de reporting attend des données agrégées et rapides à afficher, tandis qu’un workflow métier demande des validations strictes, de l’atomicité et des règles de domaine précises.

CQRS sépare ces deux usages. Une Command exprime une intention de changement, par exemple créer une commande client, valider un paiement ou annuler une réservation. Elle passe généralement par un Command Handler, qui applique les règles métier et modifie le Write Model. Une Query ne change rien. Elle demande une information, souvent via un Query Handler, optimisé pour retourner rapidement un Read Model.

Command ne veut pas dire simple setter

Une commande n’est pas seulement un appel technique du type “mettre à jour le champ statut”. Elle représente une action métier nommée, comme ExpédierCommande, ChangerAdresseLivraison ou ApprouverDossier. Cette nuance compte, car elle pousse l’équipe à parler en langage métier. Le modèle d’écriture peut alors vérifier les invariants, contrôler les droits, garantir l’idempotence d’une opération et refuser une transition invalide.

Query ne veut pas dire sous-produit du domaine

À l’inverse, le modèle de lecture n’a pas besoin d’être élégant du point de vue métier. Il doit être utile, rapide et adapté à l’écran ou à l’API qui le consomme. On peut donc créer une projection avec le nom du client, le total de ses commandes, son statut de fidélité et la date de dernière activité, sans obliger l’application à reconstruire ces informations à chaque requête depuis plusieurs agrégats.

Pourquoi CQRS devient utile quand CRUD commence à tirer sur les coutures

Le principal intérêt de CQRS n’est pas de faire “moderne”. Il sert à éviter qu’un seul modèle porte des contraintes contradictoires. Les lectures veulent souvent de la vitesse, des jointures préparées, des index spécifiques et parfois de la dénormalisation. Les écritures veulent de la cohérence, des transactions maîtrisées, des validations métier et une surface d’attaque réduite.

Cette séparation permet d’optimiser chaque côté indépendamment. Le modèle de lecture peut être répliqué ou mis en cache sans toucher au modèle d’écriture. Les commandes peuvent aussi être sécurisées plus finement, car seules certaines routes changent réellement l’état du système. Dans des applications à forte volumétrie, CQRS aide à limiter la contention de verrouillage sur la base de données : les lectures massives ne viennent pas forcément perturber les écritures critiques.

Un retour d’expérience publié sur Medium illustre cet écart de comportement : une API de lecture pouvait répondre en 18 ms, une API d’écriture en 300 ms, tandis qu’une API concurrente variait de 600 ms à 6 secondes. Ces chiffres ne prouvent pas que CQRS est toujours la bonne réponse. Ils montrent ce qu’une séparation nette peut débloquer quand le modèle unique devient le goulet d’étranglement.

Un bon système CQRS ressemble à une installation électrique bien pensée. Certaines parties supportent la charge, d’autres protègent le circuit. Le modèle de lecture peut jouer le rôle de fusible fonctionnel. S’il devient lent, obsolète ou mal dimensionné, il peut être reconstruit, remplacé ou isolé sans mettre en danger le cœur transactionnel. Cette logique évite aussi de confondre panne d’affichage et corruption métier : un tableau de bord temporairement incomplet n’a pas la même gravité qu’une commande validée deux fois.

CQRS, CRUD, CQS et Event Sourcing : ne pas tout mélanger

CQRS est souvent cité avec d’autres approches, ce qui crée de la confusion. Il faut distinguer le principe de séparation des responsabilités, le style d’API et le mode de persistance des changements. CQRS peut exister sans microservices, sans Event Sourcing et même avec une seule base de données au départ.

Approche Idée centrale Quand elle suffit Point d’attention
CRUD Un modèle sert à créer, lire, modifier et supprimer Applications simples, back-office classiques, faible complexité métier Peut devenir rigide quand lectures et écritures évoluent différemment
CQS Une méthode modifie l’état ou retourne une valeur, mais pas les deux Conception de code plus propre à petite échelle Principe de programmation, pas architecture complète
CQRS Commandes et requêtes suivent des modèles séparés Domaines complexes, volumétrie élevée, besoins de performance distincts Ajoute de la synchronisation et de la discipline d’équipe
Event Sourcing L’état est reconstruit à partir d’une suite d’événements Audit, historique complet, traçabilité forte Modèle mental plus exigeant, gestion des versions d’événements

L’Event Sourcing est souvent associé à CQRS parce qu’il facilite la création de projections de lecture à partir d’événements métier. Mais il n’est pas obligatoire. Une application peut très bien utiliser CQRS avec une base relationnelle classique, des tables dédiées à la lecture et un mécanisme de synchronisation synchrone ou asynchrone.

Mettre en œuvre CQRS sans surdimensionner le projet

La meilleure implémentation CQRS commence rarement par une refonte totale. Il est plus prudent de l’appliquer sur une zone où le bénéfice est visible : un module de commande, un moteur de réservation, un espace client très consulté ou une fonctionnalité métier difficile à faire évoluer en CRUD.

Commencer par les intentions métier

Avant les frameworks, il faut lister les commandes et requêtes importantes. Par exemple : CréerPanier, ValiderCommande, AnnulerCommande côté écriture ; AfficherRésuméCommande, ListerCommandesClient, CalculerChiffreAffairesMensuel côté lecture. Cette étape révèle souvent que certains endpoints “update” masquaient en réalité plusieurs décisions métier différentes.

Structurer les handlers simplement

Un schéma minimal peut rester très lisible : un contrôleur reçoit une commande, la transmet à un Command Handler, qui charge l’agrégat ou le modèle d’écriture, applique les règles, puis persiste le résultat. Pour une requête, le Query Handler interroge directement un modèle de lecture optimisé. En pseudo-code TypeScript, cela revient à séparer une classe ValidateOrderCommandHandler d’une classe GetOrderSummaryQueryHandler, chacune avec ses dépendances propres.

Cette séparation rend les tests plus ciblés. Les tests de commande vérifient les règles métier, les erreurs et les transitions d’état. Les tests de requête vérifient la forme des données retournées, les filtres, la pagination et les performances attendues. On évite ainsi de tester un modèle unique sous tous les angles à la fois, ce qui finit souvent par brouiller les responsabilités.

Prévoir la synchronisation dès le départ

Le point délicat de CQRS est la mise à jour du modèle de lecture. Elle peut être synchrone, lorsque la projection est actualisée dans le même flux que l’écriture, ou asynchrone, via événements et traitement différé. L’asynchrone améliore souvent la scalabilité, mais introduit la consistance éventuelle : pendant un court délai, la lecture peut ne pas refléter la dernière écriture. Ce comportement doit être assumé dans l’interface, les messages utilisateur et les tests.

Quand adopter CQRS, et quand s’en passer

CQRS est pertinent lorsque les lectures et les écritures ont des profils très différents. C’est fréquent dans l’e-commerce, la finance, l’IoT, les plateformes SaaS, les systèmes de réservation ou les domaines où l’audit, la sécurité et la performance sont critiques. Il peut aussi aider une équipe à mieux exprimer un domaine riche en s’appuyant sur des commandes métier plutôt que sur des opérations techniques génériques.

En revanche, CQRS n’est pas adapté à tous les projets. Pour un CRUD d’administration, un prototype, une application interne peu chargée ou un domaine métier simple, il risque d’augmenter la dette technique au lieu de la réduire. Deux modèles, deux chemins de code, des projections, de la synchronisation et parfois des événements : tout cela doit être justifié par un vrai gain.

Les erreurs fréquentes consistent à appliquer CQRS partout, à confondre séparation logique et duplication incontrôlée, à négliger l’idempotence des commandes ou à cacher la consistance éventuelle aux utilisateurs. Un autre piège consiste à démarrer directement avec Event Sourcing, bus d’événements, microservices et projections multiples, alors qu’un CQRS modulaire dans un monolithe bien structuré aurait suffi.

La bonne question n’est donc pas “faut-il utiliser CQRS ?”, mais “quelle partie du système souffre réellement d’un modèle unique ?”. Si vos lectures ralentissent vos écritures, si vos écrans exigent des vues très différentes du modèle métier, si vos règles de domaine deviennent illisibles dans des services CRUD, CQRS mérite une expérimentation ciblée. Sinon, gardez une architecture plus simple. La meilleure séparation des responsabilités reste celle qui résout un problème réel sans en créer trois nouveaux.

⚠️ Information : Vous recherchiez l'association La Bulle Technologique ? Le site labulletech.com n'est plus le site officiel de l'association. Vous pouvez retrouver leurs activités ici : Site officiel de l'association ou sur leur page Facebook.