lundi 14 décembre 2020

Coder et tester "mieux" avec CQRS et EventSourcing

Le titre fait le plein de buzzwords, mais c'est pas pour cela que je l'ai choisi. Non j'ai la conviction et l'expérience que l'architecture basé sur CQRS - le principe de séparation de commande et de query et le EventSourcing - le fait de calculer l'état de l'application basé sur les évènements passés, facilite les tests automatiques tout comme il facilite l'expression du domaine métier dans le code - le DDD. Je vais essayer d'expliquer pourquoi de manière visuelle. Mais tout d'abord une petite explication sur ce que je veux dire par "mieux" dans le titre de ce billet. Ici mieux veut simplement dire "plus métier", c'est pas toujours "mieux" mais "mieux" fait mieux dans un titre ;) 

Bon revenons aux choses sérieuses, en commençant par un diagramme basique de CQRS implémenté de façon EventSourcing



CQRS et EventSourcing

Lorsqu'une commande arrive, elle se fait valider par le CommandHandler qui la transforme en 0 à n évènements. Par exemple une demande de réservation hotel, peut se solder par une réservation ou non. Les évènements, s'il y en a, se font ajouter à la log des events et servent aussi à entretenir des "projections". Les projections servent avant-tout à répondre rapidement à une question (query), car avec ces projections pré-calculées pas besoin de parser l'ensemble des évènements pour chaque question. A noter tout de même qu'on peut faire sans projection pour les cas qui ne nécessitent que peu de performance.

Code orienté métier 

Mais qu'est-ce qu'il y ait de si fantastique pour les tests et en quoi cela permet de mieux exprimer le domaine? Pour y répondre regardons de plus près les 3 éléments principaux :

  1. CommandHandler
  2. ProjectionHandler
  3. QueryHandler

Le CommandHandler consiste à recevoir la commande, une intention utilisateur, la valider par rapport aux évènements passés et la traduire en quelques évènements du domaine. Bon déjà l'entrée et la sortie sont purement des données du domaine, compréhensibles par n'importe quelle personne du domaine métier.

Le ProjectionHandler reçoit des évènements et par rapport à l'état actuel de la projection calcule un nouveau état de projection. Les projections sont souvent des états pré-machés pour l'UI ou pour une autre application, donc proche du besoin métier. 

Finalement le QueryHandler répond à une requête GET de façon spécialisé (non CRUD) soit en passant une projection directement soit en parsant des évènements métier. Parfois un peu des deux. 

On voit bien que la logique est très tourné métier et que les interactions avec les bases de données sont faibles. Donc le code est à la fois tourné métier mais aussi peu couplé à la BDD.

Fonctions pures

Mais ce n'est pas tout. Ce que moi je fais énormément dans le code un peu partout, quelque soit l'architecture en place, est d'isoler des parties de pure logique. En gros avoir des fonctions qui ne font que prendre des valeurs en entrée et qui retournent des valeurs en sortie, rien d'autre. Dans le cas ci-dessus ça donnerait quelque chose comme (la logique pure en jaune)






Tel des satellites la logique est extraite et il ne reste que le travail de coordonneer appel à la BDD et appel à la logique pure. Presque l'ensemble de la logique métier se trouve alors dans des fonctions pures, toutes bien orientée métier. 

Les tests

Mais alors en quoi cela aide pour les tests? Ben c'est le doux rêve de tout écrivain de test d'avoir des fonctions pures. Pas de dépendances hard-codés, pas de dépendances à injecter, pas de mocks à initialiser. En gros cela donne des tests faciles à écrire et surtout faciles à lire. De plus ces tests ne sont pas trop sensibles à des refactorings, car plus le code est orienté métier et haut niveau plus son API est stable.

Attention ici je ne décris que les avantages de cette architecture, il y a bien-sur des inconvénients, mais ce sera le sujet d'un autre billet. Peut-être :)







dimanche 6 décembre 2020

Faut-il commenter son code?

Parfois on croise des développeurs qui disent qu'il faut toujours commenter son code. Toutes les fonctions. Parfois on croise des personnes qui ne veulent pas de commentaire du tout. Nul part! 

D'autres personnes ont un avis moins tranché, elles ont déjà vécu des moment où elles auraient pu payer très fort pour avoir un peu de doc, tout comme des fois où la doc il ne fallait surtout pas s'y fier! 

Prenons un peu de hauteur pour voir plus clair. La documentation a ou peut avoir une valeur, mais elle a aussi un coût.

La valeur

Les commentaires sont comme toute documentation, elles peuvent apporter de la valeur, parfois beaucoup. Je pense notamment à des documentations d'api publique de style swagger ou la documentation sur les libraries open source. Je pense aussi à des bouts de code bien tordus pour une raison, là une bonne documentation n'a pas de prix.

Il y a des contre exemples (plus nombreux) comme par exemple ce code qui n'apporte rien de plus que la signature même de la fonction

   /**
    * @param persons list of persons
    * @return number max age of group
    */
   function maxAgeOfGroup(persons: Person[]): number
       ... 
   }
    

La valeur de la documentation est en effet très variable. Probablement il s'agit d'une courbe suivant la loi de puissance. Si c'est bien le cas, alors il y a un faible nombre de commentaires qui apportent une majorité de la valeur.


La valeur des commentaires est très variable


Le coût

Ce qu'il est facile d'oublier est que toute commentaire vient avec un coût de maintenance. Quand on modifie le code il faut parfois modifier le commentaire. Quand la maintenance n'est pas faite cela se solde potentiellement par un bug, car on s'est fié à un commentaire qui mentait. Plus souvent les développeurs arrêtent tout simplement de le regarder. On arrête les frais. Mais même si on ignore les commentaires et on les laisse là sans les maintenir ils finissent quand même pour demander un coût. Les commentaires prennent de la place sur nos écrans au dépens des lignes des code. Dans un code commenté on voit en effet moins de lignes. Moins de lignes veut parfois dire une vue d'ensemble réduite.

Donc un commentaire qui n'a que peu de valeur, finit par coûter au lieu d'apporter.

Il faut donc écrire des commentaires avec parcimonie, là où on pense qu'elle sera lue, utile et maintenue.

Quand alors?

Je ne sais pas réellement, je ne peux partager que la conclusion que je prend personnellement. Outre la documentation des apis "publiques" qui a une valeur évidente j'essaie de mettre un maximum d'effort à rendre mon code explicite et écrit pour le lecteur. Mais je n'arrive pas toujours bien, alors dans ce cas je documente. 

Je documente quand j'échoue à rendre le code suffisamment explicite.

D'ailleurs je conseille vivement le livre Living Documentation par Cyrille Martraire pour la clarté qu'elle apporte sur ce terrain.