Structure d'un Module
Maintenant que tu as compris la magie des décorateurs, voyons comment organiser nos fichiers.
Si tu viens de l'univers React ou Express "Vanilla", tu as peut-être l'habitude de grouper par type (un dossier controllers, un dossier services).
Ici, on groupe par Domaine (Feature). Chaque fonctionnalité (ex: Users, Auth, Chat) est un Module.
🍽️ L'Analogie du Restaurant
Pour comprendre qui fait quoi dans ton dossier, imagine ton API comme un grand restaurant.
| Rôle | Fichier | Analogie Restaurant | Responsabilité |
|---|---|---|---|
| Le Client | Navigateur / Postman | Le Client | Il a faim, il passe une commande (Requête HTTP). |
| Le Contrôleur | user.controller.ts | Le Serveur 🤵 | Il prend la commande du client, vérifie qu'elle est cohérente, l'apporte en cuisine, et ramène le plat fini. Il ne cuisine jamais ! |
| Le Service | user.service.ts | Le Cuisinier 👩🍳 | Il reçoit la commande du serveur, coupe les légumes, cuit la viande (Logique métier, Base de données). Il ne parle jamais au client. |
| Le DTO | user.dto.ts | Le Bon de commande 📝 | C'est le format standardisé de la commande. "Pas de pizza sans pâte". |
| Le Module | user.module.ts | La Cuisine 🏗️ | C'est la pièce qui contient le Cuisinier et le Serveur et leur fournit les ustensiles nécessaires. |
📂 L'Arborescence Type
Voici à quoi ressemble un module standard dans my-fastify-decorators. Prenons l'exemple d'un module de gestion des utilisateurs.
src/
└── user/ <-- Le Dossier du Module
├── dto/ <-- Dossier optionnel pour la validation
│ └── create-user.dto.ts
├── user.controller.ts
├── user.service.ts
└── user.module.ts <-- Le point d'entrée
1. Le Module (user.module.ts)
C'est le chef d'orchestre. C'est lui qui dit à l'application : "Hey, j'existe, et voici les contrôleurs et services que je possède".
C'est ici qu'on utilise le décorateur @Module.
import { Module } from 'my-fastify-decorators';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
controllers: [UserController], // Les "Serveurs"
providers: [UserService], // Les "Cuisiniers"
imports: [] // D'autres modules si besoin
})
export class UserModule {}
2. Le Contrôleur (user.controller.ts)
Rappel : C'est le Serveur. Son job est de recevoir la requête HTTP, d'extraire les infos (Body, Params) et d'appeler le Service.
Ce qu'il fait : Validation basique, parsing, renvoi de réponse HTTP. Ce qu'il ne fait PAS : Appels SQL, calculs complexes, logique métier.
@Controller('/users')
export class UserController {
// Le serveur a besoin d'un cuisinier (Injection)
constructor(private userService: UserService) {}
@Get('/:id')
getUser(@Param('id') id: string) {
// Il délègue immédiatement le travail
return this.userService.findById(id);
}
}
3. Le Service (user.service.ts)
Rappel : C'est le Cuisinier. C'est ici que réside la valeur de ton application.
@Service()
export class UserService {
private db = []; // Imagine une vraie DB ici
findById(id: string) {
const user = this.db.find(u => u.id === id);
if (!user) throw new NotFoundException('User not found');
return user;
}
}
4. Les DTOs (dto/)
DTO signifie Data Transfer Object. C'est un objet simple qui définit comment les données sont envoyées sur le réseau.
Pour gérer la validation avancée de vos DTOs (champs obligatoires, email valide, etc.), nous utilisons un package compagnon. 👉 Rendez-vous sur la documentation de my-class-validator pour tout savoir.
🧠 Avancé : Pourquoi avons-nous besoin du fichier Module ?
Cette section est destinée à ceux qui veulent comprendre les tripes du framework. Tu peux la sauter si tu veux juste coder.
Tu pourrais te demander :
"Pourquoi s'embêter avec ce fichier
user.module.ts? Pourquoi ne pas juste scanner tous les fichiers.service.tset.controller.tscomme le font certains frameworks ?"
C'est une question très légitime. Voici pourquoi j'ai fait ce choix d'architecture (inspiré d'Angular et NestJS) :
1. L'Encapsulation (Le principe de la "Boîte Noire")
Sans modules, tout est global. Si UserService est public, n'importe qui peut l'utiliser n'importe comment.
Avec un Module, tu peux décider d'exporter ou non un service. Ça permet de créer des frontières claires dans ton application.
2. Le Graphe de Dépendances
Pour que l'Injection de Dépendances fonctionne, le framework doit savoir qui a besoin de quoi.
Le fichier @Module sert de carte pour le compilateur. Il dit : "Dans ce contexte précis (User), voici les pièces disponibles". Cela permet de construire l'application brique par brique au lieu d'avoir un énorme sac de nœuds.
3. Préparation aux Microservices 🚀 (Oops on y est déjà...)
Mais c'est le plus gros argument!!! Si tu codes tout en "vrac", le jour où tu veux séparer ton API Utilisateurs de ton API Chat, tu vas pleurer. Avec les Modules, ton code est déjà découplé.
Tu veux transformer UserModule en un microservice indépendant ?
- Tu copies le dossier
user/. - Tu crées une nouvelle app Fastify vide. (NestJS)
- Tu importes
UserModule. - Terminé.