Lors de la conférence Scala.io 2014, Jérémie Chassaing nous a parlé event sourcing. Ce n’est plus un scoop maintenant, malgré l’existence de langages objets on en est au fond toujours au même point. Il part d’un principe spécifique à la programmation orientée objet : l’encapsulation. En lisant en base de données l’état de votre système vous violez ce principe. Vous le violez au niveau architectural. Référencer un membre de classe ou lire une colonne d’une table, quelle différence ? Petit rappel sur l’encapsulation : on cache les structures de données à l’aide de fonctions. Le système devient définissable en termes de comportement plutôt qu’en termes de structure. On peut faire varier la structure sous-jacente d’un comportement sans impacter l’expression des usages.
Pour Jérémie, « rien ne change jamais pour aucune raison » (attention à la double négation). Si la formulation vous semble compliquée, pensez au second principe de la thermodynamique. C’est plus clair maintenant ? Quand votre système (et le nôtre aussi, mais rien à voir avec les jardins) évolue alors un (et un seul) événement en est l’origine. Autre formulation : il n’y a pas de fumée sans feu.
Mais nos systèmes ont beau être écrit en Scala, en général on perd la cause du changement. On sauvegarde uniquement les conséquences, l’effet produit. Pourquoi l’adresse d’un utilisateur change ? Parce qu’il a déménagé ! A la rigueur parce que l’adresse était erronée. Event sourcing applique la recherche de la vision formulée par Domain-Driven Design d’Eric Evans : pourquoi ce projet, pourquoi ce comportement ? Autre justification : il est plus facile de faire la synthèse d’un ensemble d’éléments plutôt que d’analyser un élément en sous-éléments. L’analyse de texte est plus complexe que la mise en page. Sinon LaTeX c’est très très bien.
Donc premier principe de l’event sourcing : sauvegarder les événements du domaine plutôt que l’état du cumulé du système. Ainsi, le journal (log), d’habitude de contenu technique (en fait fourre-tout), devient un référentiel métier. Il devient exhaustif. Avec l’event sourcing tout est journalisé. A ce niveau, pas de mise à jour, pas de suppression : uniquement de l’ajout d’événements. L’état cumulé est calculé plus loin. Il est optimisé pour être consulté en lecture seule. Corolaire : on sépare ce qui est destiné à modifier et ce qui est destiné à observer. Pourquoi ? Combien de fois je modifie mon système ? Combien de fois je le regarde ? Si le quotient lecture/écriture est très grand autant séparer les deux. Cette architecture a un nom : Command Query Responsibility Seggregation (CQRS). On optimise la lecture en préparant à chaque modification du système la ou les vues possibles. La commande change mon système (écriture). La requête l’observe (lecture).
Focalisons nous sur la partie écriture. Avec l’event sourcing elle est composée de deux fonctions : une fonction de décision qui traite les commandes et qui émet des événements ; et d’une fonction d’évolution qui applique un événement à un état pour produire un nouvel état.
Figure 1: Décision / Évolution
Que se passe-il pour migrer ? Simple : on rejoue tout l’historique d’événements avec le nouveau code. Bon je triche un peu, il y a peut-être un problème de performance si on fait ça naïvement. Mais faisons un peu de déni pour le moment.
Génial non ? Pourquoi n’y ai-je pas pensé plus tôt ? Je vais m’y mettre moi aussi à l’event sourcing. Mais attention au piège ! Si vous sauvegardez les commandes (« command sourcing ») qui entrent dans votre système vous risquez gros.
Jérémie prend l’exemple du taux de TVA. Ce taux fait partie de la logique métier. Dans un cinéma (ça c’est mon exemple), étant donné une TVA à 19,6%, lorsqu’un client achète une place pour « Plan 9 » à 10e, la comptabilité déduit une TVA à 1,96e. Dans ce scénario (merci BDD pour la formulation) le système de gestion prend une commande (au sens CQRS) composée du film et du prix de la place. Quand on joue une deuxième fois cette commande (« Plan 9 » à 10e) avec la version 2.0 du logiciel la TVA appliquée pourrait être passée à 20 %. La déduction serait donc 2e au lieu de 1,96e. On aurait donc dans ce cas une reprise d’historique fausse. La variable 19,6 % qui était (faussement) liée dans le scénario est en réalité libre. En d’autres termes, dans ce contexte, elle ne servait à rien !
La première et mauvaise solution à laquelle on pense est la suivante : il suffit de gérer un taux de TVA dépendant du temps. Ça paraît simple. Mais cette approche se révèle être désastreuse car une quantité incroyable d’informations devront être historisées. En fait toute la logique métier, c’est-à-dire le code devrait être historisé. Et non, Git n’est pas la solution...
La deuxième et bonne solution est de sauvegarder les événements produits par la fonction de décision. Dans notre exemple, la fonction de décision applique le taux de TVA à 19,6 % à la commande « Plan 9 » à 10e. Le résultat pourrait être un événement d’achat de place à 19,6 % de TVA au tarif de 10e. En rejouant plusieurs fois l’historique ce qui dépendait de la logique métier aura déjà été inclus dans l’événement. La phrase « étant donné une TVA à 19,6 % » donnait le contexte, l’état du système. Il ne faisait pas partie de la commande. En revanche il fait partie de l’événement. On aurait aussi pu avoir un événement encore plus simple ne modélisant que la TVA déduite (1,96e). Dans ce cas aussi l’événement encode implicitement le taux 19,6 %.
Ainsi en rejouant la séquence d’événements avec le nouveau code, nul besoin de recalculer la déduction. On ne rappelle pas la fonction de décision une seconde fois. En revanche, la nouvelle fonction d’évolution s’applique à chaque évolution. Elle projette tous les évènements du système dans un état. Au fil des incréments, de nouveaux indicateurs sont intégrés à la projection. Event sourcing les rend disponible même sur des données anciennes. Sous réserve bien sûr que les événements soient assez riches.
En séparant les responsabilités, on a extrait le domaine dans une fonction de décision. L’implémentation est moins contaminée par les aspects techniques liés à l’accès aux données. Le système devient performant sous la charge et son évolution élégante.
1 comment for “Event sourcing à Scala.io 2014”