Éléments constitutifs du DDD : entités et objets-valeurs
Modélisez le domaine avec des entités riches et des objets-valeurs immuables.
Éléments constitutifs du DDD : entités et objets-valeurs est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.
Le cœur tactique
Les patrons tactiques de la conception pilotée par le domaine vous fournissent un vocabulaire pour modéliser les concepts métier dans le code. Les deux blocs fondamentaux sont les entités (objets définis par leur identité au fil du temps) et les objets-valeurs (objets définis par leurs attributs et traités comme immuables). Bien établir cette distinction façonne tout le reste de votre couche de domaine.
Identité ou valeur
Une entité possède une identité stable qui persiste même lorsque ses attributs changent : un Customer reste le même client après un déménagement. Un objet-valeur n'a pas d'identité ; deux objets-valeurs ayant les mêmes attributs sont interchangeables, comme deux billets de 5 $. Demandez-vous : « si chaque champ change, est-ce toujours la même chose ? » Si oui, c'est une entité.
Un objet-valeur
Les objets-valeurs sont immuables et s'auto-valident. Les propriétés readonly de PHP 8.1 garantissent l'immutabilité ; le constructeur protège les invariants afin qu'une instance invalide ne puisse jamais exister.
<?php
final class Email {
public function __construct(public readonly string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email: $value");
}
}
public function equals(Email $other): bool {
return strtolower($this->value) === strtolower($other->value);
}
}
$a = new Email('Jane@Example.com');
var_dump($a->equals(new Email('jane@example.com'))); // true
Égalité par valeur
Les objets-valeurs sont comparés selon une égalité structurelle, et non selon leur référence. Fournissez une méthode equals() explicite plutôt que de vous fier à ==, car la comparaison permissive de PHP obéit à des règles surprenantes. Un objet-valeur Money doit prendre en compte à la fois le montant et la devise.
<?php
final class Money {
public function __construct(
public readonly int $cents,
public readonly string $currency
) {}
public function equals(Money $o): bool {
return $this->cents === $o->cents && $this->currency === $o->currency;
}
public function add(Money $o): self {
if ($this->currency !== $o->currency) {
throw new DomainException('Currency mismatch');
}
return new self($this->cents + $o->cents, $this->currency);
}
}
$sum = (new Money(500, 'USD'))->add(new Money(250, 'USD'));
echo $sum->cents, PHP_EOL; // 750
Immutabilité et méthodes « with »
Comme les objets-valeurs sont immuables, « modifier » l'un d'eux signifie renvoyer une nouvelle instance. Les méthodes comme add() ou withAmount() ne modifient jamais l'objet ; elles produisent une nouvelle valeur. Cela élimine les problèmes d'aliasing et permet de partager librement les objets-valeurs dans tout le domaine en toute sécurité.
<?php
final class DateRange {
public function __construct(
public readonly DateTimeImmutable $start,
public readonly DateTimeImmutable $end
) {
if ($end < $start) { throw new InvalidArgumentException('end before start'); }
}
public function withEnd(DateTimeImmutable $end): self {
return new self($this->start, $end);
}
}
$r = new DateRange(new DateTimeImmutable('2026-01-01'), new DateTimeImmutable('2026-01-10'));
$r2 = $r->withEnd(new DateTimeImmutable('2026-02-01'));
echo $r->end->format('Y-m-d'), ' / ', $r2->end->format('Y-m-d'), PHP_EOL;
Une entité
Une entité est définie par son identité, généralement un identifiant généré par le domaine, et non par un auto-incrément de la base de données. L'égalité compare les identifiants. Ses attributs peuvent changer grâce à des méthodes riches en comportement qui protègent les invariants.
<?php
final class Customer {
private Email $email;
public function __construct(
public readonly string $id,
Email $email
) { $this->email = $email; }
public function changeEmail(Email $new): void { $this->email = $new; }
public function email(): Email { return $this->email; }
public function sameIdentityAs(Customer $o): bool { return $this->id === $o->id; }
}
$c = new Customer('cus_1', new Email('a@b.com'));
$c->changeEmail(new Email('c@d.com'));
echo $c->email()->value, PHP_EOL; // c@d.com
Génération de l'identité
Privilégiez la génération de l'identité dans le domaine, par exemple avec un UUID, avant la persistance, plutôt que d'attendre la base de données. Vous pouvez ainsi construire en mémoire une entité entièrement valide, la référencer dans plusieurs agrégats et la tester sans base de données. L'identifiant fait partie du modèle, et non des détails de stockage.
Un comportement riche, pas des données anémiques
Un anti-modèle courant est le modèle de domaine anémique : les entités sont de simples ensembles d'accesseurs et de mutateurs publics, tandis que toute la logique se trouve dans des classes de « service ». Le DDD pousse le comportement dans l'entité. $order->cancel() encapsule les règles d'annulation au lieu de laisser les appelants modifier directement un champ d'état.
<?php
final class Order {
private string $status = 'open';
public function __construct(public readonly string $id) {}
public function cancel(): void {
if ($this->status === 'shipped') {
throw new DomainException('Cannot cancel a shipped order');
}
$this->status = 'cancelled';
}
public function status(): string { return $this->status; }
}
$o = new Order('o1');
$o->cancel();
echo $o->status(), PHP_EOL; // cancelled
Des objets-valeurs partout
Remplacez l'obsession des types primitifs par des objets-valeurs. Au lieu de faire circuler string $email, int $cents et string $currency, encapsulez-les dans Email et Money. Avantages :
- La validation a lieu une seule fois, lors de la construction.
- Les règles métier, comme la correspondance des devises, vivent avec les données.
- Les signatures de types documentent l'intention et évitent les confusions.
Remarque sur le mappage de la persistance
Les objets-valeurs sont souvent mappés vers des colonnes intégrées plutôt que vers leurs propres tables, avec Doctrine #[Embeddable]. Les entités sont mappées vers des lignes identifiées par leur identité. Gardez les préoccupations liées à la persistance en dehors des objets du domaine eux-mêmes : le modèle ne doit pas savoir qu'il est stocké dans MySQL. Un ORM ou un mappeur écrit manuellement traduit le domaine riche en données de base de données, et inversement.
Objets-valeurs composites
Les objets-valeurs se composent entre eux. Un Address regroupe plusieurs types primitifs en un concept cohérent doté de ses propres règles d'égalité et de formatage. L'entité contient alors un type riche unique plutôt que cinq chaînes de caractères indépendantes, et les règles relatives à l'adresse sont regroupées au même endroit.
<?php
final class Address {
public function __construct(
public readonly string $street,
public readonly string $city,
public readonly string $postcode
) {
if ($postcode === '') { throw new InvalidArgumentException('postcode required'); }
}
public function equals(Address $o): bool {
return $this->street === $o->street
&& $this->city === $o->city
&& $this->postcode === $o->postcode;
}
public function oneLine(): string {
return "{$this->street}, {$this->city} {$this->postcode}";
}
}
echo (new Address('1 Main St', 'Ankara', '06000'))->oneLine(), PHP_EOL;
Vérification rapide
Entité ou objet-valeur ?
Récapitulatif
Vous avez découvert les deux éléments fondamentaux du DDD. Les entités sont définies par une identité stable, portent un comportement riche et protègent leurs invariants au moyen de méthodes. Les objets-valeurs sont immuables, s'auto-valident et sont comparés selon l'égalité de leurs attributs, ce qui remplace l'obsession des types primitifs. Une modélisation correcte, avec la logique à l'intérieur des objets plutôt que dans des ensembles de données anémiques, constitue le fondement des agrégats, des dépôts et du reste de la couche du domaine.
Questions Fréquemment Posées
La leçon « Éléments constitutifs du DDD : entités et objets-valeurs » est-elle gratuite ?
Oui — le texte complet de « Éléments constitutifs du DDD : entités et objets-valeurs » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Éléments constitutifs du DDD : entités et objets-valeurs » ?
Modélisez le domaine avec des entités riches et des objets-valeurs immuables. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer PHP Academy ?
Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « Éléments constitutifs du DDD : entités et objets-valeurs » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?
Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Éléments constitutifs du DDD : entités et objets-valeurs
- Agrégats, dépôts et fabriques
- Événements et services de domaine
- Contextes délimités et cartographie des contextes