Le Domain-Driven Design (DDD), proposé par Eric Evans, est une approche de conception logicielle qui met * le domaine métier au centre de l’architecture*.
Dans une approche classique, l’architecture d’une application est souvent orientée autour de la persistance des données (modèle basé sur la base de données), ce qui entraîne une forte dépendance entre les couches et limite l’évolutivité.
En DDD, l’architecture est pensée autour des règles métier, avec une séparation nette entre le cœur métier et les couches externes.
- Permet d’avoir un code aligné avec la réalité métier, en reflétant directement les processus métiers dans le code.
- Sépare clairement la logique métier de l’infrastructure, réduisant ainsi le couplage et rendant l’application plus évolutive.
- Facilite l’évolution du projet en structurant le domaine en plusieurs sous-domaines indépendants.
- Permet une communication fluide entre développeurs et experts métier, grâce à un langage commun appelé * Ubiquitous Language*.
Un Bounded Context définit une limite claire autour d’un sous-domaine dans un système complexe.
Chaque contexte possède son propre modèle métier et ses propres règles, ce qui permet d’éviter les conflits
terminologiques et les dépendances inutiles.
Pourquoi segmenter une application en plusieurs Bounded Contexts ?
- Réduction de la complexité : chaque partie du domaine est indépendante.
- Meilleure organisation : chaque équipe peut travailler sur un contexte spécifique.
- Facilité d’évolution : un contexte peut être modifié sans impacter les autres.
- Contexte "Gestion des Produits" : création, mise à jour des articles, catégories.
- Contexte "Gestion des Commandes" : validation d’une commande, suivi de livraison.
- Contexte "Facturation" : génération de factures, paiements.
- Contexte "Gestion des Clients" : gestion des comptes utilisateurs, fidélité.
Chaque contexte communique avec les autres via des APIs ou des événements.
| Concept | Définition | Exemples |
|---|---|---|
| Entité | Un objet avec un identifiant unique et un cycle de vie long | Utilisateur, Commande, Produit |
| Value Object | Un objet sans identifiant, immuable | Adresse, Devise, Coordonnées GPS |
- Évite les effets de bord : ils sont immuables, donc pas de modifications involontaires.
- Réduit la complexité : pas besoin de gérer un identifiant unique.
- Améliore la réutilisabilité : ils peuvent être utilisés dans plusieurs entités.
Une adresse peut être représentée par un Value Object, car elle ne nécessite pas d’identifiant unique et ne change pas après sa création.
Un Aggregate est un groupe cohérent d’Entités et de Value Objects, qui garantit la cohérence des données.
L’élément principal d’un Aggregate est l’Aggregate Root, qui contrôle les accès aux autres objets de l’agrégat.
Seule l’Aggregate Root peut être modifiée directement.
L’aggregate Commande peut contenir :
- Entités :
LigneCommande,Produit - Value Objects :
Adresse de livraison,Prix
- Assure la cohérence métier : évite les modifications illogiques sur des objets liés.
- Facilite la gestion des transactions : toute modification passe par la racine.
Règle clé :
Un aggregate ne doit pas être trop grand pour éviter des mises à jour coûteuses en performance.
Un Domain Event est un événement métier qui représente un changement important dans le domaine.
Au lieu d’appeler directement des méthodes sur d’autres objets, on publie un événement que d’autres composants peuvent
écouter.
Par exemple l'envoi d'un email de confirmation après l'inscription d'un utilisateur
class UserService {
constructor(private eventDispatcher: EventDispatcher) {}
async register(email: string): Promise<void> {
// ...
this.eventDispatcher.dispatch(new UserRegisteredEvent(user.email.value));
}
}
class UserRegisteredEvent {
constructor(public readonly email: string) {}
}
class SendWelcomeEmailHandler {
public handle(event: UserRegisteredEvent) {
// Gestion de l'envoi de l'email
}
}- Évite un couplage fort entre les modules.
- Facilite la communication entre microservices dans une architecture distribuée.
- Permet d’implémenter CQRS (Command Query Responsibility Segregation) plus efficacement.
- L’utilisateur passe une commande.
- L’événement "Commande Validée" est publié.
- Le service de paiement est notifié et déclenche le paiement.
- Le service de logistique reçoit l’événement et prépare l’expédition.
Chaque service réagit indépendamment à l’événement, ce qui améliore la scalabilité.
Le CQRS (Command Query Responsibility Segregation) est un modèle où l’on sépare la lecture et l’écriture des données.
- Améliore les performances : on optimise différemment les requêtes et les mises à jour.
- Simplifie la scalabilité : les lectures et écritures peuvent être déployées séparément.
- Facilite l’intégration d’Event Sourcing.
L’architecture hexagonale s’intègre parfaitement avec le DDD, car elle impose :
- Une séparation stricte entre le domaine et l’infrastructure.
- Un faible couplage entre les composants.
- Une organisation centrée sur le métier, avec un domaine autonome.
Le Domain-Driven Design est une approche essentielle pour construire des applications robustes et évolutives. DDD est une approche pragmatique, pas une check-list rigide : il faut adapter les concepts en fonction de la complexité du domaine et des besoins du projet.
Notre projet de blog ici est simple donc il n'est pas forcément très utile d'implémenter CQRS ou une gestion complète des Domain Events dès le debut. Si le blog venait à évoluer (gestion avancée des commentaire, rôles d'utilisateur, notifications, etc.), il serait alors plus pertinent de mettre en place ces pratiques.
Ce qui est essentiel dans DDD :
- Définir des Bounded Contexts pour séparer les responsabilités
- Utiliser des Aggregates et des Root Aggregates pour structurer les entités
- Appliquer les Value Objects pour encapsuler des concepts métier réutilisables
- Assurer l'intégrité des règles métier au sein des Aggregates
Ce qui est optionnel et dépend du projet :
- CQRS (utile si lecture/écriture ont des exigences différentes)
- Domain Events (si tu veux découpler les modules)