CQRS : séparer lecture et écriture
Lecture et écriture ont des besoins opposés : l'écriture protège des invariants (agrégats petits, transactions), la lecture veut des données plates et rapides (jointur…
Prérequis : cours 4.1 et 3.2 (agrégats).
Pourquoi
Lecture et écriture ont des besoins opposés : l'écriture protège des invariants (agrégats petits, transactions), la lecture veut des données plates et rapides (jointures, dénormalisé). Un seul modèle pour les deux = compromis permanent. CQRS assume la séparation.
Concepts
- Commande : intention de changement (
PlaceOrder), passe par l'agrégat, peut échouer (règles). Requête (query) : lecture optimisée, lecture seule, peut taper une projection dénormalisée — jamais l'agrégat d'écriture. - Projection : vue pré-calculée pour la lecture (table
order_listplate, mise à jour par événements). La lecture ne joint plus : elle lit. - Éventuellement cohérent : après une écriture, la projection se met à jour (synchrone simple, ou async via événements). L'utilisateur peut voir un micro-décalage — à assumer explicitement (message « mise à jour… »).
Exemple : dashboard sans jointures monstres
// Écriture : Order.addLine (invariant protégé)
// Lecture : SELECT * FROM order_list WHERE customer_id = ? (1 table plate, <5 ms)
// Projection mise à jour à chaque OrderPlaced / LineAdded
Cas réel
Page « mes commandes » qui joint 6 tables (30 requêtes N+1 déguisées) : 2,5 s. Projection plate alimentée par événements : 40 ms. L'écriture n'a pas changé d'un iota — seule la lecture a son modèle.
Pièges
- CQRS + event sourcing + microservices d'un coup : trois révolutions = chaos. CQRS seul (même base, deux modèles) suffit dans 90 % des cas.
- Projection jamais reconstruite : prévoir le replay (tout rejouer depuis l'historique) sinon la projection dérive sans retour.
- Cohérence éventuelle non assumée produit : l'utilisateur voit sa commande « disparaître » 2 s. Le dire dans l'UI, pas le subir.
À retenir
Écriture = invariants, lecture = projections plates ; décalage assumé ; replay prévu.