Atelier modélisation

D'un brief métier à un domaine modélisé

Bootcode IWA-S04 — Semaine 17, Jour 5

Objectifs de l'atelier

1. Extraire les concepts clés

À partir d'un brief métier

2. Identifier les bounded contexts

Découper le projet en contextes

3. Modéliser agrégats, value objects, événements

Les briques du domaine

4. Lister les use cases

À partir des besoins métier

Plan du cours

1

Le brief métier FitClub

Une salle de sport — les étudiants suivent en temps réel

2

Étape par étape : les bounded contexts

Membership, Coaching, Billing…

3

Agrégats, value objects, événements, use cases

On modélise ensemble, étape par étape

4

La méthode : noms → concepts, verbes → actions

Souligner les noms et les verbes dans le brief

5

L'exercice de l'après-midi

Chacun choisit un domaine et modélise

Module 1

Le brief métier FitClub

Une salle de sport qui veut se digitaliser

Le brief FitClub

📋 Le projet

FitClub est une salle de sport qui veut lancer une plateforme digitale. Les membres peuvent s'inscrire avec leur email, choisir un type d'abonnement (mensuel ou annuel) et payer leur cotisation.

Chaque membre peut réserver une session avec un coach. Le coach est assigné à la session selon sa spécialité. La session a une durée et un créneau horaire.

Quand un membre s'inscrit, un événement MemberSubscribed est émis. Quand une session est réservée, SessionBooked est émis. Le paiement déclenche PaymentProcessed.

L'administration peut suspendre un membre, réactiver un abonnement et consulter les statistiques de fréquentation.

📝 Suivez en temps réel

Soulignez les noms (concepts) en bleu et les verbes (actions) en rouge

La méthode d'analyse

Deux couleurs, deux catégories

On lit le brief et on surligne ce qui compte

🔵

Les NOMS → concepts

Membre, abonnement, session, coach, paiement…

→ Deviennent des agrégats ou value objects

🔴

Les VERBES → actions

S'inscrire, réserver, payer, suspendre…

→ Deviennent des use cases ou des événements

🎯 La méthode est plus importante que le résultat

Le vocabulaire métier doit se retrouver dans le code

Module 2

Étape 1 — Les bounded contexts

Découper le projet en contextes métier

1️⃣ Extraire les bounded contexts

L'analogie des départements

Une entreprise a des départements : RH, Compta, Ventes… Chacun a son vocabulaire et ses règles. Un bounded context = un département du logiciel.

🆔

Membership

Inscription, profil, email, type d'abonnement

🏋️

Coaching

Sessions, coachs, spécialités, créneaux, réservations

💳

Billing

Paiements, cotisations, factures

📊

Reporting

Statistiques de fréquentation, administration

Les contextes de FitClub

Chaque contexte a son propre vocabulaire et ses propres règles

🆔

Membership

Membre, email, abonnement, suspension

« Un membre a un email unique »

🏋️

Coaching

Session, coach, spécialité, créneau

« Une session a un coach et une durée »

💳

Billing

Paiement, cotisation, facture

« Un paiement déclenche PaymentProcessed »

📊

Reporting

Statistiques, fréquentation, admin

« L'admin consulte les stats »

💡 Chaque contexte est indépendant — ils communiquent par événements

Module 3

Étape 2 — Les agrégats

Les racines de cohérence du domaine

2️⃣ Identifier les agrégats

Un agrégat = une racine de cohérence

On regroupe les concepts qui doivent rester cohérents ensemble. La racine est l'entité qu'on manipule directement.

🆔

Member (Membership)

Racine : Member — contient email, statut, abonnement

📋

Subscription (Membership)

Racine : Subscription — type, dates, statut

🏋️

Session (Coaching)

Racine : Session — membre, coach, créneau, durée

🧑‍🏫

Coach (Coaching)

Racine : Coach — nom, spécialités, disponibilité

💡 On manipule toujours l'agrégat via sa racine

Pas d'accès direct aux objets internes — la cohérence est garantie

Code — l'agrégat Member

// membership/aggregate/Member.ts

export class Member {

private constructor(

public readonly id: string,

private email: Email,

private status: MemberStatus,

private subscription: Subscription | null,

) {}

static subscribe(email: Email, type: MembershipType): Member {

return new Member(crypto.randomUUID(), email, "active", new Subscription(type));

}

suspend(): void {

if (this.status === "suspended") throw new Error("Déjà suspendu");

this.status = "suspended";

}

}

💡 La racine Member protège sa cohérence interne

On ne peut pas suspendre un membre déjà suspendu — la règle est dans l'agrégat

Module 4

Étape 3 — Les value objects

Des concepts sans identité, définis par leur valeur

3️⃣ Identifier les value objects

Un value object = défini par sa valeur, pas par une identité

Deux emails identiques sont interchangeables. Pas besoin d'ID — la valeur suffit.

📧

Email

Valide le format, immuable

🎫

MembershipType

Mensuel ou annuel

⏱️

SessionDuration

30, 45 ou 60 minutes

🕐

TimeSlot

Créneau horaire (début + fin)

🏷️

Specialty

Musculation, cardio, yoga…

💰

Money

Montant + devise

💡 Les value objects valident à la construction — pas d'état invalide possible

Code — le value object MembershipType

// membership/value-objects/MembershipType.ts

export class MembershipType {

private static readonly VALUES = ["monthly", "annual"] as const;

constructor(private readonly value: string) {

if (!MembershipType.VALUES.includes(value as any)) {

throw new Error("Type invalide");

}

}

isAnnual(): boolean {

return this.value === "annual";

}

price(): number {

return this.isAnnual() ? 480 : 45;

}

equals(other: MembershipType): boolean {

return this.value === other.value;

}

}

💡 Pas d'ID — deux MembershipType("annual") sont égaux

La validation est dans le constructeur : impossible de créer un type invalide

Module 5

Étape 4 — Les événements

Les faits passés du domaine

4️⃣ Identifier les événements

Un événement = un fait qui s'est produit dans le passé

Toujours au passé : « MemberSubscribed », jamais « SubscribeMember ». C'est ce qui s'est passé, pas ce qu'on veut faire.

🆔

MemberSubscribed

Émis quand un membre s'inscrit avec un type d'abonnement

🏋️

SessionBooked

Émis quand une session est réservée avec un coach

🧑‍🏫

CoachAssigned

Émis quand un coach est assigné à une session

💳

PaymentProcessed

Émis quand un paiement de cotisation est traité

🚫

MemberSuspended

Émis quand l'admin suspend un membre

Code — l'événement MemberSubscribed

// membership/events/MemberSubscribed.ts

export class MemberSubscribed {

constructor(

public readonly memberId: string,

public readonly email: string,

public readonly membershipType: string,

public readonly occurredAt: Date = new Date(),

) {}

static from(member: Member): MemberSubscribed {

return new MemberSubscribed(

member.id,

member.email.toString(),

member.subscription?.type.value ?? "none",

);

}

}

💡 L'événement capture l'état au moment du fait

Immuable — on ne modifie jamais un événement, on le consomme

Module 6

Étape 5 — Les use cases

Les actions que le système exécute

5️⃣ Identifier les use cases

Un use case = une action (verbe) que le système exécute

Contrairement à l'événement (fait passé), le use case est l'intention : « SubscribeMember », pas « MemberSubscribed ».

📝

SubscribeMember

Inscrit un membre avec un type d'abonnement → émet MemberSubscribed

📅

BookSession

Réserve une session pour un membre → émet SessionBooked

🧑‍🏫

AssignCoach

Assigne un coach à une session → émet CoachAssigned

💳

ProcessPayment

Traite un paiement de cotisation → émet PaymentProcessed

🚫

SuspendMember

Suspend un membre (admin) → émet MemberSuspended

🔄

ReactivateSubscription

Réactive un abonnement suspendu

Code — le use case SubscribeMember

// membership/usecases/SubscribeMember.ts

export class SubscribeMember {

constructor(

private memberRepo: MemberRepository,

private eventBus: EventBus,

) {}

async execute(cmd: { email: string; type: string }): Promise<string> {

const email = new Email(cmd.email);

const type = new MembershipType(cmd.type);

const member = Member.subscribe(email, type);

await this.memberRepo.save(member);

await this.eventBus.publish(MemberSubscribed.from(member));

return member.id;

}

}

💡 Le use case orchestre : crée l'agrégat, sauvegarde, publie l'événement

Une classe, une action, execute() — la logique est ici

Pièges courants

❌ Un seul contexte pour tout

Vouloir tout mettre dans Membership — sessions, paiements, stats…

→ Un monolithe conceptuel, impossible à découper

✅ Séparer par vocabulaire

Membership parle de membres, Coaching de sessions, Billing de paiements

→ Analogie des départements : chacun son métier

❌ Confondre use case et événement

Nommer un événement « SubscribeMember » (présent/intention)

→ On ne sait plus si c'est une action ou un fait

✅ Use case = verbe, événement = passé

SubscribeMember (action) → MemberSubscribed (fait passé)

→ Le use case déclenche, l'événement constate

❌ Mauvais nommage métier

Appeler un membre « User » ou un abonnement « Plan »

→ Le vocabulaire technique remplace le vocabulaire métier

✅ Le vocabulaire métier dans le code

Member, Subscription, Session, Coach — les mots du brief

→ Le code parle le même langage que le métier

L'exercice de l'après-midi

🎯 Chacun choisit un domaine et modélise

Appliquez la méthode vue ce matin sur votre propre brief

1️⃣

Choisissez un domaine

Bibliothèque, livraison de repas, gestion de stock, école…

2️⃣

Écrivez un brief de 5-6 lignes

Décrivez le métier comme si vous parliez au client

3️⃣

Soulignez les noms (bleu) et les verbes (rouge)

La méthode d'analyse — c'est la partie la plus importante

4️⃣

Modélisez : contexts, agrégats, value objects, événements, use cases

Un schéma sur papier ou au tableau — pas de code encore

⏱️ Présentation à 16h — chacun présente sa modélisation

La semaine prochaine : S18

On va implémenter un agrégat event-sourcé complet

Tout ce qu'on a modélisé aujourd'hui servira de base

📦

Les agrégats

Member, Session… deviennent event-sourcés

Les événements

MemberSubscribed… sont stockés et rejoués

🔄

Reconstruction d'état

On rejoue les événements pour reconstruire l'agrégat

🎯

Use cases event-sourcés

SubscribeMember émet et persiste l'événement

💡 La modélisation d'aujourd'hui est le socle de l'implémentation de demain

À retenir !

🔍

Noms → concepts, verbes → actions

La méthode d'analyse en deux couleurs

🗂️

Bounded contexts = départements

Membership, Coaching, Billing, Reporting

📦

Agrégats + value objects + événements

Les briques du domaine modélisé

🗣️

Le vocabulaire métier dans le code

Member, pas User — Subscription, pas Plan

Questions ?

Choisissez votre domaine et modélisez cet après-midi

Semaine 18 : implémenter un agrégat event-sourcé complet