Article
CQRS n'est pas lecture contre écriture
Le contresens courant sur CQRS : pas « lire contre écrire », mais séparer deux responsabilités — décider et restituer. Command, event, query, write model.
La phrase revient dans presque chaque mission d'architecture : « CQRS, c'est séparer la lecture de l'écriture ». Ce n'est pas tout à fait faux — mais c'est incomplet au point d'induire en erreur. CQRS sépare d'abord deux responsabilités : décider et restituer. Passer de « lecture / écriture » à « command / query » n'est pas un raffinement de vocabulaire : ça change ce qu'on protège, où vit la vérité, et ce qu'on a le droit d'attendre d'une donnée. (Si les mots « write model » ou « projection » vous sont neufs, un court article — CQRS et la projection — les pose sans jargon ; sinon, entrons dans le vif.)
Reprenons la formule qui circule. « Un modèle pour écrire, un modèle pour lire, souvent deux bases : l'une optimisée pour les écritures, l'autre pour les lectures. » Tout y est presque juste, et c'est ce presque qui coûte cher. Car si CQRS n'était que cela — deux stockages taillés pour deux profils d'accès — il ne mériterait ni son nom, ni les débats qu'il suscite depuis quinze ans. Le sigle ne dit pas Read Write Separation. Il dit Command Query Responsibility Segregation : la ségrégation des responsabilités de commande et de requête. Le mot qui compte est « responsabilité », pas « stockage ».
Le raccourci n'est pas gratuit, et c'est pour cela qu'il colle : dans bien des systèmes, les deux modèles finissent effectivement sur des stockages distincts, et l'intuition « lecture / écriture » attrape ce bout-là de la réalité. Le glissement se produit quand on prend cette conséquence fréquente pour la définition. On peut très bien en débattre — et les experts eux-mêmes ne s'accordent pas sur tout, on y reviendra ; mais sur ce point, la bascule vaut la peine d'être faite.
De CQS à CQRS : une affaire de responsabilité
L'idée vient de plus loin que les bases distribuées. Dans les années 1980, Bertrand Meyer pose le principe de Command-Query Separation : une méthode fait une chose ou l'autre, jamais les deux. Soit elle agit et change l'état — c'est une commande, qui ne renvoie rien — soit elle répond et renvoie une donnée — c'est une requête, qui ne change rien. La formule de Meyer : « poser une question ne doit pas changer la réponse ».
Greg Young, à qui l'on doit le terme CQRS, a repris ce principe et l'a monté d'un cran. Là où Meyer sépare des méthodes, CQRS sépare des objets : en CQRS, dit-il, « on prend l'objet qui portait à la fois les commandes et les requêtes, et on en fait deux ». D'abord tenu pour une simple extension de CQS, il a fini par être reconnu comme un pattern à part entière — parenté réelle avec CQS, mais portée différente : les modèles et les objets, non les seules méthodes. Le point à retenir tient en une ligne : la coupe ne suit pas lecture / écriture, elle suit décision / restitution. Une commande décide ; une requête restitue. Deux métiers, pas deux profils de performance.
La nuance paraît scolaire. Elle ne l'est pas. « Écrire » et « lire » décrivent des opérations techniques sur un stockage. « Décider » et « restituer » décrivent des responsabilités métier. Le premier vocabulaire vous pousse vers deux tables ; le second vers deux modèles qui ne se ressemblent pas — parce qu'ils ne servent pas la même chose.
Trois signaux, pas deux tuyaux
Dès qu'on raisonne en responsabilités, un troisième terme s'impose, que la lecture « read/write » masque complètement : l'événement. Un système bien découpé fait circuler trois signaux distincts, et les confondre est à l'origine d'une bonne moitié des erreurs de conception.
- La commande est une intention. Elle demande une décision : « réserver la chambre », « accepter le paiement », « expédier la commande ». Une commande peut être refusée — c'est même son intérêt : elle passe par le modèle qui connaît les règles, et ce modèle a le droit de dire non. Notez la forme : on écrit « réserver la chambre », pas « mettre le statut à réservé ». La commande porte une intention métier, pas une mutation de champ.
- L'événement est un fait accompli. Il est au passé, il est immuable : « la chambre a été réservée ». On ne refuse pas un événement — il a eu lieu. C'est le signal qu'émet le modèle de décision une fois qu'il a tranché, offert à qui veut y réagir. Précisons-le pour la suite : c'est un fait de domaine, pas forcément un message sur un bus — l'infrastructure de messagerie, elle, restera optionnelle. Un événement n'est pas une commande déguisée : une commande demande, un événement constate.
- La requête est une question. Elle restitue une donnée et ne change rien : aucune règle à faire respecter, aucune décision à prendre — juste de l'état, présenté.
La grammaire suffit à les distinguer : impératif pour la commande (« fais ceci »), passé accompli pour l'événement (« ceci a eu lieu »), interrogatif pour la requête (« quel est l'état ? »). Un système qui n'honore pas ces trois temps finit par traiter une requête comme une occasion de muter, ou un événement comme un ordre — et la ségrégation des responsabilités, sur laquelle tout repose, a déjà fui.
Le write model n'est pas un entrepôt d'écriture
Ici se niche le cœur du malentendu, et le plus coûteux. Dans la lecture « read/write », le modèle d'écriture est un stockage : un endroit où l'on pose des données, optimisé pour les poser vite. C'est pourtant, le plus souvent, l'inverse de ce qu'il est.
Le modèle de commande — le write model — est le domaine : le métier et ses règles, par opposition à la plomberie technique. C'est lui qui porte les règles de gestion, valide les décisions, protège les invariants — ces règles qui doivent toujours rester vraies : « on ne vend pas deux fois le dernier billet », « un solde ne passe pas sous zéro ». Les auteurs de Cosmic Python consacrent une section de leur chapitre sur CQRS aux « modèles de domaine faits pour écrire », et le précisent : un modèle de domaine n'est pas un modèle de données — on y capture la façon dont le métier fonctionne, ses règles, ses transitions d'état. Retirez cela, et il ne reste pas un write model : il reste une table.
C'est aussi pourquoi une commande se nomme par une intention, jamais par la mutation qu'elle produit. « Réserver la chambre » plutôt que « passer statut = réservé » : la première formulation laisse le domaine décider (la chambre est-elle libre ? le tarif est-il valide ?), la seconde le court-circuite en lui dictant le résultat. Nommer la mutation, c'est déjà avoir sorti la décision du modèle qui devait la porter.
Le write model est donc la source de vérité. Golden source. Ce qu'il contient fait foi ; le reste en dérive. Et c'est ici que se niche l'inversion la plus toxique : croire que le modèle d'écriture n'est qu'un modèle d'ingestion, un tapis roulant qui alimente les vues de lecture — la « vraie » donnée vivant, elle, du côté des lectures agrégées. C'est un réflexe hérité du décisionnel, où la donnée qui compte est celle des rapports ; en CQRS, c'est l'inverse : la vue de lecture est le dérivé jetable, le modèle de décision est l'original.
Le read model ne fait pas foi
Le modèle de requête — le read model — est une projection : une vue dérivée du write model, dénormalisée, taillée pour être lue vite. Il n'a aucune logique métier : il ne valide rien, ne décide rien. C'est voulu — et nous verrons au fil de la série tout ce que cette liberté permet.
Deux propriétés en découlent — la reconstructibilité de la vue et son léger différé sur la source — qu'on développera plus loin. Retenons seulement, pour l'instant, qu'elles n'ont de sens qu'une fois admis qui, du write ou du read, fait foi : une projection n'est jamais une vérité à protéger, c'est un dérivé réparable.
Point contesté. Faut-il, pour parler de CQRS, deux bases distinctes, ou suffit-il de deux modèles sur une même base ? Greg Young, Microsoft et l'essentiel de la littérature considèrent qu'une seule base suffit — la séparation est au niveau des modèles. Une partie de la communauté a pourtant longtemps réservé le mot « CQRS » au cas où la séparation descend jusqu'aux données (deux stockages distincts), qualifiant le reste de simple CQS. Désaccord réel, qu'il vaut mieux connaître que trancher : dans les deux lectures, le déplacement décisif reste le même — de « lecture/écriture » vers « commande/requête ».
Pourquoi la confusion coûte cher
« Séparer la lecture de l'écriture » n'est pas seulement imprécis : le raccourci peut égarer, parce que chaque mot mal placé ouvre une mauvaise porte.
Si le write model est « le côté écriture », on le traite comme un cache d'ingestion — et un jour on y court-circuite une règle « parce que ce n'est qu'un stockage ». Si le read model est « le côté lecture », on finit par lui faire porter des décisions — une validation « vite fait » sur la vue — et la source de vérité se dédouble. Si CQRS n'est « que » deux stockages, on croit qu'il impose deux bases, un bus de messages, de l'asynchrone partout — alors qu'une seule base suffit à en faire, et que rien n'oblige au messaging. Et surtout, en réduisant tout à « où sont rangées les données », on ouvre la porte au contresens jumeau, celui qui fait de CQRS un synonyme d'event sourcing — deux idées distinctes qu'on traitera séparément, et une seule fois pour l'event sourcing.
Le bon réflexe tient en une question. Devant un système qui se dit « CQRS », ne demandez pas « où est la base de lecture ? ». Demandez : où sont prises les décisions, et qui a le droit de dire non ? La réponse désigne le write model — le domaine, la source de vérité. Tout le reste — les vues, les projections, les lectures rapides — en découle. C'est cette dépendance, et non une affaire de tables, que la suite de cette série va dérouler : le sanctuaire transactionnel où se prennent les décisions, la projection qui parle au monde extérieur, la frontière distribuée entre les deux, et ce qui arrive quand une décision doit traverser plusieurs modèles.
Laisser un commentaire
Vous devez être connecté pour pouvoir laisser un commentaire.