Domain-Driven Design en PHP : transformer le chaos en ordre
\n\nAvez-vous déjà eu l'impression que votre projet PHP prenait des proportions incontrôlables ? Au début, tout est simple : quelques contrôleurs, un modèle, quelques requêtes à la base de données. Mais six mois plus tard, vous vous noyez dans un code spaghetti où la logique métier est dispersée dans les services, et une « simple » modification de fonctionnalité prend une semaine.
\n\nÇa vous parle ? Bienvenue au club. Mais bonne nouvelle : le Domain-Driven Design (DDD) n'est pas une théorie abstraite issue des gros livres d'Eric Evans. C'est un outil concret qui vous aide à écrire du code PHP compréhensible à la fois par les métiers et les développeurs.
\n\nDans cet article, nous allons explorer le DDD en pratique. Sans bla-bla, avec des exemples concrets en PHP. Vous apprendrez à intégrer le DDD dans votre projet sans vous prendre la tête et sans tout réécrire. C'est parti !
\n\nQu'est-ce que le Domain-Driven Design et pourquoi en tant que développeur PHP ?
\n\nImaginez que vous développez une boutique en ligne. Vous avez des réductions, un panier, des commandes. La logique métier : « la réduction ne se cumule pas avec un code promo », « la commande ne peut pas être annulée après expédition ». Si vous stockez cette logique dans les contrôleurs ou la dispersez dans les modèles ORM, vous finirez par vous y perdre.
\n\nLe DDD est une approche qui place le domaine métier (votre domaine fonctionnel) au cœur du développement. Au lieu de penser en termes de tables et de requêtes SQL, vous pensez en entités, en valeurs et en événements.
\n\nC'est particulièrement pertinent pour PHP. Le langage est flexible, mais sans architecture claire, les projets se transforment rapidement en « legacy ». Le DDD fournit une structure qui reste lisible même après un an.
\n\nConception stratégique vs Conception tactique
\n\nLe DDD se divise en deux grandes parties :
\n- \n
- La conception stratégique — c'est la carte de votre projet. Vous identifiez des contextes délimités (Bounded Contexts), vous construisez un langage omniprésent (Ubiquitous Language). Par exemple, dans le contexte « Catalogue », un produit est simplement un ensemble de caractéristiques, tandis que dans le contexte « Commande », c'est une unité avec un prix et une quantité. \n
- La conception tactique — ce sont les briques de base : Entity, Value Object, Aggregate, Repository, Domain Service, Domain Event. Nous allons nous concentrer sur celles-ci. \n
Conception tactique en PHP : les briques du DDD
\n\nVoyons à quoi ressemblent les principaux patterns du DDD en PHP. Oubliez l'ActiveRecord de Laravel ou Symfony — ici, nous construisons un modèle indépendant du framework.
\n\n1. Entity (Entité)
\nUne entité est un objet qui possède un identifiant unique et qui évolue dans le temps. Par exemple, User ou Order. On compare les entités par leur ID, pas par leurs champs.
<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Domain\\Entity;\n\nuse App\\Domain\\ValueObject\\UserId;\nuse App\\Domain\\ValueObject\\Email;\n\nclass User\n{\n public function __construct(\n private UserId $id,\n private string $name,\n private Email $email\n ) {}\n\n public function getId(): UserId\n {\n return $this->id;\n }\n\n public function changeEmail(Email $newEmail): void\n {\n // La logique métier peut être ajoutée ici : vérifier si l'email n'est pas déjà pris\n $this->email = $newEmail;\n }\n\n public function getEmail(): Email\n {\n return $this->email;\n }\n}\n\n\n2. Value Object (Objet-valeur)
\nUn Value Object est un objet immuable qui se définit par ses attributs, et non par un identifiant. Deux Value Objects sont égaux si tous leurs champs sont identiques. Exemples classiques : Money, Email, Address.
<?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Domain\\ValueObject;\n\nclass Email\n{\n private string $value;\n\n public function __construct(string $value)\n {\n if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {\n throw new \\InvalidArgumentException('Adresse email invalide');\n }\n $thi