Be Software Architecture Wise
Be Wise Apps

Read in English · Leer en español · Auf Deutsch lesen

Section 5.7 sur 5.8 · Intermédiaire · 26 min de lecture

Conception stratégique pilotée par le domaine : contextes délimités et cartes de contextes

Leçon vidéo

5 min · narré en anglais, avec sous-titres
Écouter seulement (audio)
Lire la transcription

Chaque style de ce chapitre a besoin de frontières. Dans cette leçon, vous allez découvrir la méthode la plus utilisée pour les trouver dans le métier lui-même : la conception stratégique pilotée par le domaine.

Dans un hôpital, le mot patient ne signifie pas la même chose pour chaque service. Pour l’accueil, c’est un nom, une adresse et un rendez-vous. Pour le service de soins, c’est un lit, des médicaments et des observations. Pour le service de facturation, c’est un assureur et une facture. Un seul formulaire géant pour tous serait énorme, et tout le monde se le disputerait. Cela fonctionne mieux quand chaque service tient son propre dossier simple, et que tous s’accordent sur la façon de se transmettre les informations.

La conception pilotée par le domaine, ou DDD, a été présentée par Eric Evans en 2003. Selon lui, les problèmes les plus difficiles des logiciels de gestion ne sont généralement pas techniques : ils tiennent à la compréhension du domaine. Le logiciel est donc construit autour d’un modèle du domaine, élaboré avec les experts du domaine. Tout le monde utilise un vocabulaire partagé, le langage omniprésent, dans les conversations, les documents, les tests et le code. Quand deux groupes emploient le même mot avec des sens différents, vous tenez la preuve de deux modèles différents. C’est là que commencent les frontières.

D’abord, divisez le métier en sous-domaines, et classez-les. Le domaine cœur est ce qui distingue l’entreprise. Pour une entreprise de livraison de repas, c’est probablement l’affectation : associer les commandes aux livreurs et prédire les heures d’arrivée. Il mérite vos meilleures personnes et votre modélisation la plus soignée. Un sous-domaine de soutien est spécifique et nécessaire, mais n’est pas une source d’avantage, comme la gestion des cartes. Il mérite une conception saine, mais plus simple. Un sous-domaine générique est un problème que partagent de nombreuses entreprises, comme les paiements ou l’identité. En général, il vaut mieux l’acheter, ou construire une fine couche au-dessus d’une solution existante.

Venons-en à l’idée clé. Un contexte délimité est une frontière à l’intérieur de laquelle un modèle et un langage s’appliquent de façon cohérente. Ici, une même commande client se présente différemment dans trois contextes. Dans Commandes, c’est un panier avec des prix, des remises et un statut de paiement. Dans Affectation, c’est une mission de livraison : un retrait, une remise, une heure de disponibilité et un livreur. Et en Cuisine, c’est un ticket : des plats, des notes, des allergènes et une durée de préparation. Ils ne partagent que l’ID de commande et les événements qu’ils s’échangent. Chaque modèle reste petit, parce qu’il sert un seul objectif.

Une carte de contextes montre les contextes et leurs relations. Celle-ci concerne la plateforme de livraison de repas, où une seule classe Order partagée avait atteint quatre-vingt-quatorze champs. L’architecte a animé un atelier d’event storming de deux jours, où experts métier et ingénieurs ont placé les événements du domaine sur une ligne du temps, et en a tiré cette carte. Affectation est le cœur. Commandes et Affectation sont en partenariat : ils réussissent ou échouent ensemble, et planifient donc et mettent en production conjointement le passage de relais. Le catalogue des cartes alimente Commandes selon une relation client-fournisseur : les besoins de Commandes entrent dans le backlog de Carte. Face aux systèmes qu’il ne contrôle pas, un contexte construit une couche anticorruption, qui traduit pour que le modèle étranger ne puisse pas s’infiltrer. Carte le fait pour les systèmes de caisse des restaurants, et Paiements pour le prestataire de paiement. Affectation, à l’inverse, est conformiste : il utilise le modèle du fournisseur de cartographie tel quel. C’est un choix délibéré, car le traduire coûterait plus que cela ne rapporte. Paiements offre à tous une API unique, ouverte et documentée, et les événements de commande ont un format publié et versionné.

Les contextes délimités sont le meilleur guide pour tracer les frontières des modules et des services. Chacun a son propre modèle, son langage, ses données et, idéalement, une seule équipe propriétaire. Sur la plateforme, chaque contexte devient d’abord un module d’un monolithe modulaire, avec son propre schéma. À l’intérieur de chaque module, la structure hexagonale de la section cinq point six garde le modèle indépendant de l’infrastructure. Affectation, dont les besoins de passage à l’échelle et de mise en production sont très différents, est le premier candidat à devenir plus tard un service séparé. Sa frontière a déjà la bonne forme. C’est pourquoi de nombreux praticiens disent : trouvez d’abord les contextes, et décidez du déploiement ensuite. Des services qui traversent les contextes produisent un monolithe distribué fortement couplé.

La section complète présente les huit patrons de relation, le fonctionnement de l’event storming, et des blocs de construction comme les agrégats. Ensuite, dans la section cinq point huit, la dernière de ce chapitre, nous nous demandons quand un monolithe est la bonne réponse. À très vite.

Ce qu’il faut savoir

Pourquoi la conception pilotée par le domaine a sa place dans un chapitre sur la structure

Chaque style de ce chapitre a besoin de frontières. Un monolithe modulaire a besoin de frontières entre modules ; les microservices du chapitre 6 ont besoin de frontières entre services ; même une application hexagonale doit savoir ce qui a sa place à l’intérieur de l’hexagone. La conception pilotée par le domaine (DDD) est la méthode la plus utilisée pour trouver ces frontières dans le domaine métier lui-même, plutôt que dans des couches techniques.

Eric Evans l’a présentée dans Domain-Driven Design: Tackling Complexity in the Heart of Software (Addison-Wesley, 2003). Selon lui, dans la plupart des logiciels de gestion, les problèmes les plus difficiles ne sont pas techniques : ils tiennent à la compréhension du domaine, c’est-à-dire à ce qu’est vraiment un contrat d’assurance, un sinistre, une livraison ou un remboursement, et aux règles qui les régissent. Le logiciel devrait donc être construit autour d’un modèle du domaine conçu délibérément, élaboré conjointement par les développeurs et les experts du domaine, et ce modèle devrait être protégé de toute confusion avec d’autres modèles. En 2015, Evans a publié une courte DDD Reference qui résume les patrons et en ajoute quelques-uns, comme le partenariat et la grosse boule de boue (big ball of mud) en tant que patrons de carte de contextes, et les événements de domaine en tant que bloc de construction.

La DDD comporte deux volets. La conception stratégique divise un grand domaine en sous-domaines et en contextes délimités, et cartographie les relations entre eux ; c’est de l’architecture, et c’est le sujet principal ici. La conception tactique fournit des blocs de construction (entités, objets valeur, agrégats et autres) pour écrire le modèle à l’intérieur d’un contexte ; c’est de la conception, et elle est traitée brièvement.

Langage omniprésent (ubiquitous language)

Un langage omniprésent (ubiquitous language) est un vocabulaire partagé, construit ensemble par les développeurs et les experts du domaine, et employé de manière cohérente dans les conversations, les documents, les tests et le code. Si les répartiteurs disent qu’un livreur est « empilé » lorsqu’il transporte deux commandes du même restaurant, alors le code dit stack, et non multiOrderAssignment. Quand un terme se révèle ambigu ou maladroit, l’équipe change le langage et le code en même temps.

Cela ressemble à une simple question de nommage, mais les conséquences sont architecturales. C’est dans la traduction entre les mots du métier et les mots du code que naissent les malentendus, et donc les défauts. Et dès que deux groupes emploient le même mot avec des sens différents, vous tenez la preuve de l’existence de deux modèles différents, ce qui est le point de départ des contextes délimités.

Les sous-domaines : cœur, de soutien et génériques

Le domaine métier dans son ensemble se divise en sous-domaines : des domaines d’activité comme les cartes, les commandes, les paiements et l’affectation des livreurs. Les praticiens les classent en trois catégories, en s’appuyant sur la distinction d’Evans entre le domaine cœur et les sous-domaines génériques ; la répartition en trois, avec les sous-domaines de soutien, a été popularisée par des auteurs ultérieurs, notamment Vaughn Vernon dans Implementing Domain-Driven Design (2013).

Le domaine cœur (core domain) est ce qui distingue l’entreprise de ses concurrents et la raison pour laquelle le logiciel vaut la peine d’être construit. Pour une entreprise de livraison de repas, c’est probablement l’affectation : associer les commandes aux livreurs et prédire les heures d’arrivée. Un sous-domaine de soutien (supporting subdomain) est propre à l’entreprise et nécessaire, mais n’est pas une source d’avantage : la gestion des cartes, l’intégration des livreurs. Un sous-domaine générique est un problème que partagent de nombreuses entreprises, pour lequel il existe de bonnes solutions : les paiements, l’identité, l’e-mail et les SMS.

Ce classement est une décision d’investissement. Le cœur mérite les personnes les plus expérimentées, la modélisation la plus riche et du code que vous possédez entièrement, car c’est là qu’un meilleur logiciel se traduit directement en avantage pour l’entreprise. Les sous-domaines de soutien méritent des conceptions saines mais plus simples, souvent un module en couches classique. Les sous-domaines génériques devraient généralement être achetés, utilisés sous forme de service ou construits comme de fins adaptateurs au-dessus de composants open source : construire votre propre fournisseur d’identité, c’est dépenser de l’effort là où vous ne pouvez pas gagner. Le classement évolue aussi avec le temps ; une capacité de soutien peut devenir cœur quand la stratégie de l’entreprise change.

Les contextes délimités

Un contexte délimité (bounded context) est une frontière explicite à l’intérieur de laquelle un modèle, et un langage omniprésent, s’appliquent de façon cohérente. À l’intérieur, « commande » a exactement un sens. À l’extérieur, le même mot peut signifier autre chose, et c’est acceptable, car chaque modèle est conçu pour un seul objectif. Evans a observé que vouloir unifier en un seul le modèle d’un grand système n’est ni faisable ni rentable ; la tentative produit d’énormes classes partagées qui servent mal tous les objectifs et que chaque équipe doit modifier.

Sous-domaines et contextes délimités sont des choses différentes, même s’ils sont souvent alignés. Un sous-domaine fait partie du problème : il existe dans l’entreprise, qu’il y ait un logiciel ou non. Un contexte délimité fait partie de la solution : une frontière que vous choisissez pour un modèle et, généralement, pour du code et des données qui appartiennent à une seule équipe. L’idéal est un contexte délimité par sous-domaine, mais les systèmes hérités ont souvent un seul contexte qui s’étend sur plusieurs sous-domaines, et un grand sous-domaine peut avoir besoin de plusieurs contextes.

Un mot, trois modèles

Le mot commande a des sens différents dans trois contextes délimités d’une plateforme de livraison de repas ; chaque contexte a son modèle, relié par un identifiant de commandeCommandesCommande = panier, prix,remises, statut de paiementétats : ébauche, passée,payée, annuléeAffectationMission = retrait, remise,heure de disponibilité, livreurétats : en attente, affectée,récupérée, livréeCuisineTicket = plats, notes,allergènes, durée prép.états : reçu, en cuisson,prêtSeuls partagés : l’ID de commande (A-17) et les événements échangés entre contextesChaque modèle est simple car il sert un seul objectif
« Commande » désigne un panier avec des prix pour Commandes, une mission de retrait et de remise pour Affectation, et une liste de plats à préparer dans le contexte Cuisine ; chaque modèle reste petit parce qu’il sert un seul objectif.

Les cartes de contextes

Une carte de contextes (context map) montre les contextes délimités d’un système et les relations entre eux. Ces relations décrivent à la fois l’intégration technique et les rapports de force et de communication entre les équipes : c’est pourquoi la cartographie des contextes concerne autant les organisations que le code. C’est une application directe de la loi de Conway : la carte montre où les canaux de communication de l’organisation et les chemins d’intégration du système doivent coïncider.

Beaucoup de relations sont de type amont/aval (upstream/downstream) : les décisions du contexte amont affectent le contexte aval, mais pas l’inverse. L’équipe aval doit alors choisir comment s’en accommoder, et c’est ce choix que nomment la plupart des patrons.

Patrons de relation de la carte de contextes

PatronCe que cela signifieQui s’adapte à quiExemple de livraison de repas
PartenariatDeux équipes réussissent ou échouent ensemble : elles planifient donc conjointement et font évoluer leur interface ensemble.Les deux, d’un commun accordCommandes et Affectation mettent en production ensemble le passage de relais de la commande à la livraison.
Noyau partagé (shared kernel)Deux contextes partagent une petite partie du modèle et de son code, explicitement convenue ; toute modification exige l’accord des deux équipes.Les deux, avec prudenceAffectation et Intégration des livreurs partagent une toute petite bibliothèque de profil de livreur (type de véhicule, ID du livreur).
Client-fournisseurL’équipe amont (le fournisseur) s’engage à répondre aux besoins de l’équipe aval (le client), qui font partie de sa planification.L’amont sert l’avalLe catalogue des cartes fournit les cartes et les prix à Commandes, et les besoins de Commandes entrent dans le backlog de Carte.
ConformisteL’équipe aval adopte le modèle amont tel quel, parce qu’elle ne peut pas l’influencer et que la traduction n’en vaut pas le coût.L’aval se conformeAffectation utilise directement le modèle d’itinéraires et de localisation du fournisseur de cartographie.
Couche anticorruptionL’équipe aval construit une couche de traduction pour que le modèle amont ne puisse pas s’infiltrer dans le sien.L’aval traduitCarte traduit des dizaines de formats de caisse (point de vente) des restaurants dans son propre modèle de carte ; Paiements isole l’API du prestataire.
Service hôte ouvert (open host service)Le contexte amont offre un protocole ou une API bien défini à tous les consommateurs, au lieu d’une intégration sur mesure pour chacun.L’amont publie une foisPaiements offre une API de paiement unique et documentée à Commandes, aux remboursements et aux abonnements.
Langage publié (published language)Un format d’échange partagé et documenté, souvent utilisé avec un service hôte ouvert.Les deux utilisent le langageLes événements de commande (OrderPlaced, OrderCancelled) ont un schéma versionné et documenté.
Chemins séparésLes contextes ne sont pas intégrés du tout, parce que le bénéfice est inférieur au coût.AucunLe reporting de la paie des livreurs se fait dans un outil financier séparé, alimenté par un export mensuel, et non par une intégration en direct.

Deux de ces patrons méritent une attention particulière. Une couche anticorruption (anticorruption layer, ACL) est un ensemble d’adaptateurs et de traducteurs qu’un contexte aval construit pour qu’un modèle étranger, souvent un système hérité ou un fournisseur externe, ne puisse pas s’infiltrer dans son propre modèle. C’est la même idée que l’adaptateur piloté de la section 5.6, appliquée entre contextes, et c’est le choix par défaut pour s’intégrer à tout ce que vous ne contrôlez pas. Un service hôte ouvert (open host service) associé à un langage publié (published language) est la façon dont l’amont sert bien de nombreux consommateurs : un seul protocole documenté et versionné au lieu d’une intégration sur mesure pour chacun. Un noyau partagé (shared kernel) doit rester petit ; un noyau partagé trop grand refait de deux contextes un seul, que deux équipes se disputent, et la référence d’Evans de 2015 ajoute la grosse boule de boue comme étiquette honnête pour une zone sans aucune frontière claire, qu’il faut contenir plutôt qu’étendre.

L’event storming : découvrir les contextes

Les frontières se découvrent avec les experts du domaine ; on ne les conçoit pas seul. L’event storming, créé par Alberto Brandolini et décrit pour la première fois sur son blog en 2013, est le format d’atelier le plus populaire pour cela. Les personnes qui connaissent le métier et celles qui construisent le logiciel se tiennent devant un long mur de papier et écrivent des événements de domaine (des choses qui se sont produites, au passé : Commande passée, Livreur affecté, Repas récupéré) sur des notes adhésives orange, qu’elles placent sur une ligne du temps.

Le groupe ajoute ensuite les commandes qui provoquent les événements, les personnes et les systèmes externes impliqués, les politiques qui réagissent aux événements (« chaque fois qu’une commande est passée, trouver un livreur ») et, surtout, les points chauds (hot spots), là où les gens ne sont pas d’accord ou où personne ne sait. Les groupes d’événements qui partagent un langage et un objectif suggèrent des contextes délimités ; les endroits où le même mot est employé différemment suggèrent des frontières ; les endroits où se produit un passage de relais suggèrent des points d’intégration. Une séance de vue d’ensemble (big picture) pour toute une entreprise dure un jour ou deux et ne demande presque aucune préparation, en dehors des bonnes personnes et de beaucoup de place au mur.

Les patrons tactiques en bref

À l’intérieur d’un contexte délimité, la DDD propose des blocs de construction pour le modèle. Une entité a une identité qui persiste pendant que ses attributs changent : une commande reste la même commande après un changement de statut. Un objet valeur (value object) n’a pas d’identité et n’est défini que par ses valeurs : deux objets Money(12.50, "GBP") sont interchangeables, et les objets valeur sont généralement immuables. Un événement de domaine enregistre quelque chose de significatif qui s’est produit dans le domaine.

Un agrégat est un groupe d’entités et d’objets valeur traités comme une seule unité pour les modifications, avec une entité comme racine. Le code extérieur ne peut détenir de références que vers la racine et ne doit modifier l’agrégat que par son intermédiaire, de sorte que la racine peut faire respecter les invariants de l’agrégat (des règles qui doivent toujours être vraies, comme « aucun article ne peut être ajouté après qu’une commande a été passée »). Une transaction ne devrait modifier qu’un seul agrégat ; les autres agrégats sont référencés par leur ID et mis à jour par des événements de domaine, ce qui fait des agrégats l’unité naturelle de cohérence et, plus tard, de distribution. Un repository charge et enregistre des agrégats entiers et se présente au domaine comme une collection ; son interface est un port piloté, en termes d’architecture hexagonale.

from dataclasses import dataclass, field
from decimal import Decimal

@dataclass(frozen=True)
class Money:                               # value object: equal if values equal
    amount: Decimal
    currency: str
    def __add__(self, other):
        if other.currency != self.currency:
            raise ValueError("currency mismatch")
        return Money(self.amount + other.amount, self.currency)
    def times(self, n: int):
        return Money(self.amount * n, self.currency)

@dataclass
class OrderLine:                           # entity inside the aggregate
    dish_id: str
    qty: int
    price: Money

@dataclass
class Order:                               # aggregate root
    id: str
    restaurant_id: str                     # other aggregates referenced by ID only
    lines: list[OrderLine] = field(default_factory=list)
    status: str = "draft"
    events: list = field(default_factory=list)

    def add(self, dish_id, qty, price):
        if self.status != "draft":
            raise ValueError("order already placed")    # invariant
        self.lines.append(OrderLine(dish_id, qty, price))

    def place(self, minimum: Money):
        total = Money(Decimal(0), minimum.currency)
        for line in self.lines:
            total = total + line.price.times(line.qty)
        if total.amount < minimum.amount:
            raise ValueError("below restaurant minimum")  # invariant
        self.status = "placed"
        self.events.append({"type": "OrderPlaced", "order_id": self.id, "total": str(total.amount)})
Un agrégat Order : un objet valeur (Money), une entité (OrderLine), la racine de l’agrégat qui fait respecter les invariants, et un événement de domaine enregistré lorsque la commande est passée.

L’exemple garde l’agrégat petit, référence le restaurant par son ID au lieu de détenir un objet restaurant, et fait respecter ses règles dans des méthodes au lieu de les laisser aux appelants. Une erreur courante est l’inverse : des agrégats si grands que la modification de chaque utilisateur verrouille les mêmes lignes, ou des objets du domaine qui ne sont que des sacs de getters et de setters, tandis que les règles vivent dans des classes de service (Martin Fowler appelle cela le modèle de domaine anémique).

Exemple commenté

Exemple commenté : une carte de contextes pour la plateforme de livraison de repas

La plateforme a grandi jusqu’à environ 60 ingénieurs répartis en huit équipes, et chaque modification semble toucher la classe partagée Order, qui compte désormais 94 champs. L’architecte anime un atelier d’event storming de deux jours avec les chefs de produit, le personnel des opérations, le support aux restaurants et les ingénieurs, puis dessine la carte de contextes présentée ci-dessous.

1. Trouver les sous-domainesD’après la ligne du temps des événements : gestion des cartes, commande et paiement, paiements, affectation (associer les livreurs aux commandes et prédire les heures d’arrivée), intégration des livreurs, notifications aux clients, intégration des restaurants.
2. Les classerL’affectation est le cœur : des livraisons plus rapides et moins chères sont ce qui gagne les clients et les restaurants. La carte, les commandes et l’intégration des livreurs sont de soutien : nécessaires et propres à cette entreprise, mais ce n’est pas là qu’elle se distingue de la concurrence. Les paiements et les notifications sont génériques : des problèmes déjà résolus, qu’il vaut mieux acheter ou construire comme une fine couche au-dessus d’un fournisseur.
3. Dessiner les contextes délimitésChaque sous-domaine reçoit son propre modèle. La « commande » de Commandes (panier, prix, statut de paiement) est séparée de la « mission de livraison » d’Affectation (retrait, remise, heure de disponibilité) et du « ticket » de la vue cuisine du restaurant. La classe de 94 champs devient trois modèles ciblés, reliés par l’ID de commande.
4. Nommer les relationsCommandes et Affectation : partenariat. Carte vers Commandes : client-fournisseur. Paiements : service hôte ouvert avec un langage publié, et une couche anticorruption vers le prestataire externe. Carte : couche anticorruption vers les systèmes de caisse des restaurants. Affectation : conformiste vis-à-vis de l’API de cartographie, un choix délibéré, car traduire son modèle riche coûterait plus que cela ne rapporte.
5. Décider de l’investissementLes meilleurs ingénieurs et la modélisation la plus soignée vont à Affectation. Paiements utilise un prestataire au travers d’un fin adaptateur. Notifications utilise un service de messagerie acheté.
6. Décider du déploiementLes contextes deviennent d’abord des modules de l’application existante, qui se transforme ainsi en monolithe modulaire (section 5.3), chacun avec son propre schéma et son interface publique. Affectation, dont les besoins de passage à l’échelle et de mise en production sont très différents, est le premier candidat à devenir plus tard un service séparé.

Classer Commandes comme sous-domaine de soutien a surpris certaines parties prenantes. Le critère n’est pas l’importance (l’entreprise ne peut pas fonctionner sans prise de commande) mais la différenciation : un meilleur modèle de commande gagnerait-il à l’entreprise plus de clients qu’un meilleur modèle d’affectation ?

La relation conformiste avec le fournisseur de cartographie est un compromis conscient, consigné dans un enregistrement de décision, et non un accident. Si le fournisseur change ou est remplacé, Affectation le ressentira.

La carte de contextes

Carte de contextes de la plateforme de livraison de repas : contextes délimités, systèmes externes et patron de relation sur chaque connexionCaisses (POS)externes, multi-éditeursAPI cartoexterneCat. des cartesde soutienCommandesde soutienAffect.cœurPaiementsgénériqueNotificationsgénériqueIntégr. livreursde soutienPrestat. paiementexterneUDACL (Carte)UDConformisteUDclient /fourniss.PartenariatDUOHS / PLévts commande(langagepublié)Noyau partagéDUACL (Paiements)U = amont, D = avalBoîte corail : sous-domaine cœur ; grise : système externePointillés : événements asynchrones
Chaque ligne nomme le patron de relation ; U et D marquent l’amont (upstream) et l’aval (downstream), et les couches anticorruption se trouvent du côté aval, face aux systèmes que la plateforme ne contrôle pas.

Les contextes délimités comme frontières de modules et de services

Les contextes délimités sont le meilleur guide disponible pour savoir où tracer les frontières des modules dans un monolithe et celles des services dans un système distribué. Un contexte a son propre modèle, son propre langage, ses propres données et, idéalement, une seule équipe propriétaire : ce sont exactement les propriétés qui rendent un module cohésif et faiblement couplé. Dans un monolithe modulaire, chaque contexte devient un module avec son propre schéma ; à l’intérieur, la structure hexagonale de la section 5.6 garde le modèle indépendant de l’infrastructure.

Si un contexte doit plus tard être déployé séparément, sa frontière a déjà la bonne forme pour un service, et ses relations sur la carte de contextes vous indiquent à quoi doit ressembler l’intégration. C’est pourquoi de nombreux praticiens recommandent de trouver d’abord les contextes et de décider du déploiement ensuite. L’inverse est vrai aussi : des services qui traversent les contextes délimités (un « service des commandes » dans lequel écrit chaque contexte) produisent le monolithe distribué fortement couplé étudié à la section 6.3. Notez aussi qu’un contexte délimité n’est pas automatiquement un microservice ; un contexte peut être déployé sous forme de plusieurs services, et plusieurs petits contextes peuvent partager un même déploiement.

Ce que la DDD stratégique apporte et ce qu’elle coûte

AspectEffetJustification
Modifiabilité au sein d’un contexteÉlevéUn modèle, un langage et un propriétaire : les modifications restent locales et faciles à raisonner.
Autonomie des équipesÉlevéLes frontières des contextes s’alignent sur celles des équipes : les équipes peuvent donc modifier leurs modèles sans négocier chaque changement.
Adéquation entre le logiciel et le métierÉlevéLe langage omniprésent et la modélisation conjointe réduisent les erreurs de traduction entre les experts et le code.
Effort initial et continuMoyen à élevéLes séances de modélisation, le temps des experts du domaine, les couches de traduction et la maintenance des contrats demandent tous de l’effort.
Duplication des données et intégrationCoût moyenChaque contexte garde sa propre copie de ce dont il a besoin, qu’il faut maintenir synchronisée au moyen d’événements ou d’API.
Intérêt pour les domaines simplesBasPour les systèmes surtout CRUD, avec peu de logique, la DDD tactique en particulier ajoute du formalisme pour un faible retour.

Vérifiez votre compréhension

Quelle est la différence entre un sous-domaine et un contexte délimité ?

Un sous-domaine fait partie du problème métier et existe qu’il y ait un logiciel ou non ; un contexte délimité est une frontière choisie dans la solution, à l’intérieur de laquelle s’applique un modèle. Idéalement, ils sont alignés, mais ce n’est pas obligatoire.

Votre équipe s’intègre à un système de facturation hérité dont le modèle est confus et que vous ne pouvez pas modifier. Quel patron de carte de contextes protège votre modèle ?

Une couche anticorruption : des traducteurs de votre côté qui convertissent le modèle hérité dans vos propres termes, pour qu’il ne puisse pas s’infiltrer dans votre contexte.

Pourquoi une transaction ne devrait-elle généralement modifier qu’un seul agrégat ?

Parce que l’agrégat est l’unité dont les invariants doivent être vrais ensemble ; en modifier plusieurs à la fois crée de gros verrous et du couplage, alors que les autres agrégats peuvent être mis à jour par des événements de domaine, ce qui permet aussi de les distribuer plus tard.

Pourquoi les paiements sont-ils généralement un sous-domaine générique pour une entreprise de livraison de repas ?

Parce qu’encaisser des paiements est un problème déjà résolu, partagé par de nombreuses entreprises, et que ce n’est pas là que l’entreprise se distingue de la concurrence : mieux vaut l’acheter ou le construire comme une fine couche au-dessus d’un prestataire que le modéliser en profondeur.

Suivant · 5.8Choisir un monolitheMonolithFirst, le surcoût des microservices et le monolithe majestueux ; quand une seule unité de déploiement est le bon choix, jusqu’où un monolithe peut passer à l’échelle, les signes qu’il est temps de le découper, et un guide de décision pour les styles du chapitre 5.Continuer →

Sources et lectures complémentaires

Une question sur cette section ? ArchitectBot, l’assistant d’étude IA du cours, répond d’abord à partir du cours.

Demander : « Notre table « Customer » est utilisée par les ventes, le support et la facturation, et personne n’ose la modifier. Comment utiliser les contextes délimités pour la démêler sans réécriture big bang ? »