Kill the Aggregate : et si la frontière de cohérence n'était pas l'entité ?
Ce que l'aggregate garantit et ce qu'il rigidifie, ce que proposent les Dynamic Consistency Boundaries, et comment décider si votre invariant justifie de sortir du modèle aggregate.
Par Elias Varen5 min de lectureMembres
L'aggregate Cart contient tous les items du panier. Chaque modification d'item (ajout, suppression, changement de quantité) écrit dans le même stream. À faible trafic, tout va bien. À l'échelle, dix utilisateurs qui modifient leur panier en même temps produisent dix conflits d'optimistic locking sur le même stream.
Un hot aggregate de ce genre signale moins un souci de performance qu'une frontière de cohérence mal posée. La bonne question porte sur l'invariant : lequel le Cart protège-t-il vraiment ?
Si chaque item est indépendant des autres, le Cart n'a aucune raison d'être un aggregate unique, et la frontière de cohérence descend au niveau de l'item.
Ce que l'aggregate garantit
Avant de questionner le pattern, il faut nommer ce qu'il apporte.
Un aggregate est une frontière de cohérence transactionnelle. Il garantit des invariants comme « le solde d'un compte ne peut pas passer en dessous de zéro » ou « une commande ne peut pas être expédiée si elle n'est pas payée ». Ces invariants portent sur l'état complet de l'aggregate, ce qui justifie que toutes les modifications passent par une seule frontière protégée par optimistic locking.
Le pattern convient particulièrement quand :
- l'invariant touche plusieurs attributs du même objet ;
- la cohérence est nécessaire à l'intérieur d'une même transaction ;
- la taille du stream reste raisonnable (quelques centaines à quelques milliers d'événements).
Correctement dimensionné, l'aggregate reste le bon outil.
Des frontières tirées par la structure des données
L'ennui commence quand on croit que toute décision métier doit partager la même frontière de cohérence permanente.
L'aggregate trop large. Un Catalog contient tous les produits, et chaque mise à jour de prix écrit dans le même stream. Avec quelques milliers de produits et des mises à jour fréquentes, le stream accumule des millions d'événements, le chargement de l'aggregate devient lent et l'optimistic locking produit des conflits constants. La frontière a été tirée par la forme des données (un catalogue) et non par un invariant métier.
Le dossier complet : Systèmes event-sourcés
À lire ensuite
Toute la rubrique ArchitectureArchitecture
Optimistic locking : éviter le Lost Update silencieux
Comment expectedVersion empêche deux retraits concurrents de s'écraser, comment le CommandBus fait un retry, et les règles à tenir en production.
3 minLecture libre
Architecture
Durable execution : moteur de workflow ou table d'état ?
Un moteur de durable execution et une table d'état résolvent le même problème avec des contraintes opposées, surtout le jour où vous modifiez un workflow dont des instances sont en cours. Les stratégies de versionnement et le critère pour choisir.
7 minMembres
Architecture
Sagas : la compensation que personne ne teste
Une compensation s'exécute rarement, dans le pire état possible, et peut elle-même échouer. Comment énumérer les états de panne partielle d'une saga, ordonner les étapes autour d'un pivot, et tester chaque chemin de retour par injection de pannes.
7 minMembres