Découvrir le Domain-Driven Design (DDD), Avantages, Bonnes Pratiques et Exemples en Java

Par KamangaJul 23, 20248 mins de lecture

Introduction au Domain-Driven Design (DDD)

Un code qui paraît compliqué sans qu'on sache pourquoi. La logique métier dispersée partout, mélangée à des détails techniques. Le Domain-Driven Design (DDD) s'attaque précisément à ce problème.

L'objectif est simple : aligner le code sur la réalité métier du projet. Chaque entité, chaque service, fait exactement ce qu'il est censé faire. Ajouter une fonctionnalité devient prévisible, et le risque de régression chute. Je l'ai constaté à plusieurs reprises en mission, notamment chez des clients bancaires et assurantiels.

Cet article explique ce qu'est réellement le DDD, en quoi il transforme la manière de construire un logiciel, et comment l'appliquer en Java avec des exemples concrets. Eric Evans a formalisé cette approche dans son ouvrage "Domain-Driven Design" (2003), qui reste la référence incontournable sur le sujet.


Pourquoi le Domain-Driven Design (DDD) est-il important dans la conception logicielle ?

Le DDD répond à un problème récurrent : dans un projet complexe, la logique métier finit souvent dispersée dans des considérations techniques. On manipule plus de "comment" que de "quoi" et "pourquoi". Les conséquences sont mécaniques : bugs, incompréhensions, code difficile à maintenir. J'ai observé ce schéma dans des dizaines d'équipes : les coûts de maintenance explosent précisément parce que la logique métier est diluée dans tout le code.

Le DDD inverse cette logique en plaçant le domaine métier au centre de la conception. Il structure le code autour des règles spécifiques au domaine, et pas autour des technologies utilisées. Un point systématiquement sous-estimé dans les grandes DSI où j'interviens : on modélise les tables de base de données avant le domaine, et on le regrette des mois plus tard.

Un exemple concret : La gestion d'une bibliothèque

Prenons un exemple : un système de gestion pour une bibliothèque. Vous avez des livres, des emprunts, des abonnés, et des règles métier comme "un abonné ne peut pas emprunter plus de 5 livres". Sans discipline, ces règles finissent enterrées entre les classes de gestion de base de données et les classes de validation. Plus personne ne sait où elles vivent vraiment.

Le DDD demande de modéliser ces concepts (livres, abonnés, emprunts) explicitement, avec des objets métier qui représentent ces réalités. Le code devient plus facile à lire, à maintenir et à faire évoluer. Les règles vivent là où elles doivent : dans le domaine métier. Concrètement, cela se traduit par des cycles de livraison plus courts et moins d'erreurs métier en production.


Vous voulez savoir modéliser un agrégat propre au lieu d'empiler les classes ?

Identifier où vivent vraiment les règles métier, découper un agrégat sans le faire exploser, nommer les concepts comme un expert du domaine : ça ne s'apprend pas en lisant un article, ça se travaille. En mentoring 1:1, je relis votre code avec vous et on remodélise ensemble une partie réelle de votre projet.

Vous repartez avec le réflexe de placer le domaine au centre, sur vos propres classes, pas sur un exemple de bibliothèque.

Les concepts clés du Domain-Driven Design (DDD)

Le DDD, ce n’est pas juste une nouvelle façon de coder. Avant de maîtriser ces approches, voici les concepts de base à comprendre, avec des explications simples et des exemples en Java pour illustrer le tout.

1. Entité

Une entité est un objet qui possède une identité unique qui le distingue des autres, même si ses attributs peuvent changer. Autrement dit, deux objets avec les mêmes données peuvent être considérés comme différents s'ils ont des identités distinctes.

Exemple en Java :

public class Abonne {
    private final String id;  // Identité unique
    private String nom;
    private String adresse;

    public Abonne(String id, String nom, String adresse) {
        this.id = id;
        this.nom = nom;
        this.adresse = adresse;
    }

    public String getId() {
        return id;
    }

    // getters et setters...
}

Astuce : Les entités doivent avoir une identité claire. Je vous recommande d'utiliser un identifiant unique (comme un UUID) pour chaque entité afin de les différencier facilement dans votre domaine.

2. Objet de valeur (Value Object)

Un objet de valeur est un objet qui n'a pas d'identité propre. Il est défini uniquement par ses attributs. Si deux objets ont les mêmes attributs, ils sont considérés comme identiques.

Exemple en Java :

public class Adresse {
    private String rue;
    private String ville;
    private String codePostal;

    public Adresse(String rue, String ville, String codePostal) {
        this.rue = rue;
        this.ville = ville;
        this.codePostal = codePostal;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Adresse adresse = (Adresse) o;
        return rue.equals(adresse.rue) && ville.equals(adresse.ville) && codePostal.equals(adresse.codePostal);
    }

    @Override
    public int hashCode() {
        return Objects.hash(rue, ville, codePostal);
    }

    // getters et setters...
}

Astuce : Les objets de valeur sont parfaits pour modéliser des concepts immuables, comme des adresses ou des coordonnées. Ils doivent être comparés par valeur, pas par référence.

3. Agrégat

Un agrégat est un groupe d’entités et d’objets de valeur qui forment un ensemble cohérent. Il a une racine d'agrégat, qui est l’entité principale, responsable de l’intégrité de l'ensemble.

Exemple en Java :

public class Emprunt {
    private final String id;
    private Abonne abonne;
    private List<Livre> livresEmpruntes = new ArrayList<>();

    public Emprunt(String id, Abonne abonne) {
        this.id = id;
        this.abonne = abonne;
    }

    public void ajouterLivre(Livre livre) {
        if (livresEmpruntes.size() >= 5) {
            throw new RuntimeException("Un abonné ne peut pas emprunter plus de 5 livres.");
        }
        livresEmpruntes.add(livre);
    }

    // getters et setters...
}

Alerte : Faites attention à ne pas créer des agrégats trop grands ou complexes. Ils doivent rester simples et gérables. Un agrégat ne doit pas avoir trop de dépendances. C'est exactement ce que j'ai observé dans une équipe que j'accompagnais chez un client dans le secteur bancaire : un agrégat "Contrat" regroupait une vingtaine d'entités et devenait impossible à tester.

4. Service de domaine

Un service de domaine encapsule la logique métier qui concerne plusieurs entités à la fois, mais ne possède pas de données propres.

Exemple en Java :

public class ServiceBibliotheque {
    public void enregistrerEmprunt(Emprunt emprunt, Livre livre) {
        emprunt.ajouterLivre(livre);
        // Logique supplémentaire, comme la mise à jour des stocks de livres...
    }
}

Astuce : Un service de domaine doit rester simple et concentré sur une tâche spécifique qui ne peut pas être facilement modélisée dans une entité ou un objet de valeur.

5. Référentiel (Repository)

Les référentiels sont chargés de la persistance des agrégats. Ils fournissent des méthodes pour récupérer et stocker des agrégats, tout en masquant les détails techniques de la persistance.

Exemple en Java :

public interface EmpruntRepository {
    Emprunt trouverParId(String id);
    void sauvegarder(Emprunt emprunt);
}

Alerte : Ne mettez pas de logique métier dans les référentiels. Ils doivent se limiter à la persistance des agrégats, sans interférer avec le domaine.


Les meilleures pratiques pour implémenter DDD

Quelques principes essentiels pour implémenter DDD de manière efficace dans vos projets Java.

1. Commencez par le domaine, pas par la technologie

Avant de vous plonger dans les frameworks ou les bases de données, assurez-vous de bien comprendre le domaine métier. Prenez le temps de discuter avec les experts du domaine pour modéliser les concepts clés de manière claire. L'atelier Event Storming est la méthode la plus efficace pour aligner développeurs et experts métier en quelques heures et identifier les Bounded Contexts.

2. Utilisez des schémas pour visualiser les agrégats

Les agrégats sont le cœur de votre modèle métier. Utilisez des schémas pour représenter les entités et objets de valeur qui les composent et leurs relations.

3. Adoptez une approche modulaire

Organisez votre application en modules correspondant aux sous-domaines. Cela vous permet de garder une structure claire et maintenable.

Exemple de structure en Java :



src/
└── main/
    └── java/
        └── com/
            └── ma_bibliotheque/
                ├── emprunts/
                ├── abonnés/
                └── livres/

4. Gardez vos services de domaine fins et spécifiques

Chaque service de domaine doit être responsable d’une seule tâche métier spécifique. Cela vous aidera à garder une architecture claire et flexible.

5. Testez vos domaines métiers de manière exhaustive

Les tests unitaires sont essentiels pour vous assurer que votre logique métier fonctionne correctement. Je vous recommande de tester chaque agrégat et service de manière isolée. Pour que ces tests soient rapides et sans infrastructure, le Dependency Inversion Principle est indispensable : le domaine ne doit pas dépendre directement des implémentations concrètes.

Exemple en Java avec JUnit :

public class EmpruntTest {
    
    @Test
    public void ne_peut_pas_emprunter_plus_de_5_livres() {
        Abonne abonne = new Abonne("123", "Jean Dupont", "1 Rue de Paris");
        Emprunt emprunt = new Emprunt("emprunt1", abonne);
        
        for (int i = 0; i < 5; i++) {
            emprunt.ajouterLivre(new Livre("Livre " + i));
        }
        
        assertThrows(RuntimeException.class, () -> {
            emprunt.ajouterLivre(new Livre("Livre 6"));
        });
    }
}

Astuce : Les tests doivent couvrir toutes les règles métier importantes. Ils vous aideront à repérer rapidement les régressions lorsque vous faites des modifications dans votre code.


Modéliser le domaine n'est qu'une des 100 pratiques qui séparent le code junior du code senior

Placer le domaine au centre, comme le montre cet article, c'est une seule pratique craft. Le Craft Bundle réunit les 100 pratiques que j'applique pour concevoir un code propre et maintenable, de la modélisation métier au découpage des agrégats jusqu'aux tests qui protègent vos règles. Ce sont les réflexes que l'IA ne vous apprendra jamais, parce qu'elle ne les a jamais vus tenir un domaine complexe en production.


FAQ sur le Domain-Driven Design (DDD)

1. Est-ce que le DDD est trop complexe pour les petits projets ?

Pas nécessairement. Le DDD est plus adapté aux projets complexes, mais certains de ses principes peuvent être utiles même dans de plus petits projets pour organiser votre code.

2. Dois-je utiliser tous les concepts du DDD dès le début ?

Non, vous pouvez les introduire progressivement en fonction de vos besoins.

3. Est-ce que je dois utiliser un framework spécifique pour appliquer DDD en Java ?

Non. Vous pouvez utiliser des bibliothèques comme Spring ou Hibernate tout en organisant votre code selon les principes de DDD.

4. Comment savoir si j’ai correctement modélisé mon domaine ?

Si un expert métier peut comprendre votre modèle sans difficultés, c’est un bon signe.

5. Comment éviter que les agrégats deviennent trop gros ?

Divisez-les si nécessaire. Un agrégat doit rester cohérent et ne contenir que les entités nécessaires à son bon fonctionnement.

6. Et si l’équipe métier change souvent d’avis ?

DDD permet de mieux gérer les changements car la logique métier est bien isolée dans le code, ce qui facilite les modifications.

7. Combien de temps faut-il pour maîtriser le DDD ?

Cela dépend de votre expérience, mais en commençant avec les bases, vous pouvez progresser rapidement.

Ressource gratuite : 10 signaux que votre équipe tech est en danger

10 signaux d'alarme pour identifier les problèmes systémiques cachés dans votre équipe avant qu'ils deviennent critiques. Auto-diagnostic inclus : 5 minutes pour savoir où vous en êtes.


Ecris par Kamanga

Expert IT avec 25 ans d'expérience en développement logiciel, diplômé EPITECH et MBA. Spécialisé en software craftsmanship, gestion du changement, stratégie, direction des systèmes d'information, coaching et certifié en agilité.