Autres réflexions sur le DDD…

Petit résumé du précédent épisode : le BC est l’élément de plus haut niveau du . Dans sa structure, il est comparable à un coffre-fort qui protège le code fonctionnel des problématiques de persistance et des velléités à le réutiliser. Il encourage la spécialisation du code qu’il contient. Il contient notamment des entités, des services, et… des agrégats, qui regroupent les entités devant évoluer de concert de façon transactionnelle. Dans le cas nominal, aucune demande de traitement ne rentre dans un agrégat sans passer par la racine de l’agrégat. Rien ne sort du BC si ce n’est par des notifications. On définit les contextes conjointement avec le métier en utilisant des cartes de contextes, qui sont des vues “macro” des BCs à l’échelle de la société. Le langage omniprésent est embarqué dans le BC. Le fait aussi une distinction forte entre ce qui peut être traité de façon asynchrone et ce qui doit l’être de façon synchrone.

Synchrone, asynchrone…

Le coût de l’asynchronisme est la consistance éventuelle. Cela signifie qu’à un instant donné, les différents îlots applicatifs seront probablement déphasés entre eux. Cependant, et c’est là toute la magie de l’approche, les patrons de conception du DDD assurent que chaque îlot puisse être totalement consistant, et résilient aux erreurs. A l’intérieur du contexte applicatif, tout est synchrone, et transactionnel. Si les contextes sont découplés fonctionnellement, la consistance éventuelle peut être envisagée sereinement, et il est rare que dans ce cas, elle représente un enjeu particulier. On vit très bien avec, depuis la nuit des temps. Faire en sorte que les notifications et les informations arrivent aux bons interlocuteurs est la responsabilité du bus d’entreprise. Nothing comes for free. Ce composant d’infrastructure revêt donc un aspect critique.

Si la consistance éventuelle vous pose un problème : la consistance éventuelle est accentuée si les BCs de la solution sont fonctionnellement couplés. Les traitements s’enchaînent de services en services et la cohérence globale est impossible. De plus, pour rappel, il est impossible à l’heure ou j’écris ces lignes de rendre transactionnels des traitements distribués sur plusieurs machines, ou VMs, ou même de les rendre résilients face aux erreurs. Par exemple, si l’un des traitements de la chaîne est en échec, l’état du système sera mauvais, ou au mieux, transitoire, en attendant que l’erreur soit reprise. Le DDD propose des solutions…

L’encapsulation et la factory

En DDD, on encapsule pour mieux régner. On ne divise pas. Cela vaut pour le constructeur de chaque classe. On souhaite que les objets soient instanciés légitimement aux yeux du code fonctionnel. Une bonne pratique consiste à rendre privé (ou interne à son assembly) chaque constructeur. En l’état, les objets ne sont donc plus instanciables sans une factory. Nommer correctement la factory, conformément au cas d’utilisation et au langage omniprésent, permet de légitimer la construction de l’objet, ou du graphe d’objets. Et cela permet aussi accessoirement d’en contrôler le cycle de vie.

Si vous utilisez un ORM…

Si c’est EF, tant pis pour vous. Pour citer Mark Seeman sur stackoverflow : soit vous utilisez EF, soit vous faites du DDD. Un simple exemple : EF ne supporte pas les VOs. Même si ces derniers peuvent être codés comme une surcouche de l’ORM, c’est très dommage, car il s’agit d’une notion simple qui ne devrait logiquement pas nécessiter un travail supplémentaire. Les types complexes de EF se rapprochent de cette notion, mais cela reste très insuffisant. Difficile aussi d’obtenir du framework des entités en lecture seule… sans parler des possibilités de mapping Objet/Relationnel qui sont très limitées. De plus, toutes les propriétés des entités sont des propriétés qui implémentent des accesseurs publics. En vérité, il est impossible d’encapsuler quoi que ce soit dans les objets, même si toutes les méthodes sont privées !

RICH VS ANEMIC

Le modèle anémique est un anti-pattern du DDD. Il amène des entités ne contenant pas de métier, comme c’est souvent le cas lorsque l’on componentise à l’extrême les fonctionnalités, ou lorsque l’on délègue tout le fonctionnel à des services (y-compris à l’intérieur du modèle). On obtient alors des applications composées d’une majorité de code technique, d’interfaces, etc… ou même de services internes au BC, qui ne permettent pas d’encapsuler les comportements là ou il le faudrait.

L’importance du contexte…

La notion de contexte est une notion clé du DDD. Il s’agit du contexte d’utilisation. Lorsqu’une fonctionnalité distribuée est sollicitée, il est indispensable de se demander dans quel contexte elle l’est. Cela permet de la tester par rapport à un besoin précis, et de savoir si chacun des composants fait le job ou pas. C’est contractuel : il faut pouvoir tester la fonctionnalité de bout en bout… tester une distribuée en testant chacune des APIs de manière isolée n’est pas fiable. Chaque service peut tout à fait fonctionner en isolation, mais l’ensemble peut tout à fait ne pas fonctionner et ne pas répondre aux besoins du métier. Dans ce travers, le seul test significatif que l’on peut envisager est le test d’intégration : coûteux, il n’a pas vocation à tester le fonctionnel et peut difficilement être réalisé sur le poste d’un développeur.

Le notion de limite. Si un BC doit régulièrement se tourner vers un autre BC pour fonctionner, ce n’est plus un BC : “it is no bounded anymore”. La carte des contextes est mauvaise, et le découpage des responsabilités (et peut-être celui des équipes métier, mais cela est moins probable…) est à revoir.

Il n’en demeure pas moins possible de synchroniser des agrégats à l’intérieur d’un BC par l’intermédiaire d’APIs ou de notifications, si le besoin est là. Petit détail au passage, s’appuyer sur des agrégats disqualifie l’utilisation du Lazy-Loading. Concernant les notifications, elles doivent être immutables, car elles sont une représentation du passé. Il est important d’en garder une trace viable, pour des questions d’audit.

Conclusion

Adopter le DDD représente un coût. On ne l’applique pas aux fonctionnalités transverses pouvant être externalisées (sauf pour se faire plaisir…), mais uniquement au coeur de métier de la société, au “Core Domain”. Les sous-domaines peuvent être pensés de manière à être anémiques et réutilisables.

Add a Comment

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

30 − = 23