03 / 04 Frontières et contrats entre services : bounded contexts, shared kernel et published language
  1. ← Modèles d'architecture de services : intégration, autonomie et fédération
  2. 01 Monolithe intégré et services autonomes : deux réponses opposées au partage de concepts
  3. 02 Le modèle fédéré : plan de contrôle, projections locales et réconciliation
  4. 03 Frontières et contrats entre services : bounded contexts, shared kernel et published language
  5. 04 Choisir son modèle : grille de décision, signaux d’alerte et trajectoires de migration
Modèles d'architecture de services : intégration, autonomie et fédération · 03 / 04

Frontières et contrats entre services : bounded contexts, shared kernel et published language

Pourquoi « client » n'a pas le même sens dans deux services, pourquoi la bibliothèque de types partagée est un piège, et quels patterns du Domain-Driven Design organisent sainement les relations entre services.

Les deux premiers chapitres ont décrit comment organiser les concepts transverses. Ce chapitre explique pourquoi le modèle fédéré est le bon, avec les outils conceptuels du Domain-Driven Design. Car le DDD a formalisé, il y a vingt ans, exactement le problème que nous manipulons : que se passe-t-il quand le même mot désigne des choses différentes selon l’endroit où l’on se trouve ?

À la fin de ce chapitre, tu sauras lire un système multi-services comme une carte de bounded contexts, situer les six grands patterns de relation entre contextes, reconnaître l’anti-pattern du noyau partagé et son symptôme (le monolithe distribué), et formuler les conventions minimales qu’un écosystème doit publier pour rester cohérent sans se coupler.

Même mot, sens différents : le bounded context

Le Domain-Driven Design part d’une observation linguistique : dans toute organisation, le sens d’un mot dépend du contexte. « Client » ne désigne pas la même chose pour le service commercial (un prospect signé), la comptabilité (un compte à facturer) et le support (un détenteur de contrat). Forcer une définition unique produit un modèle monstrueux qui ne satisfait personne.

Le DDD nomme bounded context la frontière à l’intérieur de laquelle un modèle et son vocabulaire sont cohérents et univoques. La leçon centrale : ne pas unifier les modèles, unifier la carte. On accepte plusieurs définitions de « client », une par contexte, et l’on rend explicites les relations entre contextes.

Vérifie-le toi-même sur notre suite de commerce. Parcours les trois contextes ci-dessous et observe : aucun attribut ne coïncide, à part l’identifiant. Ta mission : trouve ce qui arriverait si l’on fusionnait ces trois définitions en une seule entité « Client » universelle.

Le même mot, vu depuis trois contextes

Client = un visiteur avec des préférences

  • client_ididentifiant local
  • langue, devise
  • historique de navigation
  • listes de souhaits

Ici, le client est un profil de personnalisation. Aucune notion de paiement ni d’obligation légale : ce contexte n’en a pas besoin, et son modèle reste simple précisément parce qu’il ignore le reste.

La fusion produirait une entité à trente attributs dont chaque service n’utiliserait qu’un cinquième, soumise aux contraintes réglementaires du paiement même pour afficher une liste de souhaits, et modifiée par trois équipes aux agendas incompatibles. C’est exactement le « modèle monstrueux » que le bounded context évite.

Même nom, même intuition, trois définitions. Elles partagent un lien (c’est « le même » client aux yeux de l’utilisateur), mais ni leurs attributs, ni leur cycle de vie, ni leurs invariants ne coïncident. Chaque service est un bounded context, et c’est très bien ainsi.

La carte des relations : six patterns à connaître

Une fois les contextes posés, le DDD nomme les relations possibles entre eux : c’est le context mapping. Six patterns couvrent l’essentiel du territoire :

PatternRelationQuand l’utiliser
PartnershipDeux équipes co-évoluent leurs contextes, succès liéDeux services au cœur du même flux critique
Customer-SupplierL’aval exprime ses besoins, l’amont planifie pour luiLe fournisseur accepte de servir son consommateur
ConformistL’aval adopte tel quel le modèle de l’amontL’amont ne négocie pas (service externe dominant)
Anticorruption LayerL’aval traduit le modèle de l’amont vers le sienProtéger son modèle d’un amont imposé ou legacy
Open Host ServiceL’amont publie une API stable ouverte à tousUn service consommé par de nombreux clients
Published LanguageUn format d’échange commun, publié et versionnéÉcosystème entier, interopérabilité large

Deux de ces patterns méritent qu’on s’y arrête, parce qu’ils sont les deux pôles de notre sujet : le shared kernel (que le tableau omet volontairement, on y vient) et le couple Open Host Service + Published Language, qui décrit exactement le contrat de fédération du chapitre 2 : chaque service publie une API stable (OHS) dont les conventions (références externes, sémantique minimale) constituent un langage publié (PL).

L’anticorruption layer mérite aussi un mot : c’est la couche de traduction qu’un contexte place à sa frontière pour convertir le modèle d’autrui vers le sien. Quand le service support consomme les événements du service paiement, il ne stocke pas « une transaction » : il traduit en « un motif de contact possible ». Le modèle étranger ne pénètre jamais ; il est traduit à la douane. C’est le mécanisme concret qui rend les bounded contexts étanches.

L’anti-pattern : le noyau partagé involontaire

Face à nos trois définitions de « client », la tentation du chapitre 1 revient sous une forme plus sournoise : « puisque les services sont séparés, partageons au moins les types ». Une bibliothèque commune, suite-core, qui définit la structure Client une fois pour toutes, et que chaque service importe.

Le DDD appelle ce pattern shared kernel : un fragment de modèle possédé conjointement par plusieurs contextes. Et il l’accompagne d’un avertissement que l’industrie a appris à ses dépens : le noyau partagé n’est viable qu’entre équipes très proches, coordonnées en continu, sur un périmètre minuscule. Partout ailleurs, il produit le pire des deux mondes :

  • Chaque évolution du type partagé exige de re-versionner et re-déployer tous les services qui l’importent : tu as recréé le couplage d’évolution du monolithe.
  • Mais sans les bénéfices du monolithe : pas de transactions communes, pas d’intégration native, et la complexité opérationnelle du distribué en prime.

Ce résultat porte un nom : le monolithe distribué. Des services techniquement séparés, logiquement soudés. On paie le prix des deux architectures et l’on ne touche les dividendes d’aucune.

Le shared kernel involontaire : un type possédé par personne, importé par tous

Note la nuance : publier une bibliothèque de types depuis un service, vers ses clients (le contrat du service de paiement, consommé par qui veut l’appeler) est sain : la dépendance suit le sens de l’appel et le fournisseur versionne son contrat. C’est l’Open Host Service. Ce qui est toxique, c’est le type possédé par personne et importé par tous.

Le pattern sain : le langage publié

Si l’on ne partage ni base de données (chapitre 1), ni types (ci-dessus), que reste-t-il ? La réponse du DDD s’appelle published language : un langage d’échange publié, stable, versionné, que chaque contexte traduit vers son modèle interne.

Les exemples canoniques sont les grands standards d’interopérabilité : les protocoles d’identité fédérée, les formats d’événements normalisés, les spécifications d’API ouvertes, les formats d’échange comptables ou bancaires. Aucun ne partage de types internes ; tous publient un format de frontière que chacun implémente chez soi.

Pour un écosystème fédéré, le langage publié prend une forme concrète et minimale, en trois conventions :

  1. Un vocabulaire et une sémantique minimale. Ce qu’est une « boutique » pour l’écosystème : une clé stable, un nom d’affichage, l’appartenance à une organisation. Rien de plus : chaque contexte enrichit localement.
  2. Un schéma d’identifiants normalisé. Le format des références externes (suite:org/{org}/shop/{key}…) : structuré pour le plan de contrôle qui les émet, opaque pour les services qui les stockent.
  3. Le contrat de fédération. Toute brique expose un upsert idempotent par référence externe sur ses entités topologiques, documenté dans une spécification d’API versionnée.

Une page de documentation suffit à fixer ces trois conventions. C’est l’investissement le plus rentable de toute l’architecture : il coûte une décision, et il épargne des années de couplage.

La carte complète

Tu disposes maintenant de la lecture complète des trois premiers chapitres :

QuestionRéponseOutil conceptuel
Qui possède la topologie ?Le plan de contrôleFédération (chap. 2)
Qui possède les ressources métier ?Chaque serviceBounded context
Que signifie « client » ?Une définition par contexteUbiquitous language local
Que partage-t-on entre services ?Identifiants + conventions, jamais les typesPublished language
Comment se protège-t-on d’un modèle étranger ?Traduction à la frontièreAnticorruption layer
Comment converge-t-on ?Réconciliation idempotenteÉtat désiré déclaratif
Que ne fait-on jamais ?La bibliothèque de types importée par tousAnti-pattern shared kernel

Cette grille s’applique bien au-delà des suites logicielles commerciales : plateformes de développement, systèmes industriels, écosystèmes de microservices internes d’une grande organisation, fusions de systèmes d’information après acquisition. Partout, la même tension entre intégration et autonomie, et partout la même issue : des contextes souverains, un langage publié, une convergence déclarative.

Vérifie ta compréhension

Quiz
  1. 1. Que désigne un bounded context en DDD ?

  2. 2. Quel est le rôle d'une anticorruption layer ?

  3. 3. Pourquoi la bibliothèque de types partagée entre tous les services est-elle un anti-pattern ?

  4. 4. Quel duo de patterns DDD décrit le contrat de fédération du chapitre 2 ?

Ce qu’il faut retenir

  • Chaque service est un bounded context : « client » y a une définition locale légitime. On n’unifie pas les modèles, on unifie la carte.
  • Le context mapping nomme les relations possibles : partnership, customer-supplier, conformist, anticorruption layer, open host service, published language.
  • Le shared kernel involontaire (la bibliothèque de types importée par tous) produit le monolithe distribué : couplage du monolithe, complexité du distribué, bénéfices d’aucun.
  • Le published language est le pattern sain : vocabulaire minimal, schéma d’identifiants normalisé, contrat de fédération, le tout versionné et documenté.
  • Entre contextes circulent des identifiants et des messages, jamais des structures internes ; l’anticorruption layer traduit à la frontière.
  • Reste la question pratique : face à un système réel, comment choisir son modèle, et comment migrer de l’un à l’autre ? C’est l’objet du dernier chapitre.