Publié par
Il y a 2 mois · 9 minutes · Craft

Retour sur l’après-midi du Domain-Driven Design

Le 7 juin dernier s’est déroulé l’après-midi du Domain-Driven Design au centre de conférence Microsoft, à Issy-les-Moulineaux. Cet évènement, animé par Thomas Pierrain, Bruno Boucard et Jérémie Grodziski (co-organisateurs du meetup DDD Paris et fondateurs du mouvement « Let’s reboot DDD »), avait pour objectif l’introduction aux patterns techniques et à l’approche du DDD au travers d’un live-coding longue durée sur un code legacy. Autrement dit, les conditions que nous, développeurs, rencontrons au quotidien par opposition aux présentations partant habituellement d’une feuille blanche.

Xebia était présente à cet évènement et vous propose de revenir sur ce dernier en vous fournissant un certain nombre de pointeurs pour creuser le sujet et être en mesure de l’appliquer dès à présent sur vos bases de code.

L’après-midi s’est articulé autour d’une keynote revenant sur la genèse du DDD suivie de 3 sessions de live-coding respectivement dédiées à la pose d’un harnais de tests, à l’utilisation des patterns techniques du DDD et à celle des concepts d’une architecture hexagonale. Nous vous proposons de suivre ce même cheminement dans ce compte-rendu.

Keynote

Le DDD trouve son origine dans un constat simple : il est devenu aisé de faire du logiciel mais les coûts de maintenance restent trop élevés, dû notamment à la complexité. Cette dernière est de 3 types : le domaine, le logiciel et les individus.

Combattre la complexité au coeur du logiciel

Le best-seller d’Eric Evans, à l’origine du DDD, propose de « combattre la complexité au coeur du logiciel ».
Jérémie Grodziski définit ce dernier comme « l’ensemble des concepts qui permettent de résoudre des problèmes à travers des cas d’utilisation ».
Ce coeur varie bien évidement d’un contexte à l’autre.

Par exemple, dans le domaine de la comptabilité à double entrée, on utilise les concepts de compte, débit, crédit, montant, etc. pour résoudre des problèmes de traçabilité et de robustesse. Autre exemple, dans le domaine des environnements de développement intégrés, on utilise les concepts de projet, fichier, refactoring, analyse, etc. pour résoudre des problèmes d’intégration et de productivité.

Pour traiter cette complexité, on cherche un meilleur alignement entre l’espace du problème et l’espace de la solution. Et celui-ci passe incontestablement par une meilleure compréhension entre les personnes. Pour reprendre le mojo de Thomas et Jérémie : « Make the implicit, explicit » (« Explicitez les implicites ! »).

DDD, une boite à outils des plus riches

DDD peut être vu comme :

  • une approche de conception (prendre des décisions plus ou moins impactantes mais de manière consciente).
  • et une boite à outils à 2 étages :
    • lorsque l’on code, via les patterns tactiques (entités, values objects, fonctions pures, etc.).
    • lorsque l’on architecture, via les patterns stratégiques (bounded contexts, couches d’anti-corruption, architecture hexagonale, etc.).

Attention ! DDD n’est surtout pas un processus ou une méthode qui nous dit quoi faire à chaque instant.
Il s’agit de faire usage de cette approche et de cette boite à outils pour connecter nos décisions aux objectifs du métier.

En résumé, le DDD cherche à répondre à la question suivante : « comment intégrer au mieux le domaine dans le logiciel ? »

Contexte du live-coding

Thomas, Bruno & Jérémie se sont inspirés du kata TrainReservation d’Emily Bache pour mener à bien leur après-midi de live-coding (4h au total, respect !).
Dans ce dernier, il s’agit de réserver des billets de train en tenant compte d’un certain nombre de règles telles que :

  • Ne pas dépasser 70% des places réservées dans le train (pour laisser la possibilité de mener des opérations de dernière minute).
  • Ne pas dépasser 70% des places réservées dans chaque voiture (on n’aime pas voyager dans un train bondé !).
  • Garder les sièges d’une même réservation dans la même voiture.

Bien évidemment, nos 3 amis partent d’un code existant « legacy » constitué d’une seule API REST et démuni de tests.
Jérémie joue le rôle de l’expert métier; Thomas et Bruno ceux des développeurs.

Session n°1 – TDD – Bruno Boucard

Dans cette première session, Bruno se charge de la mise en place d’un harnais de tests. Hors de question d’envisager quelque évolution sans filet de sécurité.

Avant toute chose, Bruno nous invite à toujours commencer par une phase d’appropriation du code. Il s’agit de simplement dérouler le code en notant les premiers concepts rencontrés. On peut uniquement s’autoriser des changements mineurs automatiques via son IDE tels que des renommages ou des suppressions de code mort.

Parmi les smell codes rencontrés, on retiendra celui dit du « Feature Envy », lorsqu’un code appelle plusieurs fois le code d’une autre classe, cela nous indique que ce code devrait être extrait.

Vient ensuite le temps de commencer à couvrir le code. Il s’agit ici de commencer par un cas simple, pour se donner du courage. Dans notre contexte ferroviaire, il s’agit de faire une réservation dans un train d’une voiture de 10 places totalement libres. Ce premier test, généralement d’acceptance, est souvent le plus douloureux à écrire car c’est à travers lui que l’on découvre les dépendances cachées ou statiques. Pour y faire face, il est nécessaire de pouvoir extraire et simuler ces dépendances. Autre technique efficace pour ne pas casser l’API (introduite par Michael Feathers dans son livre « Working Effectively with Legacy Code »), les constructeurs paramétrés.

Soit le constructeur suivant avec une dépendance dite cachée :

public class WebTicketManager {
 
 private final TrainDataService trainDataService;
 
 public WebTicketManager(){
  this.trainDataService = new TrainDataService();
 }
}

L’extraction de cette dépendance nous amène à introduire un second constructeur :

public class WebTicketManager {
 
 private final TrainDataService trainDataService;
 
 public WebTicketManager(){
  this.trainDataService = new TrainDataService();
 }
 
 public WebTicketManager(TrainDataService trainDataService){
  this.trainDataService = trainDataService;
 }
}

Pour assurer la compatibilité (le temps d’atteindre une couverture de test nous permettant de refactorer de manière sûre), nous chainons ces constructeurs.

public class WebTicketManager {
 
 private final TrainDataService trainDataService;
 
 public WebTicketManager(){
  this(new TrainDataService());
 }
 
 public WebTicketManager(TrainDataService trainDataService){
  this.trainDataService = trainDataService;
 }
}

Session n°2 – DDD all the way – Thomas Pierrain

Le code ainsi couvert, c’est Thomas qui s’est chargé d’introduire un peu de DDD. A vrai dire, le constat est clair, on ne trouve dans le code aucun des concepts discutés avec Jérémie, notre expert métier (par exemple le concept de Voiture). Tout juste trouve-t-on quelques objets adolescents (dixit Martin Fowler) ou objets anémiques qui n’ont pas encore pris toutes leurs responsabilités.

Pour changer tout cela, la cible est ambitieuse :

Quelques rappels glanés sur les concepts du DDD :

  • Entité : mutable. La représentation de l’état prend en compte le temps. On a donc à la fois un historique et une image de l’état à un instant T.
  • Value Object : immuable. Ne varie pas dans le temps sans pour autant être un objet adolescent. Attention, la différence entre entité et value object est contextuelle.
    • Ex : Un billet de banque est un VO pour vous comme pour moi mais est une entité pour la Banque de France. Dans le cas du kata autour du train, les sièges sont des VO car leur réservation est gérée par un système externe et que l’on n’a donc aucun historique en interne.
  • Agrégat : ensemble de VO et d’entités fortement cohérents et que l’on traite comme un ensemble. On ne peut alors y accéder que par son élément racine.
    • Ex : le train est un agrégat qui contient les voitures et les sièges. On ne peut parler directement aux voitures et aux sièges pour une réservation, on s’adresse donc au train. Ce qui est intéressant, car cela lui permet  de s’assurer de la cohérence de ses contenus : lui seul les modifie. 

Au travers de l’extraction de classes/types, de renommages et de suppressions, Thomas arrive assez rapidement à un code bien plus métier.
On retiendra notamment :

  • la suppression de code inutile d’un point de vue métier; un cache alors que plusieurs entreprises peuvent réserver un même train.
  • la suppression de doubles listes ; il s’agit d’agréger les listes plutôt que de compter deux fois la même chose.
    • Ex : chaque voiture compte le nombre de sièges réservés. Le train parent n’a donc plus qu’à agréger ces valeurs plutôt que de tout recompter lui même.
  • un exemple ou un schéma est souvent un excellent moyen de comprendre le métier.
    • Ex : cas d’exception : je souhaite réserver 6 places pour une réservation alors que la règle des 70% est déjà atteinte pour chaque voiture mais pas pour le train.
  • l’usage de la fermeture sur opération (une fonction qui retourne le même type que son argument) et de fonctions pures pour assurer l’immutabilité des VO. Une opération sur un VO renvoie un nouveau VO plutôt que de le faire muter, charge à l’appelant de l’utiliser. Dès que l’on enlève le temps de l’équation, la complexité décroit !

Session n°3 – Hexagonal Architecture for the win – Thomas Pierrain

Une fois les patterns tactiques du DDD introduits, Thomas s’est tourné vers l’architecture hexagonale dont nous avions déjà traité sur ce blog pour pérenniser notre métier. Pour rappel, cette dernière a pour objectifs :

  • d’embrasser le changement (même pas mal !);
  • de protéger/pérenniser le code du domaine;
  • de renforcer la testabilité.

On retiendra, pêle-mêle :

  • Thomas propose une mise en place en trois étapes :
    • écrire les adapteurs en sortie;
    • instancier l’hexagone;
    • écrire les adapteurs en entrée.
  • Thomas propose d’expliciter l’hexagone dans le code en créant un wrapper dans l’objectif avoué d’amener tout nouvel arrivant à déclencher une discussion sur le pourquoi de cette architecture.
  • Il est idéal de pouvoir séparer le domaine, l’infrastructure et la fine couche d’injection de dépendances, idéalement dans des projets ou modules séparés. On renforce ainsi la règle primordiale de cette architecture : les dépendances ne peuvent aller que de l’extérieur vers l’intérieur.

TL;DR

Pour conclure, laissons une dernière fois la parole aux organisateurs (et merci à eux pour cette belle après-midi).

« Concentrez-vous sur, soulignez et protégez la valeur métier dans votre code ».

Clément Héliou
Pragmastism Driven Developer passionate about xDD (TDD/BDD/DDD), clean architectures (Event Sourcing, Hexagonal Architecture and al.) and code quality. Enthusiastic defender of knowledge sharing and human skills.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *