Domain-Driven Design
Chapitre 5 / 10 · 13 min de lecture

Sous-domaines & distillation

Tout le domaine n'a pas la même valeur : concentrer le meilleur effort sur le Core Domain, isoler le générique.

Dans un grand système, tous les morceaux semblent indispensables. Le calcul des prix, l'envoi des e-mails, la gestion des fuseaux horaires, le catalogue, l'authentification : chacun est nécessaire au fonctionnement, et chacun réclame du temps. Mais ils n'ont pas tous la même valeur. Certains font la différence sur le marché ; d'autres sont des problèmes déjà résolus mille fois ailleurs. La tragédie ordinaire des projets logiciels, c'est de répartir l'effort uniformément — voire d'envoyer les meilleurs développeurs sur les parties les plus « techniquement intéressantes », qui sont presque toujours les moins distinctives.

Eric Evans consacre tout un chapitre à ce problème sous le nom de distillation. Distiller, c'est séparer les composants d'un mélange pour en extraire l'essence sous une forme plus précieuse et plus utile. Appliquée au modèle de domaine, l'idée est double : identifier le cœur de métier (Core Domain) — la partie qui justifie l'existence même du logiciel — puis le débarrasser de tout ce qui l'encombre, jusqu'à ce qu'il devienne visible, petit et puissant. Ce chapitre déroule cette démarche stratégique : découper le domaine en sous-domaines, reconnaître les trois grandes catégories, et appliquer les techniques qui rendent le cœur explicite.

Découper le domaine en sous-domaines

Un domaine métier complet est presque toujours trop vaste et trop complexe pour être appréhendé d'un seul tenant. Un sous-domaine (Subdomain) est une sous-partie cohérente de ce domaine, un modèle logique que l'on peut raisonner isolément : la tarification, l'expédition, le catalogue, la facturation, l'authentification… Chaque sous-domaine correspond à un domaine d'expertise clair, avec un ou plusieurs experts métier qui le maîtrisent, et une importance stratégique plus ou moins grande pour l'entreprise.

Le sous-domaine appartient à l'espace du problème : il décrit ce que l'entreprise doit accomplir. Le contexte délimité (Bounded Context), lui, appartient à l'espace de la solution : c'est la frontière dans laquelle un modèle et son langage vivent de façon cohérente. L'objectif idéal du DDD est de les aligner un pour un : un sous-domaine, un contexte délimité.

Note

Sous-domaine = espace du problème (la réalité métier à couvrir). Contexte délimité = espace de la solution (la frontière de votre modèle). Quand les deux coïncident, le contexte reste propre et focalisé sur une seule intention stratégique.

Cet alignement n'est pas toujours atteignable. Lorsqu'on doit héberger un second modèle dans le même contexte — par exemple une capacité de support à l'intérieur du contexte du cœur de métier —, il faut au minimum le séparer dans un module dédié (un package / namespace). Ce simple geste fait une déclaration linguistique forte : ceci est le cœur, cela n'est que du support.

Les trois types de sous-domaines

Toute la stratégie tient dans une question : où investir ? Pour y répondre, Evans oppose le Core Domain aux sous-domaines génériques, les rôles de support s'intercalant entre les deux ; Vaughn Vernon formalise ensuite la taxonomie à trois catégories nommées. Les reconnaître est la décision la plus rentable de toute la conception.

TypeRôleDifférenciant ?Stratégie
Core DomainCœur de métierOui, avantage concurrentielMeilleurs développeurs, modélisation la plus soignée
Supporting SubdomainSupport nécessaireNon, mais spécifiqueDéveloppement sur mesure, investissement modéré
Generic SubdomainProblème déjà résoluNon, ni spécifiqueAcheter, réutiliser, sous-traiter

Core Domain : là où l'on excelle

Le cœur de métier (Core Domain) rassemble les parties du modèle distinctives et centrales à la raison d'être de l'application. C'est là que se crée la valeur, l'avantage concurrentiel, l'actif que l'entreprise possède et que ses concurrents n'ont pas. Comme une organisation ne peut exceller en tout, le Core Domain marque l'endroit précis où elle doit exceller.

La conséquence est radicale : appliquez vos meilleurs talents au Core Domain, et recrutez en conséquence. Cherchez-y un modèle profond et une conception souple, investissez-y le plus libéralement. Tout investissement ailleurs ne se justifie que par la façon dont il soutient ce cœur distillé.

À retenir

Le syndrome classique des projets qui échouent : les développeurs les plus chevronnés gravitent vers l'infrastructure technique ou les problèmes « propres » qui ne demandent aucune connaissance métier (CV plus brillant, compétences transférables). Pendant ce temps, le module qui différencie réellement le produit est confié à des débutants. Evans a vu un développeur brillant peaufiner un élégant système de commentaires… périphérique, tandis qu'un incompétent transformait le module « prêt syndiqué » — le vrai cœur — en un magma dont le projet a failli ne jamais se relever.

Prenons une boutique en ligne dont l'argument de vente est la pertinence des recommandations et la finesse de la tarification dynamique. C'est là le cœur : il mérite un modèle riche, expressif, déclaratif.

// ✅ Core Domain : tarification dynamique, modèle riche.
// On exprime des RÈGLES métier, pas des if/else épars.
class MoteurTarification {
  constructor(private readonly regles: RegleTarifaire[]) {}

  prixFinal(article: Article, client: Client): Prix {
    return this.regles
      .filter((r) => r.sApplique(article, client))
      .reduce((prix, r) => r.appliquer(prix, client), article.prixBase);
  }
}

interface RegleTarifaire {
  sApplique(article: Article, client: Client): boolean;
  appliquer(prix: Prix, client: Client): Prix;
}

Generic Subdomain : ne pas réinventer la roue

Certaines parties du modèle ajoutent de la complexité sans capturer de connaissance spécialisée. Un organigramme d'entreprise, un modèle comptable standard, la gestion des fuseaux horaires, l'envoi de notifications, l'authentification : autant de concepts dont beaucoup d'entreprises ont besoin, sous une forme presque identique. Ce sont les sous-domaines génériques (Generic Subdomains).

La règle est claire : factorisez-les dans des modèles généraux, sans aucune trace de vos spécialités, et placez-les dans des packages séparés. Puis envisagez une solution sur étagère, un modèle publié, ou la sous-traitance. Surtout, n'y envoyez pas vos développeurs du cœur : ils n'y gagneraient aucune connaissance métier.

Attention

Evans raconte avoir vu, à deux reprises, les meilleurs développeurs d'un projet passer des semaines à redessiner la gestion des dates et fuseaux horaires. Ces composants doivent fonctionner, mais ils ne sont pas le cœur conceptuel. Le danger n'est pas le code généré : c'est l'attention détournée du Core Domain, et la connaissance métier non accumulée.

L'envoi d'e-mails transactionnels d'une boutique est l'archétype du sous-domaine générique. Aucun avantage concurrentiel à le coder soi-même.

// ✅ Generic Subdomain : on dépend d'une abstraction,
// l'implémentation est un service acheté (SendGrid, SES…).
interface ServiceNotification {
  envoyer(message: Notification): Promise<void>;
}

// Détail d'infrastructure, hors du domaine métier.
class NotificationParEmail implements ServiceNotification {
  constructor(private readonly fournisseur: FournisseurEmail) {}
  async envoyer(message: Notification): Promise<void> {
    await this.fournisseur.send(message.destinataire, message.corps);
  }
}

Astuce

« Générique » ne veut pas dire « réutilisable ». Si vous implémentez vous-même un sous-domaine générique, ne vous souciez pas de sa réutilisabilité : cela irait à l'encontre de la distillation. Modélisez et codez seulement la partie dont vous avez besoin — mais restez strict sur le périmètre générique. Introduire un concept propre à votre métier dans ce module le contaminerait : ce concept appartient au cœur ou à un sous-domaine spécialisé, bien plus précieux.

Supporting Subdomain : nécessaire, non différenciant

Entre les deux se trouve le sous-domaine de support (Supporting Subdomain) : une situation qui exige du développement sur mesure (aucune solution sur étagère n'existe), mais qui ne distingue pas l'entreprise. Le cœur ne peut pas réussir sans lui, et pourtant il ne mérite pas le même niveau d'investissement.

Pour une boutique en ligne, le catalogue produits est typiquement un sous-domaine de support : il faut le construire à votre image (catégories, attributs, variantes propres à votre offre), mais ce n'est pas lui qui fait préférer votre boutique à une autre. On peut envisager de le sous-traiter — précisément pour éviter de le prendre pour un élément stratégique et d'y surinvestir.

// ✅ Supporting Subdomain : sur mesure mais sobre.
// Modèle correct, sans la profondeur réservée au cœur.
class Catalogue {
  constructor(private readonly produits: Map<Sku, FicheProduit>) {}

  rechercher(critere: CritereRecherche): FicheProduit[] {
    return [...this.produits.values()].filter((p) =>
      p.correspond(critere),
    );
  }
}

Un cœur est relatif au point de vue

Le Core Domain dépend de votre point de vue. Beaucoup d'applications ont besoin d'un modèle générique de la monnaie (devises, taux de change). Mais une application de trading de devises considérera la monnaie comme une partie de son cœur, avec un modèle bien plus élaboré. Le Core Domain de l'un est le composant générique de l'autre. L'identification du cœur évolue d'ailleurs au fil des itérations : ce qui paraissait central au départ se révèle parfois n'avoir qu'un rôle de support.

La distillation : rendre le cœur visible

Reconnaître les trois catégories est la première moitié du travail. La seconde consiste à distiller le modèle : réduire le cœur à son essence et le rendre explicite, pour que toute l'équipe le voie et le partage. Evans propose une gamme de techniques, de la plus légère à la plus engageante. Inutile de toutes les appliquer : chacune demande un engagement croissant, mais « une lame s'affûte d'autant mieux qu'on la passe finement ».

Domain Vision Statement : la boussole

La technique la moins coûteuse est l'énoncé de vision du domaine (Domain Vision Statement) : une description courte (une page environ) du Core Domain et de la valeur qu'il apporte — sa proposition de valeur. On y ignore délibérément tout ce qui ne distingue pas ce modèle des autres. Écrit tôt, révisé au fil des découvertes, il sert de boussole partagée pour orienter l'allocation des ressources et les choix de modélisation.

Boutique en ligne — Domain Vision Statement (extrait)

Le modèle représente la pertinence de l'offre faite à chaque client :
recommandations personnalisées et tarification dynamique selon le profil,
l'historique d'achat et les stocks. Il doit maximiser la valeur du panier
tout en respectant les marges et les politiques promotionnelles.

(N'EST PAS dans la vision : l'envoi d'e-mails, l'authentification,
le rendu des pages — importants, mais non différenciants.)

Note

Distinguez bien ce qui relève de la vision du domaine de ce qui n'en relève pas. « Le modèle équilibre les priorités du client et les stratégies de l'entreprise » appartient à la vision. « L'interface sera accessible via le web avec une confirmation en moins de 5 secondes » est important, mais n'a rien à y faire.

Highlighted Core : le cœur surligné

Une vision en termes généraux laisse chacun interpréter à sa façon quels éléments précis composent le cœur. Sans une communication exceptionnelle, chaque développeur doit, en permanence, filtrer mentalement le grand modèle pour retrouver le cœur — un effort épuisant, jamais cohérent d'une personne à l'autre ni d'un jour à l'autre. Le cœur surligné (Highlighted Core) résout ce problème par des moyens légers, sans toucher au code :

  • Un document de distillation : 3 à 7 pages aérées décrivant le cœur et les interactions principales entre ses éléments. Pas un document de conception complet — un point d'entrée minimaliste qui renvoie ensuite vers le code.
  • Un marquage du cœur (Flagged Core) dans le dépôt du modèle lui-même, pour qu'un développeur voie sans effort ce qui est dans le cœur ou hors du cœur.

En TypeScript, ce marquage peut prendre la forme d'une convention de packages et d'annotations partagées.

// ✅ Le cœur est « surligné » par une convention explicite.
/** @coreDomain Tarification dynamique — voir Vision §1. */
export class MoteurTarification {
  /* ... */
}

// Arborescence parlante : les modules racontent l'histoire.
//   src/tarification/   <-- @coreDomain
//   src/recommandation/ <-- @coreDomain
//   src/catalogue/      <-- supporting
//   src/notification/   <-- generic
//   src/auth/           <-- generic

Astuce

Le document de distillation est aussi un outil de processus. Si une paire modifie du code sans toucher au document — parce qu'elle travaille hors du cœur ou ne change que des détails —, elle garde toute son autonomie. Si son changement oblige à mettre le document à jour (cœur fondamentalement modifié, frontière redessinée), une concertation d'équipe s'impose. Les changements du cœur doivent avoir un grand effet ; les autres, non.

Cohesive Mechanisms : isoler le « comment »

Parfois, un calcul atteint une complexité telle qu'il noie la conception : le quoi conceptuel est englouti sous le comment mécanique. La parade est le mécanisme cohésif (Cohesive Mechanism) : extraire le calcul intriqué dans un cadre léger et séparé, exposé par une interface révélatrice d'intention (Intention-Revealing Interface). Le modèle exprime alors le problème de façon déclarative et délègue l'intrication de la solution.

Evans donne l'exemple d'un organigramme dont les questions (« Qui, dans cette chaîne hiérarchique, peut approuver ceci ? ») se ramènent au parcours d'un graphe — un formalisme étudié, documenté, que l'on isole dans son propre cadre.

// ❌ Avant : la logique de parcours pollue le modèle métier.
class Organisation {
  approbateurPour(employe: Employe, montant: Money): Employe {
    let courant: Employe | null = employe;
    while (courant) {
      if (courant.plafondApprobation.estSuperieurA(montant))
        return courant;
      courant = courant.responsable; // parcours mêlé au métier
    }
    throw new AucunApprobateur();
  }
}

// ✅ Après : le mécanisme de graphe est isolé, le modèle
// se contente de DÉCLARER ses nœuds et ses arêtes.
class Organisation {
  constructor(private readonly graphe: Graphe<Employe>) {}

  approbateurPour(employe: Employe, montant: Money): Employe {
    return this.graphe.remonterJusqua(employe, (e) =>
      e.plafondApprobation.estSuperieurA(montant),
    );
  }
}

Note

Sous-domaine générique ou mécanisme cohésif ? Tous deux délestent le cœur, mais la nature diffère : un sous-domaine générique exprime un aspect du métier (en plus simple, moins central) ; un mécanisme cohésif ne représente rien du métier — il résout un problème de calcul. Formule d'Evans : un modèle expressif propose, un mécanisme cohésif dispose.

Segregated Core : repackager le cœur

Quand le cœur reste entremêlé au reste malgré l'extraction des génériques, la technique du cœur ségrégué (Segregated Core) s'attaque directement au problème : refactoriser le modèle pour séparer les concepts du cœur de leurs comparses, renforcer sa cohésion et réduire son couplage — quitte à briser des packages bien cohésifs au passage. Dans l'exemple maritime d'Evans, on extrait un package Delivery <<core>> (Cargo, Itinéraire, Spécification de route, Accord client) en reléguant le reste à des rôles de support — deux packages spécifiques (Customer, Logistics) et un sous-domaine générique (Money <<generic>>). Le gain : le cœur devient enfin manipulable seul.

Piège courant

Le Segregated Core est une décision d'équipe : tout le monde doit adopter la même définition du cœur, sans la figer (le cœur évolue lui aussi). C'est un travail conséquent qui peut toucher du code partout. Réservez-le aux grands contextes critiques dont le cœur est noyé sous trop de capacités de support.

Décider où mettre l'énergie

La distillation débouche sur des décisions faciles. Concentrez l'effort sur le cœur, gardez le reste aussi générique que possible. Si une part de votre conception doit rester secrète comme avantage concurrentiel, c'est le Core Domain — inutile de gaspiller de l'énergie à dissimuler le reste. Et lorsqu'il faut trancher, faute de temps, entre deux refactorisations souhaitables, on choisit d'abord celle qui affecte le plus le cœur.

Le même principe guide la gestion du risque. La tentation agile est de bâtir d'emblée un système de bout en bout sur un sous-domaine de support — plus facile à analyser. C'est un piège : un premier jet ne réduit le risque que s'il est une version embryonnaire des parties difficiles. Sauf équipe aguerrie sur un domaine familier, le premier jet doit porter sur une partie du Core Domain, si simple soit-elle, car c'est lui le vrai risque : souvent plus ardu que prévu, et sans lui le projet ne peut réussir.

À retenir

Choisir ses cibles de refactorisation. Plutôt que « tout refactoriser » ou « seulement là où ça fait mal », Evans propose : si une douleur trouve sa racine dans le cœur ou dans sa relation au support, corrigez-la d'abord. Et quand vous refactorisez librement, améliorez en priorité la factorisation du cœur, sa ségrégation, et la purification des supports vers le générique. C'est le meilleur retour sur investissement.

Un prolongement moderne

L'analyse de Vaughn Vernon (DDD Distilled, 2016) ajoute un usage qu'Evans n'avait pas formalisé : les sous-domaines comme outil de cartographie des systèmes existants. Un Big Ball of Mud — un monolithe legacy où plusieurs modèles s'enchevêtrent — peut être raisonné en y imaginant des sous-domaines logiques (frontières en pointillés). Cela rend le legacy moins monolithique pour la pensée, même s'il ne l'est pas dans le code, et aide à décider avec quels sous-domaines votre nouveau cœur devra s'intégrer. Cette lecture rejoint la cartographie de contextes (Context Mapping), elle aussi postérieure aux formulations initiales du livre de 2003.

À retenir

  • Découpez le domaine en sous-domaines alignés idéalement un pour un avec les contextes délimités : le sous-domaine est l'espace du problème, le contexte délimité l'espace de la solution.
  • Trois catégories, trois stratégies : Core Domain (différenciant → meilleurs talents, modèle profond), Supporting (sur mesure mais non différenciant → effort modéré), Generic (problème résolu → acheter, réutiliser, sous-traiter).
  • N'envoyez pas vos meilleurs développeurs sur le générique : ils n'y accumulent aucune connaissance métier, et le cœur en pâtit (la leçon des fuseaux horaires).
  • Distiller, c'est rendre le cœur visible : du plus léger (Domain Vision Statement, Highlighted Core) au plus engageant (Cohesive Mechanism, Segregated Core).
  • « Générique » ≠ « réutilisable » : codez seulement ce dont vous avez besoin, mais restez strict sur le périmètre pour ne pas contaminer le générique avec vos spécialités.
  • L'arbitrage est stratégique : en cas de choix, privilégiez ce qui touche le cœur ; commencez le projet et la maîtrise du risque par le Core Domain, jamais par un support.