Builders Academy
Domain-Driven Design — Modéliser la Complexité
1 of 2

Chapitre 1 · Leçon 1

Entities & Value Objects

Les deux types fondamentaux d'objets en DDD

Section 1

Entity — identité dans le temps

Step 1

Qu'est-ce qu'une Entity ?

Une Entity est définie par son identité, pas ses attributs. Deux utilisateurs avec le même nom sont des entités différentes car ils ont des IDs différents. L'identité persiste dans le temps.

snippet.typescript

typescript
1// Entity — identité via l'ID
2abstract class Entity<T> {
3  protected readonly _id: T;
4
5  constructor(id: T) {
6    this._id = id;
7  }
8
9  get id(): T { return this._id; }
10
11  // Égalité basée sur l'ID
12  equals(other: Entity<T>): boolean {
13    return this._id === other._id;
14  }
15}
16
17// Entité User — son identité est son ID
18class User extends Entity<string> {
19  private _email: Email;        // Value Object
20  private _name: FullName;      // Value Object
21  private _role: UserRole;
22  private _events: DomainEvent[] = [];
23
24  private constructor(
25    id: string,
26    email: Email,
27    name: FullName,
28    role: UserRole,
29  ) {
30    super(id);
31    this._email = email;
32    this._name = name;
33    this._role = role;
34  }
35
36  // Factory method — validation dans la création
37  static create(props: CreateUserProps): User {
38    const id = generateId();
39    const email = Email.create(props.email);   // peut lancer une erreur
40    const name = FullName.create(props.firstName, props.lastName);
41    return new User(id, email, name, 'viewer');
42  }
43
44  // Reconstitution depuis la persistence (pas de validation re-exécutée)
45  static reconstitute(data: UserData): User {
46    return new User(
47      data.id,
48      Email.reconstitute(data.email),
49      FullName.reconstitute(data.firstName, data.lastName),
50      data.role as UserRole,
51    );
52  }
53
54  // Méthode métier — émet un Domain Event
55  promoteToAdmin(): void {
56    if (this._role === 'admin') throw new DomainError('Déjà admin');
57    this._role = 'admin';
58    this._events.push(new UserPromotedToAdmin(this._id, new Date()));
59  }
60
61  get email(): string { return this._email.value; }
62  get fullName(): string { return this._name.full; }
63  get role(): UserRole { return this._role; }
64  get domainEvents(): DomainEvent[] { return [...this._events]; }
65  clearEvents(): void { this._events = []; }
66}

Step 2

Value Object — égalité par valeur

Un Value Object n'a pas d'identité propre — deux VO avec les mêmes attributs sont identiques. Ils sont immuables. Email, Adresse, Montant sont des Value Objects classiques.

snippet.typescript

typescript
1// Entity — identité via l'ID
2abstract class Entity<T> {
3  protected readonly _id: T;
4
5  constructor(id: T) {
6    this._id = id;
7  }
8
9  get id(): T { return this._id; }
10
11  // Égalité basée sur l'ID
12  equals(other: Entity<T>): boolean {
13    return this._id === other._id;
14  }
15}
16
17// Entité User — son identité est son ID
18class User extends Entity<string> {
19  private _email: Email;        // Value Object
20  private _name: FullName;      // Value Object
21  private _role: UserRole;
22  private _events: DomainEvent[] = [];
23
24  private constructor(
25    id: string,
26    email: Email,
27    name: FullName,
28    role: UserRole,
29  ) {
30    super(id);
31    this._email = email;
32    this._name = name;
33    this._role = role;
34  }
35
36  // Factory method — validation dans la création
37  static create(props: CreateUserProps): User {
38    const id = generateId();
39    const email = Email.create(props.email);   // peut lancer une erreur
40    const name = FullName.create(props.firstName, props.lastName);
41    return new User(id, email, name, 'viewer');
42  }
43
44  // Reconstitution depuis la persistence (pas de validation re-exécutée)
45  static reconstitute(data: UserData): User {
46    return new User(
47      data.id,
48      Email.reconstitute(data.email),
49      FullName.reconstitute(data.firstName, data.lastName),
50      data.role as UserRole,
51    );
52  }
53
54  // Méthode métier — émet un Domain Event
55  promoteToAdmin(): void {
56    if (this._role === 'admin') throw new DomainError('Déjà admin');
57    this._role = 'admin';
58    this._events.push(new UserPromotedToAdmin(this._id, new Date()));
59  }
60
61  get email(): string { return this._email.value; }
62  get fullName(): string { return this._name.full; }
63  get role(): UserRole { return this._role; }
64  get domainEvents(): DomainEvent[] { return [...this._events]; }
65  clearEvents(): void { this._events = []; }
66}

Topics and skills covered

  • DDD
  • Entity
  • Value Object
  • Ubiquitous Language