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

Chapitre 1 · Leçon 2

Aggregates & Domain Events

Frontières de cohérence et communication entre contextes

Un Aggregate est un groupe d'objets (entités + value objects) traité comme une unité de cohérence. Toute modification passe par la racine de l'agrégat (Aggregate Root). Les Domain Events permettent la communication découplée entre agrégats.

Modéliser un Order Aggregate

snippet.typescript

typescript
1// Order Aggregate — racine d'agrégat
2class Order extends Entity<string> {
3  private _items: OrderItem[] = [];        // Entités internes à l'agrégat
4  private _status: OrderStatus = 'draft';
5  private _total: Money;
6  private _events: DomainEvent[] = [];
7
8  // ❌ On n'expose PAS les items directement
9  // Toutes les mutations passent par des méthodes de l'agrégat
10
11  addItem(productId: string, name: string, price: Money, qty: number): void {
12    if (this._status !== 'draft') {
13      throw new DomainError('Impossible de modifier une commande confirmée');
14    }
15    const existing = this._items.find((i) => i.productId === productId);
16    if (existing) {
17      existing.increaseQuantity(qty); // mutation interne autorisée
18    } else {
19      this._items.push(OrderItem.create({ productId, name, price, qty }));
20    }
21    this._total = this.calculateTotal();
22  }
23
24  confirm(): void {
25    if (this._items.length === 0) {
26      throw new DomainError('Impossible de confirmer une commande vide');
27    }
28    this._status = 'confirmed';
29    // Domain Event — d'autres parties du système peuvent réagir
30    this._events.push(
31      new OrderConfirmed({
32        orderId: this._id,
33        total: this._total,
34        confirmedAt: new Date(),
35      })
36    );
37  }
38
39  private calculateTotal(): Money {
40    return this._items.reduce(
41      (acc, item) => acc.add(item.subtotal),
42      Money.of(0, 'XOF')
43    );
44  }
45}
46
47// Domain Event — immutable, passé simple
48class OrderConfirmed implements DomainEvent {
49  readonly name = 'OrderConfirmed';
50  readonly occurredAt: Date;
51  readonly orderId: string;
52  readonly total: Money;
53
54  constructor(props: { orderId: string; total: Money; confirmedAt: Date }) {
55    this.orderId = props.orderId;
56    this.total = props.total;
57    this.occurredAt = props.confirmedAt;
58  }
59}
Un transaction = un agrégat

La règle d'or DDD : une transaction de base de données ne doit modifier qu'un seul agrégat. Si tu dois modifier deux agrégats en même temps, utilise des Domain Events pour déclencher la deuxième modification de façon asynchrone.

DDD — retenir l'essentiel

Entity = identité. Value Object = immuable + égalité par valeur. Aggregate = frontière de cohérence. Domain Event = fait métier qui s'est produit. Ces 4 concepts couvrent 80% du DDD tactique.

Topics and skills covered

  • DDD
  • Aggregate
  • Domain Events
  • Consistency