#6 - DDD Stratégique : les premiers Bounded Contexts

#6 - DDD Stratégique : les premiers Bounded Contexts
Photo by Jan Canty / Unsplash

Bounded Context is a central pattern in Domain-Driven Design. It is the focus of DDD's strategic design section which is all about dealing with large models and teams. DDD deals with large models by dividing them into different Bounded Contexts and being explicit about their interrelationships.
Martin Fowler.

Identifier les contextes métier - bounded contexts

Dans l'article précédent, nous avons aboutit à la création d'une cartographie des domaines métier.

Cartographie des domaines d'une gestion de ligue de Boodbowl


Nous allons maintenant voir comment passer de cette modélisation de domaine, à une proposition de modélisation d'architecture logicielle. Donc en quelque sortes, comment passer de la modélisation du problème à la modélisation de la solution.

L'identification de ces contextes métier est issue du regroupement de processus  liés entre eux. L'architecture logicielle cible doit donc nous permettre de répliquer cette organisation métier, dans le code. Nous allons donc designer une architecture qui fait la part belle aux "modules" métier. C'est a dire à des "paquets" fonctionnellement cohérent, et dont l'action se borne à un aspect du cycle de vie de nos processus. C'est ce que le Domain Driven Design appelle le "bounded context" ou contexte d'utilisation.

Et là en toute logique vous allez me dire, "ouais ben super je fais un contexte 'équipe' et je mets tout dedans".

Alors oui,

mais non.

Déjà cela ne sera pas super cohérent avec notre cartographie métier, donc non.
Mais raisonnons par l'absurde, et mettons que l'on modélise une équipe dans son entièreté, avec toutes ses responsabilités, dans un seul module orienté entité. Donc in fine, dans une seule bonne grosse classe.
Pensez-vous que nous allons avoir besoin de toutes les données de la classe tout le temps ?
Question con hein, mais pensez vous que la notion d'équipe affichée dans un tableau de classement, soit la même que la notion d'équipe utilisée dans un rapport de match ?
Dans le premier cas vous allez avoir besoin du nom, du logo de l'équipe, de son coach et des ses point de classement.
Dans le second cas, les points de classement ne vous servent à rien, en revanche il vous la liste des joueurs, leur niveau d'expérience, savoir si ils ont été blessés si ils ont participé au match...
Donc ces deux "facettes" de la notion d'équipe, n'ont en réalité rien à voir l'une avec l'autre, selon le contexte dans lequel on manipule la notion. Si on vient les lier dans l'implémentation, on va passer notre temps à se trimballer des données, dont on ne sert que partiellement. Et ça ralentit toute la machine, à la fois en Run mais aussi en Build, car les structures deviennent trop volumineuse.
Il est donc nécéssaire de dissocier ces deux facettes, et de ranger chaque déclaration a sa place, dans le répertoire qui vient modéliser le contexte d'exécution: le fameux bounded contexte.

C'est pas plus compliqué que ça.

En revanche c'est ultra contre intuitif et c'est aussi un peu l'inverse de ce que l'on nous enseigne à l'école : modéliser une entité avec toutes se responsabilité, pour surtout ne pas se répéter, le fameux principe Don't Repeat Yourself - DRY. C'est ce principe DRY à qui il est obligatoire de dire au revoir, pour le bien de notre architecture, dans une certaine mesure.

Et d'un coup d'un seul deux phénomènes vont apparaître :

  • une simplification drastique à l'échelle locale
  • une complexification non moins drastique à l'échelle de l'architecture de l'application

Mais nous verrons ça quand nous seront dans le code, dans le prochain article, et nous aurons largement le temps d'explorer plus en détails la construction interne d'un Bounded Context, en tant que composant logiciel de notre application.

Nos bounded contextes, et l'approche "Model it wrong"

Cet article à pris davantage de temps à écrire que les autres, pour plusieurs raisons, dont la principale est la peur de se tromper.

En effet j'ai mené une première analyse qui aboutit au schéma ci-dessus, une première version de ce qu'on appelle une contexte map, mais que se passe-t-il si les bounded context identifiés ne sont pas les bons ? il faudra nécessairement retoucher cette map, pour que tout soit bien propre dans l'article, afin de ne pas passer pour un charlot.
Et quelle garantie ai-je à ce stade que ma modélisation est la bonne ? Carrément aucune.

C'est encore un point relativement paralysant dans l'approche DDD. On n'est pas sûr de faire "juste".
Ce que l'on doit apprendre ici, c'est que "juste" est en réalité une notion qui n'existe pas en DDD. Il y a des modélisations qui fonctionnent, et d'autre qui fonctionnent moins bien, mais nous ne savons pas, au moment ou l'on démarre un projet dans quel cas nous nous trouvons. Nous sommes donc bien avancés !

Donc je ne sais pas si la modélisation actuelle de la solution est pertinente, ou non. Et ce n'est pas forcément grave.

Ce qui compte c'est de démarrer. Notre modélisation est forcément fausse, au moins dans une certaine mesure à l'étape ou nous en sommes. Nous avons juste employé des techniques de modélisation du problème qui nous permettent de ne pas faire trop faux, dans une première approche.

C'est comme ça que nous avons envisagé la méthode "Model It Wrong".

Nous jugerons de la pertinence de notre découpage au fur et à mesure de l'implémentation. Si nous sommes amené à faire rentrer trop de responsabilités dans un contexte, ou si certaines entités deviennent pénible à manipuler, c'est le signe que notre granularité est trop importante, et qu'une session de découpage doit être menée, afin sans doute de diviser un bounded context en deux.

Finalement la liste des bounded contextes

Faire la liste complète des bounded contexts, dont TOUTE l'application aura besoin,  va être un exercice fastidieux et inutile à ce stade (déjà parce que nous ne savons pas bien quelles sont les fonctions métier qui vont être couverte in fine par notre appli).
Partons donc avec un premier objectif simple en tête. Cet objectif va nous permettre de rester dans un périmètre raisonnable, et donc de poser l'environnement technique de notre projet DDD.

Nous allons donc, dans un premier temps offrir à nos utilisateurs de :

  • Créer un compte, vérifié par email pour accéder à tout le contenu de la plateforme
  • Créer une équipe
  • Inscrire cette équipe à une ligue (qui aura été préalablement crée par un commissaire de ligue )

Pour pouvoir enchainer ces simples étapes, nous allons avoir besoin de la liste des contextes ci-dessous.

Access:
Le context qui va gérer les droits d'accès et de visualisation au sein de la plateforme. C'est ce contexte qui va contenir les fonctions de login ou de création de compte par exemple. Le domaine Onboarding va s'appuyer sur les fonctions du contexte Access. Je ne suis pas complètement sûr du bien fondé de la présence de ce contexte, un peu trop technique à mon goût, mais partons avec et nous verrons bien si sa présence est nécessaire au fil de l'eau.

Communication :
La fonction naïve est de pouvoir écrire des articles sur la plateforme. On se doute déjà que le métier embarqué dans ce contexte va pas être foufou. Nous allons plutôt être sur (comme disent les agents immobiliers) sur un contexte full CRUD.

TeamRegistration :
L'objectif est ici de permettre aux futurs coach de créer et d'inscrire leurs équipes à une saison, d'une ligue, d'un club. Déjà là il y a beaucoup plus de notions métier. Et il n'est pas du tout garantit que toutes ces notions métiers doivent êtres gérées au sein du contexe TeamRegistration.
- une équipe : créer et inscrire une équipe semble le coeur de la fonctionnalité de ce domaine, donc ok, les entités "équipe" et inscriptions devraient bien être gérées ici
- un club / une ligue / une saison : Ces notions, simples à appréhender, n'ont à priori aucunes raisons d'être gérées complètement par le contexte team registration. Il paraît plus juste que ces notions qui soient gérées par un contexte de création / enregistrement de ligues et de clubs, dédié à cet objectif. Donc le domaine team and clubs devrait en quelque sorte s'abonner aux évènements de gestion qui interviennent dans le domaines LeaguesAndClubRegistration. Un premier cas de communication entre bounded contexts sans doute.

Ceci étant, pour pouvoir créer une team, nous allons également devoir nous appuyer sur les règles du jeu, pour créer des équipes valides. Donc quelque part le contexte RuleRepository devra aussi nous envoyer des infos.

LeaguesAndClubRegistration :

Comme on en a besoin pour créer une équipe, créons donc le contexte qui permet de gérer le cycle de vie des clubs, ligues, saison de compétition. D'ailleurs si on réfléchit cinq minutes, lorsque un de nous utilisateur va créer une ligue, ne va-t-il pas avoir besoin de plus que clubs et ligue ? des règles de départ par exemple, puis des règles de calcul de classement, des définitions de poule également.
Doit on créer un contexte pour ces entités ?
Bonne question, et je ne suis pas sûr qu'il y ait de mauvaises réponses à cette question. Disons que oui, et nous verrons si ce choix est justifié.

RankingRulesCreation :

Ce domaine va nous permettre de créer des règles de calcul de classement. La manière dont nous allons permettre à l'utilisateur de faire ses propres règles ne sera à mon avis pas triviale du tout.

RulesRepository :

La notion de règles rejoint celle de référence. En tout cas dans le jeu qui nous concerne. Telle équipe peut avoir tel et tel joueur, en telle quantité... ce genre de chose. Sachant que nous avons environ 120 à 150 profils de joueurs, répartis en environ 25 types d'équipes différentes, pour vous donner une idée.

Ceci évidemment sans compter les Star players, et les types d'équipes (que l'on appellera roster dans la suite) non-officiels, auquels viennent parfois s'ajouter les roster et joueurs "maisons" c'est a dire inventés par certains coachs, pour certaines ligues seulement.

Donc quand on parle de règle, il s'agit davantage de référentiel que de règles métier en tant que telle.

Ouf !

Bon normalement avec les contextes définis ici, nous devrions être en mesure de pouvoir remplir nos premiers objectifs métier. C'est à dire que, logiquement, nous avons définit les structures en charge de gérer les cylces de vie et les actions métiers que nous allons appliquer sur nos premières entités.

En revanche, pour faire ça, il va nous falloir poser TOUTE la tripaille technique. Il nous faut donc nous appuyer sur un cadre technique qui va supporter au mieux l'expresssion du métier identifié.

Les développeurs vont donc trouver davantage leur compte dans la série d'article qui suit: Le DDD Tactique - la mise en place de l'architecture globale et particulière.