Skip to content

Instantly share code, notes, and snippets.

@joanrieu
Created May 12, 2017 11:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joanrieu/0cf89e31d8771f8c3ba1891bfac1b318 to your computer and use it in GitHub Desktop.
Save joanrieu/0cf89e31d8771f8c3ba1891bfac1b318 to your computer and use it in GitHub Desktop.
Notes d'une formation DDD donnée par Cyril Martraire
title author date
DDD
Cyrille Martraire
10 et 11 Avril 2017

Introduction

DDD se situe du côté de XP. Bien comprendre le métier : transformer le savoir en code. Important pour les micro-services pour faire le découpage au bon endroit.

Références

Concepts importants

Coeur de métier

Faire attention au coeur de métier. Appliquer un effort proportionnel à la relevance d'un sujet particulier vis-à-vis du core domain. Le coeur de métier peut ne pas être le logiciel : certaines boîtes se concentrent plus sur la pub du logiciel que le logiciel (qui se contente d'être good enough).

La pertinence métier bat la technique 3 fois sur 4.

C'est à relier au make or buy. Attention à ne pas tenter de customizer la partie buy, ça revient à du développement mais dans les pires conditions.

La valeur et le risque sont dans le domaine métier.

Une partie de DDD c'est savoir quand ne pas faire de DDD.

Modélisation

Le vocabulaire contient de la connaissance, c'est un citoyen de première classe : il faut être VERBATIM. Il faut qu'on puisse apprendre le métier en lisant le code.

Contrairement à UML, on considère que le code est le meilleur modèle. Il ne faut donc pas le générer à partir d'autre chose (comme un modèle graphique), pour éviter une traduction (qui entraîne une perte de fidélité). Par contre, on peut générer un diagramme à partir du code pour les besoins de documentation.

Le processus de modélisation est itératif.

Experts métiers

On est tous des experts métiers.

On a porté des pantalons toute notre vie.
On est experts métier des pantalons.

On a un modèle mental mais il est tacite.

On le sait mais on ne sait pas l'expliquer.

Les experts métier ont beau être compétents, il ne sont pas parfaits. Il faut les aider à expliquer leur savoir tacite.

Technique

What would happen if [stretch the model]?

Un bon modèle a un pouvoir prédictif, pour répondre à des cas jamais vus.

Résumé

On a pas réellement de projets DDD, c'est une démarche. On ne peut pas le voir sur un diagramme d'architecture. Il n'y a pas de frameworks DDD, même si on en trouve (qui couvrent des concepts techniques).

DDD, c'est subordonner le technique au métier.

Le langage de programmation n'a pas d'importance, mais c'est plus facile avec un langage expressif (type F#).

Bounded Context

Défini de façon abstraite par Eric Evans. Voir aussi Bounded Context (Martin Fowler).

Zone de vocabulaire qui précise un mot.

Exemple

On demande de décrire un client idéal à chaque département de l'entreprise. Pour un même concept, on obtient des descriptions totalement différentes. Pourquoi ? Il y a beaucoup de facettes distinctes pour un même concept de client.

Mais on a tedance à créer une grosse table "client" dans la base de donnée !?

On a en fait modélisé plusieurs systèmes, un pour chaque contexte. Il n'y a pas matière à trop les mélanger, puisque chaque service a des objectifs différent.

Pour clarifier le langage, on peut regarder les semi-synonymes du mot "client" trop vague.

  • Marketing : "This ad campaign is only for 30+ years old audience."
  • Sales : "Visitor browses the online catalog."
  • Billing : "Customer sees recommendations from own past purchases."
  • Shipping : "Recipient was not there to answer the door bell at delivery time."
  • Support : ...

On peut découvrir que ce "client" peut en fait être plusieurs personnes.

Langage commun

On ne peut pas avoir un langage précis / formel dans un groupe au dela d'une quinzaine de personnes. Il faut fragmenter et spécialiser le vocabulaire à chaque contexte, voire sous-contexte.

On ne peut pas avoir un langage commun pour toute l'entreprise.

On a donc un Domain Language (Ubiquitous Language) par Bounded Context.

Découpage

On a généralement plusieurs contextes :

  • s'il y a plusieurs services dans l'entreprise ;
  • s'il y a des contradictions ;
  • s'il y a trop de données par rapport à ce qui est fait.

On peut choisir pour chaque contexte quels outils sont les plus adaptés, pour le traitement comme pour la persistence.

Tout faire avec une seule techno, c'est impensable !

La volumétrie peut être très différente aussi (élasticité, scaling).

Même l'équipe de développement peut être différente (component teams) ! C'est incompatible avec les feature teams de Spotify qui sont de bout-en-bout par rapport à l'utilisateur.

On obtient des micro-services verticaux d'un point de vue technique, qui vont de l'UI à la persistence. On a des tranches qui peuvent être lecture-seule et d'autres écriture-seule (typiquement catalogue selon qu'on est acheteur ou vendeur).

Difficulté

Les contextes sont plutôt orientés back-end, car les utilisateurs sont généralement à cheval sur plusieurs contextes. Un même écran peut tout mélanger.

Ils faut des technologies de mashup.

La façon naïve charge chaque morceau d'une page web de façon indépendante. On peut mettre en place un back-end for front-end qui fait de l'aggrégation.

Il est intéressant de séparer sur un diagramme les domaines métiers des composants liés à l'interface utilisateur.

Synchronisation

Il y a toujours quelques points d'attache entre les services, le mot clé étant "projection" d'un modèle vers un autre, avec les problématiques de synchronisation.

Il faut poser la question de délai de synchronisation d'un point de vue métier. On dit non aux transactions distribuées et à l'atomicité. On dit adieu à la consistence immédiate. Mais "immédiat" veut souvent dire moins d'une second pour un humain.

Voir l'e-book Migrating to Microservice Databases (Edson Yanaga).

DRY / Couplage

Le principe Don't Repeat Yourself introduit du couplage. Même si des données dans deux domaines sont a priori identiques, il peut être sage de les dupliquer !

Au début, tous les métiers sont CRUD.

Comprendre le métier

Lire la bible, le livre officiel de référence, du métier. Regarder les modèles OASIS qui sont une base pour réfléchir sur beaucoup de métiers. Se servir des ressources comme checklists et bases pour entamer la discussion.

The plural of "domain expert" is "confusion".
Or potential Bounded Contexts.

Quand il y a deux points de vue, il y a deux contextes.

Idées

  • Organiser des sessions de formation de 30min, deux fois par semaine, avec un expert métier.
  • Définir tous les mots nouveaux (sans a priori sur les synonymes, qu'il faut confirmer ou infirmer).
  • Parler avec l'expert à la machine à café plutôt qu'à son bureau.
  • S'inspirer de se que font les testeurs (ex: mind-map à partir de l'acronyme CRUD).
  • Chercher les antagonismes (une chose qui en contre-balance une autre).
  • Demander pourquoi jusqu'à en arriver à parler d'argent (tout besoin exprimé est généralement plus une solution dont le besoin est à trouver).
  • Demander des exemples concrets dès qu'on a trop d'aisance !
  • Demander des documents réels comme exemples.
  • Utiliser du BDD pour faire l'interview (scénarios + given / when / then).
  • Déterminer les invariants du système (et ne pas modéliser ce qui peut être déduit, pour réduire les risques d'erreurs).
  • Demander dans quelles situations quelque chose est faux.

Voir aussi : Liz Keogh.

Cunningham' Law:
The best way to get the right answer on the Internet [...] is to post the wrong one.
-- Ward Cunningham

L'expert métier

Rester en posture basse, même quand on pense que l'expert métier a tort. Rappeler que l'on est pas là pour lui prendre sa place ou le rendre inutile.

Toujours demander confirmation même quand on est sûr. L'expert métier a toujours le dernier mot.

Tactiques

Voir :

Value Objects

  • Immutable.
  • Identité comparable champ par champ.
  • Fait ses traitements lui-même (pas Plain Old Data ni un Data Tranfser Object).

Deux instances sont indistinguables si elles ont les mêmes champs.

Il faut un Builder pour construire une instance.

Exemple : un boulon.

Entities

  • Identité arbitraire.
  • Persistent Data Structure qui connait son histoire via une collection append-only de Value Objects.

L'identité ne change pas, tout au long de sa vie (indépendament de son contenu).

Exemple : la pomme de Cyrille (qu'elle soit verte, rouge ou marron, un peu ou beaucoup mangée).

Aggregate

  • Transactionnel (consistency boundary).
  • Valide.

Collection d'objets avec :

  • une identitée collective,
  • une racine de l'aggrégat.

Un objet est soit complètement dans l'aggrégat (tout son contenu est dans l'aggrégat), ou totalement en dehors (référencé par un identifiant).

Exemple : plateau repas qui aggrège ce qui est posé dessus.

Peut être utilisé pour du sharding avec un.

Exemple : une comptabilité groupée par année.

Exercice

Prendre des notes en écrivant du code.

Il y a une vie avant TDD.

Domain Service

On peut avoir des services externes que l'on souhaite utiliser mais qui sortent du domaine (du legacy par exemple).

On fait de l'inversion de dépendance avec une Interface dans le domaine et un Adapter qui l'implémente à l'extérieur et fait appel au legacy. On peut aussi construire un Mock pour les tests.

Rappels

Les domaines font partie du métier (requirements), les bounded contexts font partie de la solution (design).

Quand deux personnes tiennent un discours incohérent, c'est probablement qu'elles sont chacune dans un bounded context différent, avec des représentations différentes.

Modèle unifié réutilisable

Quand on parle de construire un modèle unifié réutilisable, il faut faire attention.

On peut s'en approcher mais en général, un modèle unifié sera trop complexe et trop verbeux pour tout le monde. Et il ne sera jamais totalement aligné avec chaque problématique.

Qui plus est, construire ce type de modèle coûte une fortune car il nécessite une coordination à grande échelle.

Ce type de modèle peut devenir plus flexible s'il est accepté qu'il puisse se fragmenter en dialectes plus adaptés à certains contextes, mais il n'a plus l'aspect unifié voulu au départ.

Découpage

Un Bounded Context ne peut pas se résumer à une Entity.

C'est jamais une bonne idée.

Exemple : User ne fera jamais un bon microservice, c'est une entité que l'on va retrouver partout, on ne peut pas la factoriser ! Tout le monde va en dépendre, et tout le monde va chercher à le modifier. C'est un concept qui va prendre une forme différente dans chaque domaine.

Astuce : généralement, un bon candidat a un nom en -ing ou -tion (verbe substantivé).

Astuce : la nécessité d'une granularité différente est un bon indice d'un découpage à faire entre deux contextes.

Exercice

Découper en micro-services une élection politique.

Bounded Context Notions
Registration citoyen, liste électorale
Voting électeurs, élection, candidats
Counting bulletins, élection, résultats
Reporting suffrages, élection, candidats

Context Mapping

Quand on introduit une dépendance entre services, on passe de separate ways à integration.

Exemple : un calcul de risque dépend d'un calcul de prix.

Approche conformist

Le service downstream réutilise directement le langage () de l'upstream.

(+) Facile (-) Prisonnier

Approche anti-corruption layer

Un filtre prend les données de l'upstream, jette l'inutile et traduit ce qui est conservé. Si l'upstream change son modèle, seul le filtre est impacté.

On s'approche de l'architecture hexagonale.

Note : si le mapping est difficile (trop gros), il y a probablement un problème avec le modèle.

(+) Autonomie (-) Coûteux

Stratégies

L'architecture hexagonale est le bon choix par défault.

On ommence souvent conformist et on isole progressivement quand le besoin apparaît avec un ACL.

Si l'upstream est legacy, c'est généralement qu'on est insatisfait de ce dernier et qu'un ACL aurait sa place.

Si le vocabulaire du domaine est différent, un ACL est potentiellement nécessaire.

Quand il existe un standard stable, il est raisonnable d'introduire deux ACL et de briser la dépendance.

Rétro-compatibilité

Deux règles :

Ignore unknown fields.
Never remove, always add.

Voir : consumer-driven contract.

Persistance

On introduit un repository pour chaque aggrégat, ou pour chaque sous-ensemble d'aggrégat (cf la note sur le sharding plus haut).

Dans la mesure du possible, on peut tendre vers l'approche Functional Core, Imperative Shell avec le domaine lui-même totalement pur, testable, cachable, et une interaction avec l'extérieur dans une couche impérative autour du coeur pur.

Astuce : avant de lui trouver un meilleur nom, on peut l'appeler All[...]s.

Note : s'il y a du batch à effectuer en direct sur l'espace de stockage, par exemple pour du reporting, c'est possible mais c'est le même domaine de responsabilité que le code du service (géré par la même équipe).

Command Query Responsibility Segregation

Quand on pousse un modèle suffisament loin, on arrive souvent à distinguer un modèle / contexte lié à l'écriture et un lié à la lecture. Il convient alors de les séparer dans l'implémentation.

Il n'y a pas de solution optimale pour intégrer ces deux systèmes.

  • On peut utiliser des vues matérialisées en base de donnée SQL.
  • On peut faire de l'envoi de message.
  • On peut faire du polling (à ne pas négliger).

On passe à de l'Eventual Consistency.

Event Sourcing

Consiste à stocker sous forme d'événements les résultats des traitements et à utiliser une projection pour reconstituer l'état courant du système.

Certains modèles se prêtent bien à l'Event Sourcing mais si cela n'apparaît pas dans le modèle lui-même, cela risque créer une complexité inutile.

Par ailleurs, faire de l'Event Sourcing implique de faire du CQRS.

Attention : le modèle d'événements utilisé est privé, il n'est pas à publier sur un bus au risque qu'ils deviennent une forme de contrat public.

Note : c'est à distinguer du Command Sourcing qui stocke les commandes plutôt que les résultats des traitements (contrairement à ce qu'a pu dire Martin Fowler).

C'est la seule fois de ma vie où je pense que Martin Fowler a tort.

Monoids

Utiliser des monoïdes peut être avantageux dans beaucoup de traitements.

Un monoïde est composé d'un semi-groupe, c'est à dire un opérateur + associatif mais pas nécessairement commutatif, et d'un élément neutre, qui ne change rien au résultat.

Ca permet de diviser le travail, de recombiner les résultats, voire d'agréger à une granularité plus faible.

Système fermé

On peut construire un système fermé en ne fournissant que des opérations qui retombent dans le même monoïde (ou type dans le cas où on n'a pas un monoïde).

C'est applicable aux Value Objects.

Diagnostic

Séparation métier / technique

Générer un diagramme de classes simplifié avec la partie domaine encadrée. On peut aisément identifier ce qui n'est pas à sa place.

Ubiquitous Language

Construire un nuage de mots à partir du code source. Le métier doit ressortir visiblement.

On peut même générer un glossaire à partir du code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment