Bounded Contexts

Comment un gros projet se découpe en domaines indépendants

Bootcode IWA-S04 — Semaine 17, Jour 4

Objectifs de la leçon

1. Naviguer dans un monorepo

Organisé en bounded contexts

2. Identifier les briques DDD

Agrégats, repositories, use cases

3. Comprendre les interactions

Entre contextes par événements

Plan du cours

1

Le problème

50 agrégats dans un seul dossier = ingérable

2

Qu'est-ce qu'un Bounded Context ?

Un département indépendant de l'entreprise logicielle

3

Le monorepo Bootcode

IAC · Education · Notifications · Social · Giraffe

4

User ≠ Student

Deux classes différentes — et c'est voulu

5

Communication entre contextes

Par Domain Events, pas par imports directs

Module 1

Le problème

Quand tout est dans un seul dossier, ça devient ingérable

50 agrégats dans un seul dossier = ingérable

Imaginez un projet qui grossit…

Au début, tout est simple. Puis les fonctionnalités s'empilent. Un seul dossier core/ contient TOUT.

// core/ — un seul dossier pour TOUT le domaine 😱

core/

User.ts

Student.ts

Course.ts

Enrollment.ts

Notification.ts

Post.ts

Comment.ts

Giraffe.ts

Invoice.ts

Payment.ts

… et 40 autres fichiers 😵

💡 Le problème

On ne sait plus qui dépend de qui. Changer un fichier peut casser n'importe quoi. Le cerveau humain ne peut plus suivre.

Module 2

Qu'est-ce qu'un Bounded Context ?

Un département indépendant de l'entreprise logicielle

Bounded Context = département d'entreprise

Dans une entreprise, chaque département a son propre vocabulaire et ses propres règles

🏥

Comptabilité

"Facture", "TVA", "Exercice fiscal"

📦

Logistique

"Colis", "Bon de livraison", "Tournée"

👥

RH

"Salarié", "Congés", "Bulletin de paie"

💡 L'analogie

Un Bounded Context = un département. Il a ses propres modèles, son vocabulaire, ses règles. Il ne mélange pas avec les autres.

Chaque contexte a son propre vocabulaire

L'Ubiquitous Language revisitée : le même mot peut avoir un sens différent selon le contexte

📋 Contexte "Education"

"User" = un étudiant qui suit des cours

Propriétés : enrollments, progress, certificates

🔐 Contexte "IAC"

"User" = un compte d'authentification

Propriétés : email, passwordHash, roles

💡 Le mot "User" n'est pas universel

Dans IAC, on parle de connexion. Dans Education, on parle d'apprentissage. Deux mondes, deux modèles.

Module 3

Le monorepo Bootcode

5 bounded contexts en pratique

Les 5 bounded contexts de Bootcode

🔐

IAC

Identity & Access Control

Auth, users, roles, sessions

🎓

Education

Cours & apprentissage

Courses, students, enrollments

🔔

Notifications

Emails & alertes

Templates, delivery, logs

💬

Social

Posts & communauté

Posts, comments, likes

🦒

Giraffe

L'outil pédagogique

Exercises, grading, feedback

💡 Chaque contexte = un département

Ils vivent dans le même monorepo mais sont indépendants. Chacun a son propre domaine métier.

Structure uniforme : core/ + adapters/ + messages/

Chaque contexte suit la MÊME structure — on s'y retrouve partout

🧠 core/

Le domaine pur

Agrégats, use cases, interfaces de repositories

🔌 adapters/

L'infrastructure

Implémentations repositories, API HTTP, DB

📡 messages/

Les événements

Domain Events pour communiquer entre contextes

💡 On retrouve ses marques partout

Que vous soyez dans IAC ou dans Giraffe, la structure est la même. Le cerveau n'a qu'un seul plan à mémoriser.

Anatomie d'un contexte : Education

contexts/education/

core/

aggregates/

Student.ts

Course.ts

Enrollment.ts

usecases/

EnrollStudent.ts

CompleteCourse.ts

repositories/

StudentRepository.ts // interface

adapters/

api/

EnrollmentController.ts

repositories/

PostgresStudentRepository.ts

messages/

events/

StudentEnrolled.ts

CourseCompleted.ts

💡 Cette structure se répète pour chaque contexte

IAC, Notifications, Social, Giraffe : même squelette, contenus différents

Module 4

User ≠ Student

Deux classes différentes — et c'est voulu

"User" dans IAC ≠ "Student" dans Education

Le même humain, deux modèles différents

Une personne qui s'inscrit à un cours est un "User" pour l'auth et un "Student" pour l'éducation. Deux classes, deux responsabilités.

🔐 IAC — User

Ce qui compte : l'identité

  • • email, passwordHash
  • • roles, permissions
  • • lastLoginAt
  • • sessions actives

🎓 Education — Student

Ce qui compte : l'apprentissage

  • • enrollments (cours suivis)
  • • progress (avancement)
  • • certificates obtenus
  • • exercices soumis

💡 C'est INTENTIONNEL

Chaque contexte ne garde que ce qui le concerne. IAC ignore les cours. Education ignore les mots de passe.

User (IAC) vs Student (Education) en code

// contexts/iac/core/User.ts

export class User {

constructor(

private id: string,

private email: string,

private passwordHash: string,

private roles: string[]

) {}

canAccess(resource: string): boolean {

return this.roles.includes("admin");

}

}

// contexts/education/core/Student.ts

export class Student {

constructor(

private id: string,

private enrollments: Enrollment[],

private certificates: string[]

) {}

isEnrolledIn(courseId: string): boolean {

return this.enrollments

.some(e => e.courseId === courseId);

}

}

💡 Deux classes, zéro lien entre elles

User ne sait pas ce qu'est un cours. Student ne sait pas ce qu'est un mot de passe. Indépendance totale.

Deux classes "User" : bug ou feature ?

❌ Penser que c'est un bug

"Pourquoi on a deux classes User ? On devrait factoriser en une seule !"

→ On mélange l'auth et l'éducation → couplage → chaos

✅ Comprendre que c'est intentionnel

"Chaque contexte a son propre modèle. User (IAC) et Student (Education) servent des buts différents."

→ Indépendance → clarté → évolutivité

💡 La règle d'or

Ne JAMAIS partager un agrégat entre contextes. Chaque contexte a ses propres modèles, point.

Module 5

Communication entre contextes

Par Domain Events, pas par imports directs

Les contextes communiquent par événements

Pas d'import direct entre contextes

IAC ne peut pas importer Student depuis Education. À la place, il publie un événement et Education réagit.

1️⃣

IAC publie un événement

"UserRegistered" avec l'email et l'ID

2️⃣

Education écoute et réagit

Crée un Student avec cet ID, prêt à s'inscrire à des cours

3️⃣

Notifications écoute aussi

Envoie un email de bienvenue au nouvel utilisateur

💡 Le principe

L'événement est le SEUL pont entre contextes. Celui qui publie ne sait pas qui écoute. Couplage minimal.

Domain Events en code TypeScript

// 1️⃣ IAC publie un événement

// contexts/iac/messages/events/UserRegistered.ts

export class UserRegistered {

constructor(public userId: string, public email: string) {}

}

// contexts/iac/core/usecases/RegisterUser.ts

async execute(input) {

const user = await this.repo.save(input);

await this.eventBus.publish(

new UserRegistered(user.id, user.email)

);

}

// 2️⃣ Education réagit à l'événement

// contexts/education/adapters/handlers/OnUserRegistered.ts

@EventHandler(UserRegistered)

async handle(event: UserRegistered) {

const student = new Student(event.userId, [], []);

await this.studentRepo.save(student);

}

💡 IAC ne sait pas qu'Education existe

Il publie juste "UserRegistered". Ceux qui veulent réagir s'abonnent. Couplage = zéro.

Comment les contextes se parlent-ils ?

❌ Import direct

// iac/core/usecases/RegisterUser.ts

import { Student } from

"../../education/core/Student";

// IAC dépend d'Education 😱

const student = new Student(...);

Couplage fort : changer Education casse IAC

✅ Communication par événement

// iac/core/usecases/RegisterUser.ts

await this.eventBus.publish(

new UserRegistered(id, email)

);

// IAC ne connaît pas Education ✅

Couplage faible : chaque contexte évolue librement

Pièges courants à éviter

❌ Penser que deux classes "User" sont un bug

✅ C'est intentionnel : chaque contexte a son propre modèle adapté à son métier

❌ Confondre bounded context et dossier technique

✅ Un bounded context = un domaine métier, pas un regroupement technique (ex: "utils/", "helpers/")

❌ Partager un agrégat entre contextes

✅ Chaque contexte a ses propres modèles. La communication se fait par événements, pas par imports

Naviguer dans le monorepo — concrètement

Ouvrez le monorepo Bootcode dans VS Code et suivez ces repères

📂

1️⃣ Le dossier racine contexts/

C'est là que vivent les 5 bounded contexts : iac/, education/, notifications/, social/, giraffe/

🧠

2️⃣ Dans chaque contexte, commencez par core/

C'est le cœur métier : les agrégats, les use cases, les interfaces de repositories

📡

3️⃣ Regardez messages/events/ pour comprendre les liens

Les événements révèlent comment les contextes se parlent sans se connaître

🔍

4️⃣ Utilisez la recherche VS Code (Ctrl+P)

Tapez "Student" — vous verrez qu'il n'existe que dans education/. Preuve d'indépendance.

À retenir !

🏢

Bounded Context = département

Domaine métier indépendant, pas dossier technique

🗣️

Vocabulaire propre

Le même mot = sens différent selon le contexte

📡

Communication par événements

Pas d'import direct entre contextes

🏗️

Structure uniforme

core/ + adapters/ + messages/ partout

Questions ?

Ouvrez le monorepo Bootcode et identifiez les 5 contextes

Demain : Atelier de modélisation — dessinez vos propres bounded contexts