Décrire les faits passés du domaine
Bootcode IWA-S04 â Semaine 17, Jour 3
L'étudiant doit savoir :
1. Créer des événements
Avec les bonnes données readonly
2. Produire des événements
Dans un agrégat
3. Comprendre le découplage
L'agrégat publie, les handlers réagissent
Le concept
Un événement = un fait passé (UserSignedUp, OrderPlaced)
La rĂšgle de nommage
Toujours au passé, factuel, lié au domaine
Commande vs ĂvĂ©nement
Intention (futur) vs fait accompli (passé)
Pourquoi les événements ?
Historique, audit, découplage
Live coding
Un agrégat Article qui produit des événements
Module 1
Un fait passé dans le domaine
Quelque chose S'EST PASSĂ dans le domaine
Un Ă©vĂ©nement dĂ©crit un fait qui a dĂ©jĂ eu lieu â il est immuable
đ€
UserSignedUp
Un utilisateur s'est inscrit
đŠ
OrderPlaced
Une commande a été passée
đ
ArticlePublished
Un article a été publié
đž
PaymentReceived
Un paiement a été reçu
đĄ La clĂ© : le passĂ©
On ne dit pas ce qu'on VEUT faire, on dit ce qui S'EST PASSĂ
Un événement = une entrée dans le journal
Le domaine tient un journal de tout ce qui s'y passe
đ 2025-01-15 10:32
UserSignedUp { userId: "u-42", email: "alice@mail.com" }
đ 2025-01-15 10:35
ArticleCreated { articleId: "a-7", title: "Hello DDD" }
đ 2025-01-15 11:00
ArticlePublished { articleId: "a-7", publishedAt: "11:00" }
đĄ Chaque entrĂ©e est un fait
On ne peut ni modifier ni effacer une entrĂ©e â elle est arrivĂ©e, point
Module 2
Au passé · factuel · lié au domaine
1ïžâŁ Toujours au passĂ©
Le verbe est conjugué au passé : Created, SignedUp, Placed
2ïžâŁ Factuel
On dĂ©crit un fait, pas une intention : ArticlePublished â â pas PublishArticle â
3ïžâŁ LiĂ© au domaine (mĂ©tier)
Le nom parle le langage du mĂ©tier : UserSignedUp â â pas RowInserted â
đĄ Le test ultime
Lisez le nom Ă voix haute : "UserSignedUp" â est-ce que votre mĂ©tier comprend ?
â Nommage technique
RowInserted
RecordSaved
DbUpdated
EmailSentEvent
Le mĂ©tier ne comprend pas â c'est du jargon de dĂ©veloppeur
â Nommage mĂ©tier
UserSignedUp
OrderPlaced
ArticlePublished
WelcomeEmailSent
Le mĂ©tier comprend â c'est son langage
đĄ L'Ă©vĂ©nement parle le langage ubiquitaire
Si votre expert métier ne comprend pas le nom, c'est mauvais signe
Module 3
Intention vs fait accompli
đš Commande
Une intention â on VEUT faire quelque chose
Tournée vers le futur
CreateArticle
SignUpUser
PlaceOrder
"Je veux créer un article"
đą ĂvĂ©nement
Un fait accompli â ça S'EST PASSĂ
Tourné vers le passé
ArticleCreated
UserSignedUp
OrderPlaced
"Un article a été créé"
đĄ La commande prĂ©cĂšde l'Ă©vĂ©nement
CreateArticle (commande) â ArticleCreated (Ă©vĂ©nement qui en rĂ©sulte)
đš Commande (intention)
class CreateArticle {
constructor(
readonly title: string,
readonly content: string,
readonly authorId: string,
) {}
}
"Je veux crĂ©er cet article" â peut Ă©chouer
đą ĂvĂ©nement (fait)
class ArticleCreated {
constructor(
readonly articleId: string,
readonly title: string,
readonly occurredAt: Date,
) {}
}
"L'article a Ă©tĂ© créé" â c'est fait, immuable
đĄ La diffĂ©rence visible
La commande contient ce qu'on VEUT faire â l'Ă©vĂ©nement contient ce qui S'EST PASSĂ + un horodatage
Module 4
Historique · Audit · Découplage
Tout est tracé
Chaque événement est une trace immuable de ce qui s'est passé
đ
Historique complet
On peut rejouer l'historique pour reconstruire l'état actuel
đ
Audit
"Qui a fait quoi et quand ?" â la rĂ©ponse est dans les Ă©vĂ©nements
// L'historique d'un article
ArticleCreated { articleId: "a-7", title: "Hello" }
ArticleEdited { articleId: "a-7", newTitle: "Hello DDD" }
ArticlePublished { articleId: "a-7", at: "11:00" }
đĄ C'est la base de l'event sourcing (S18)
On reconstruit l'Ă©tat en rejouant les Ă©vĂ©nements â comme un Git log
L'émetteur ne connaßt pas les récepteurs
L'agrĂ©gat publie un Ă©vĂ©nement â il ne sait PAS qui va rĂ©agir
đ§ AgrĂ©gat Article
Produit ArticlePublished
â publie l'Ă©vĂ©nement â
ArticlePublished { articleId, title, at }
â des handlers y rĂ©agissent, indĂ©pendamment â
đ§ EmailHandler
Notifie les abonnés
đ StatsHandler
Met Ă jour les stats
đ IndexHandler
Indexe pour la recherche
đĄ L'agrĂ©gat ne sait pas qu'ils existent
On peut ajouter/supprimer des handlers sans toucher à l'agrégat
Module 5
Un agrégat Article qui produit des événements
Une classe simple avec des donnĂ©es readonly â aucune logique
// domain/events/ArticleCreated.ts
export class ArticleCreated {
constructor(
readonly articleId: string,
readonly title: string,
readonly authorId: string,
readonly occurredAt: Date = new Date(),
) {}
}
đĄ 3 choses Ă remarquer
1ïžâŁ Tout est readonly â immuable · 2ïžâŁ Aucune mĂ©thode, juste des donnĂ©es · 3ïžâŁ Un horodatage par dĂ©faut
L'agrégat collecte les événements qu'il déclenche
// domain/aggregates/Article.ts
export class Article {
private events: object[] = [];
constructor(
readonly id: string,
readonly title: string,
readonly authorId: string,
) {
// L'agrégat publie un événement
this.events.push(new ArticleCreated(
id, title, authorId
));
}
pullEvents(): object[] {
const copy = [...this.events];
this.events = [];
return copy;
}
}
đĄ L'agrĂ©gat ne sait pas qui rĂ©agit
Il se contente de collecter les Ă©vĂ©nements â on les rĂ©cupĂšre avec pullEvents()
Un handler est totalement indépendant de l'agrégat
// infra/handlers/NotifySubscribersOnArticleCreated.ts
export class NotifySubscribersOnArticleCreated {
constructor(private mailer: Mailer) {}
async handle(event: ArticleCreated): Promise<void> {
// Réagit au fait : un article a été créé
const subscribers = await this.getSubscribers();
for (const sub of subscribers) {
await this.mailer.send(sub.email, {
subject: "Nouvel article : " + event.title,
});
}
}
}
đĄ Le handler ne connaĂźt que l'Ă©vĂ©nement
Il ne sait pas que l'Ă©vĂ©nement vient d'un Article â dĂ©couplage total
AgrĂ©gat â ĂvĂ©nement â Handler
1ïžâŁ L'agrĂ©gat agit
Un Article est créé â l'agrĂ©gat publie ArticleCreated
â
2ïžâŁ L'Ă©vĂ©nement est collectĂ©
article.pullEvents() retourne les événements accumulés
â
3ïžâŁ Les handlers rĂ©agissent
Chaque handler reçoit l'événement et fait son travail, indépendamment
const article = new Article(id, title, authorId);
const events = article.pullEvents();
// â dispatcher.dispatch(events) â handlers
â Confondre commande et Ă©vĂ©nement
â Insister sur passĂ© vs futur : CreateArticle (commande) â ArticleCreated (Ă©vĂ©nement)
â Nommer les Ă©vĂ©nements de façon technique
â Utiliser le langage mĂ©tier : RowInserted â â UserSignedUp â
â Vouloir modifier ou supprimer un Ă©vĂ©nement
â Un Ă©vĂ©nement est un fait â il est immuable. On ne le change pas, on en produit un nouveau
Les événements sont la base de l'event sourcing
La semaine prochaine, on pousse le concept plus loin
đŠ
Aujourd'hui (S17)
L'agrégat produit des événements en plus de changer d'état
đź
Semaine 18 (Event Sourcing)
L'Ă©tat EST reconstruit en rejouant les Ă©vĂ©nements â comme un Git log
đĄ Ce que vous apprenez aujourd'hui prĂ©pare S18
Maßtriser les événements maintenant = event sourcing plus facile ensuite
âȘ
ĂvĂ©nement = passĂ©
ArticleCreated, pas CreateArticle
đ
Découplage total
L'émetteur ne connaßt pas les récepteurs
đŠ
Classe simple, données readonly
Aucune logique â juste des faits
đ
Immuable
Un Ă©vĂ©nement est un fait â on ne le modifie pas
Créez un agrégat qui produit un événement dans votre projet
Demain : Bounded Contexts â dĂ©limiter son domaine