Conception pilotée par domaine (DDD) dans les microservices
Lorsque les organisations passent d’une architecture monolithique aux microservices, elles sont confrontées à une question critique aux enjeux élevés : Comment tracer les limites de nos services ?
En théorie, les microservices devraient être des unités lâches et découplées qui peuvent être développées, déployées et mises à l’échelle indépendamment. Dans la pratique, cependant, de nombreuses équipes finissent par créer un monolithe distribué : un système dans lequel les services sont si étroitement couplés qu’un seul changement commercial nécessite de modifier et de déployer plusieurs services simultanément, ce qui aggrave la latence du réseau et les blocages de déploiement.
Pour éviter cet écueil, les architectes logiciels se tournent vers la Domain-Driven Design (DDD). Introduite pour la première fois par Eric Evans en 2003, DDD est une méthodologie de développement logiciel qui aligne les structures de code avec les domaines métier complexes qu’elles représentent.
Dans cet article, nous explorerons comment DDD fournit les plans stratégiques et tactiques pour concevoir des microservices propres, découplés et hautement maintenables.
1. Le plan stratégique : tracer les limites des services
DDD est divisé en deux phases principales : Conception stratégique et Conception tactique. La conception stratégique consiste à modéliser les contextes commerciaux et à définir les limites des services. Il fournit la vue « macro » de l’architecture.
A. Langue omniprésente
Au sein d’une entreprise, différents départements utilisent les mêmes mots pour désigner des choses complètement différentes. Par exemple :
- Pour l’équipe commerciale, un « utilisateur » est un prospect ou un client potentiel.
- Pour l’équipe de sécurité, un « utilisateur » est un ensemble d’identifiants de connexion.
- Pour l’équipe d’expédition, un « utilisateur » est une adresse physique de destinataire.
Essayer de créer un modèle de base de données unique et unifié pour un « utilisateur » qui satisfasse tout le monde aboutit à une base de code massive et enchevêtrée. DDD résout ce problème en établissant un langage omniprésent : un vocabulaire partagé défini et utilisé à la fois par les experts du domaine métier et les développeurs dans un cadre spécifique.
B. Contextes délimités
Un Contexte limité est la limite explicite à l’intérieur de laquelle un modèle de domaine s’applique. À l’intérieur de la frontière, tous les termes de la langue omniprésente ont une signification unique et sans ambiguïté.
Au lieu d’un seul modèle « Utilisateur », nous définissons des modèles distincts dans leurs contextes délimités respectifs :
- Dans le Contexte de commande, nous avons un modèle
Ordercontenant les coordonnées du client. - Dans le Contexte d’identité, nous avons un modèle
Credentials. - Dans le Contexte d’expédition, nous avons un modèle
DeliveryAddress.
La règle d’or des microservices : Un contexte délimité correspond directement à un microservice.
En divisant le système en contextes délimités, nous garantissons que les microservices sont faiblement couplés et représentent des capacités commerciales distinctes.
C. Cartographie du contexte : comment les services communiquent
Les contextes délimités n’existent pas de manière isolée ; ils doivent interagir. Une Context Map définit les relations et les mécanismes de traduction entre les contextes. Les modèles clés incluent :
- Noyau partagé : deux contextes partagent un petit sous-ensemble du modèle de domaine et de la base de données (généralement déconseillé dans les microservices en raison du couplage).
- Client-Fournisseur : Un contexte (fournisseur) doit fournir des données à un autre (client). Le fournisseur doit coordonner les calendriers de livraison avec le client.
- Couche anti-corruption (ACL) : une couche de traduction qui traduit les données entrantes d’un système externe dans le modèle de domaine interne du client, empêchant ainsi les modèles externes de polluer l’architecture interne.
2. Le plan tactique : structurer la base de code du microservice
Une fois les limites du service définies à l’aide d’une conception stratégique, Tactical Design fournit un ensemble de modèles de conception pour structurer le code au sein d’un seul microservice.
A. Entités et objets de valeur
Au sein d’un microservice, les modèles sont divisés en deux catégories :
- Entités : objets qui ont une identité unique qui persiste dans le temps, même si leurs attributs changent. Les exemples incluent
OrderouProduct. - Objets de valeur : objets qui n’ont pas d’identité unique et sont entièrement définis par leurs attributs. Ils sont immuables. Les exemples incluent
ShippingAddressouProductDimensions. Si deux objets de valeur ont les mêmes attributs, ils sont considérés comme égaux.
B. Agrégats et racines d’agrégats
Un agrégat est un cluster d’entités et d’objets de valeur associés qui sont traités comme une unité unique pour les modifications de données. Chaque agrégat possède une racine d’agrégat, qui est le seul point d’entrée par lequel les objets externes peuvent interagir avec l’agrégat. La racine garantit que tous les invariants métier (règles) sont appliqués.
Par exemple, dans le schéma ci-dessus :
- Dans le Service de commande, le
Orderest la racine agrégée. Il contientOrderItem(Entité) etShippingAddress(Value Object). Les services externes ne peuvent pas modifier directementOrderItem; ils doivent appeler une méthode sur la racineOrder(par exemple,order.AddItem()), qui valide que la commande n’est pas déjà expédiée.
C. Événements de domaine
Un événement de domaine est un événement qui s’est produit dans le domaine et qui intéresse les experts métier. Il est utilisé pour communiquer les modifications dans les contextes délimités de manière asynchrone.
Lorsqu’un agrégat exécute une commande, il publie un événement de domaine (par exemple, OrderCreated). D’autres services écoutent cet événement et mettent à jour leur état en conséquence, garantissant ainsi une cohérence éventuelle sans dépendances API synchrones.
3. Un exemple concret : service de commande vs service d’inventaire
Examinons une implémentation simplifiée du DDD tactique dans Go pour le Order Service, montrant comment la racine agrégée coordonne les règles métier.
package domain
import (
"errors"
"time"
)
// Value Object (Immutable, identity-less)
type ShippingAddress struct {
Street string
City string
ZipCode string
}
// Entity (Has identity, mutable)
type OrderItem struct {
ProductID string
Quantity int
Price float64
}
// Aggregate Root (Enforces transactional boundaries)
type Order struct {
ID string
Items []OrderItem
Address ShippingAddress
Status string
CreatedAt time.Time
}
// NewOrder creates a new Order Aggregate Root
func NewOrder(id string, address ShippingAddress) *Order {
return &Order{
ID: id,
Items: []OrderItem{},
Address: address,
Status: "PENDING",
CreatedAt: time.Now(),
}
}
// AddItem enforces business rules before mutating state
func (o *Order) AddItem(productID string, qty int, price float64) error {
if o.Status != "PENDING" {
return errors.New("cannot add items to a finalized or cancelled order")
}
if qty <= 0 {
return errors.New("quantity must be greater than zero")
}
o.Items = append(o.Items, OrderItem{
ProductID: productID,
Quantity: qty,
Price: price,
})
return nil
}
Lorsque la commande est enregistrée avec succès, le service publie un événement sur un courtier de messages :
{
"event_id": "evt_98231",
"event_type": "OrderCreated",
"timestamp": "2026-06-26T00:15:00Z",
"payload": {
"order_id": "ord_5521",
"items": [
{ "product_id": "prod_88", "quantity": 2 }
]
}
}
Le Inventory Service écoute cet événement, met à jour son entité StockLevel locale et termine le flux de manière asynchrone.
4. DDD stratégique ou tactique dans les microservices
| Phases/Aspects | DDD stratégique | DDD tactique |
|---|---|---|
| Portée | Architecture système globale (macro) | À l’intérieur d’une base de code de service unique (micro) |
| Objectif principal | Tracez des limites de services claires et découplées | Modélisez une logique métier riche et appliquez des invariants |
| Concepts clés | Contextes délimités, langage omniprésent, carte contextuelle | Entités, objets de valeur, agrégats, événements de domaine |
| Public | Architectes, chefs de produits, développeurs | Développeurs de logiciels, réviseurs de code |
| Impact sur les microservices | Détermine le nombre et l’étendue des services | Détermine les transactions de base de données et la structure des répertoires |
Conclusion : commencez par le DDD stratégique
L’application de la conception pilotée par domaine aux microservices est un moyen puissant de garantir que votre architecture correspond à la structure de votre entreprise. Cependant, les équipes commettent souvent l’erreur de trop se concentrer sur des schémas tactiques (comme l’écriture d’interfaces de référentiel et d’objets de valeur) tout en ignorant la conception stratégique.
Si vous ne définissez pas correctement les limites, aucun code tactique propre n’empêchera votre système de se transformer en un monolithe distribué. Commencez toujours par une cartographie stratégique : définissez votre langage omniprésent, regroupez la logique dans des contextes délimités, cartographiez les flux de communication et laissez les limites de vos microservices émerger naturellement de ces définitions.