Pourquoi le DDD n'est pas une question de diagrammes ennuyeux, mais d'architecture vivante
\nLorsqu'un projet en C++ dépasse la taille d'une application « jouet », les développeurs sont souvent confrontés au chaos : la logique métier est dispersée dans les contrôleurs, la base de données dicte la structure des classes, et les débutants hésitent à toucher au code legacy. Domain-Driven Design (DDD) n'est pas un ensemble de règles abstraites, mais un outil pratique qui aide à maîtriser la complexité. Dans cet article, nous verrons comment implémenter le DDD en C++ à l'aide d'exemples de code concrets, et pourquoi cela sauvera votre projet du « gros tas de boue ».
\n\nQu'est-ce que le DDD et pourquoi un développeur C++ en a-t-il besoin ?
\nDomain-Driven Design (conception orientée domaine) est une approche où l'accent est mis sur le modèle du domaine métier, et non sur les détails techniques. En C++, c'est particulièrement pertinent : le langage offre des outils puissants (templates, RAII, pointeurs intelligents), mais sans une architecture appropriée, le projet se transforme en monolithe où la modification d'une fonctionnalité casse tout.
\nLe DDD propose deux niveaux :
\n- \n
- Modélisation stratégique : division du système en contextes (Bounded Contexts) et configuration de leurs interactions. \n
- Modélisation tactique : blocs de construction — Entity, Value Object, Aggregate, Repository, Domain Service. \n
Nous nous concentrerons sur la modélisation tactique, car c'est elle qui fournit des modèles concrets pour le code en C++.
\n\nArchitecture en couches : le fondement du DDD en C++
\nLe DDD implique une séparation claire en couches. Une structure typique pour un projet C++ ressemble à ceci :
\n- \n
- Couche Domaine (noyau) : entités, value objects, agrégats, services de domaine. Aucune dépendance vis-à-vis des frameworks ou des bibliothèques externes. \n
- Couche Application : cas d'utilisation (use cases), orchestration. Dépend uniquement du Domaine. \n
- Couche Infrastructure : implémentation des repositories (SQL, Redis, fichiers), travail avec les API externes. Implémente les interfaces du Domaine. \n
- Couche Présentation : interface utilisateur (console, API REST, GUI). \n
Ce n'est pas une règle stricte, mais une recommandation. L'essentiel est que les dépendances soient dirigées vers l'intérieur : les couches externes peuvent dépendre des couches internes, mais pas l'inverse.
\n\nExemple : comment ne pas faire
\n// ❌ Mauvais : logique métier mélangée avec du SQL\nclass UserController {\n void createUser(const std::string& name) {\n // Requête SQL directement ici\n db.execute("INSERT INTO users (name) VALUES (?)", name);\n // Logique de validation également ici\n if (name.empty()) throw std::runtime_error("Name required");\n }\n};\n\n\nExemple : style DDD
\n// ✅ Bon : séparation claire\nclass User : public Entity<User, int> {\n std::string name_;\npublic:\n explicit User(int id, std::string name) : Entity(id), name_(std::move(name)) {\n if (name_.empty()) throw std::invalid_argument("User name cannot be empty");\n }\n const std::string& name() const { return name_; }\n};\n\n// Interface du repository (Domaine)\nclass IUserRepository {\npublic:\n virtual ~IUserRepository() = default;\n virtual void save(const User& user) = 0;\n};\n\n// Implémentation (Infrastructure)\nclass SqlUserRepository : public IUserRepository {\n void save(const User& user) override {\n // Uniquement du SQL, sans logique métier\n }\n};\n\n\nBlocs de construction du DDD en C++
\n\nEntity (Entité)
\nC'est un objet avec un identifiant unique qui persiste dans le temps. En C++, il est pratique de créer une classe de base template :
\ntemplate<typename T, typename IdType>\nclass Entity {\n IdType id_;\npublic:\n explicit Entity(IdType id) : id_(std::move(id)) {}\n IdType id() const { return id_; }\n bool operator==(const Entity& other) const { return id_ == other.id_; }\n