main.ts → setup.ts → AppKernel.ts
Bootcode IWA-S04 — Semaine 20, Jour 2
1. Comprendre le rôle de chaque fichier de démarrage
main.ts, setup.ts, AppKernel.ts — chacun a une responsabilité unique
2. Tracer le flux de l'initialisation de l'application
De l'entrée du programme jusqu'aux routes Express
3. Identifier quels Build sont chargés et dans quel ordre
Chaque domaine apporte son Build dans AppKernel
4. Comprendre comment les controllers sont connectés aux routes
routing-controllers branche les décorateurs sur Express
main.ts : le point d'entrée
Crée Express, appelle setup(), écoute sur le port
setup.ts : la chaîne de middlewares
bodyParser, morgan, cors → container → routing-controllers
AppKernel.ts : le container Inversify
TypeORM DataSource, les Build de chaque domaine
Tracer le flux de démarrage de A à Z
Dans VS Code, suivre l'exécution pas à pas
Intégrer un nouveau domaine
Ajouter un Build dans AppKernel
Simple : créer le serveur et écouter
« main.ts est volontairement simple. Toute la complexité est déléguée à setup() et AppKernel. »
Trois étapes : créer Express, configurer, écouter.
// apps/giraffe/src/main.ts
import express from 'express';
import { setup } from './setup';
const app = express();
// 1️⃣ Configurer middlewares + container + routes
setup(app);
// 2️⃣ Démarrer le serveur
const port = process.env.PORT ?? 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
✅ C'est tout. Pas de logique métier ici. main.ts délègue tout à setup().
Configure Express : middlewares → container Inversify → routing-controllers.
// apps/giraffe/src/setup.ts
export function setup(app: Express) {
// 1️⃣ Middlewares globaux
app.use(bodyParser.json());
app.use(morgan('dev'));
app.use(cors());
// 2️⃣ Container Inversify (DI)
const kernel = new AppKernel();
await kernel.init();
const container = kernel.container;
// 3️⃣ routing-controllers branche les controllers
useExpressServer(app, {
controllers: [UserController, AuthController, ...],
middlewares: [AuthenticationMiddleware],
interceptors: [],
});
}
💡 setup.ts = plomberie Express. Il ne contient AUCUNE logique métier.
Le container Inversify + TypeORM + les Builds
« AppKernel initialise TypeORM, charge les Builds de chaque domaine, configure l'event sourcing. »
Le container Inversify, la DataSource TypeORM, et la liste des Builds.
export class AppKernel {
public container: Container;
// Les Builds de chaque domaine
private builds = [
new UserBuild(),
new AuthBuild(),
new WebhookBuild(),
];
async init() {
this.container = new Container();
// 1️⃣ Initialiser TypeORM
const dataSource = await initializeDataSource();
this.container.bind(DataSource).toConstantValue(dataSource);
// 2️⃣ Charger chaque Build
for (const build of this.builds) {
build.build(this.container);
}
}
}
Chaque domaine apporte un Build qui enregistre ses dépendances dans le container.
// packages/iac/core/build/UserBuild.ts
export class UserBuild implements Build {
build(container: Container) {
// Repositories
container.bind(TYPES.UserRepository)
.to(PostgresUserRepository).inSingletonScope();
// Use cases
container.bind(TYPES.SignUpUseCase).to(SignUp);
container.bind(TYPES.SignInUseCase).to(SignIn);
// Handlers d'événements
container.bind(TYPES.UserRoleUpgradedHandler).to(UserRoleUpgradedHandler);
}
}
💡 Un Build = un module d'enregistrement de dépendances. Ajouter un domaine = ajouter un Build dans AppKernel.
De main.ts aux routes Express
1️⃣ main.ts crée Express et appelle setup(app)
2️⃣ setup.ts branche les middlewares (bodyParser, morgan, cors)
3️⃣ setup.ts instancie AppKernel et appelle init()
4️⃣ AppKernel.init() initialise TypeORM + charge les Builds
5️⃣ setup.ts branche routing-controllers sur Express
6️⃣ main.ts appelle app.listen(port)
Deux fichiers, deux responsabilités. Ne pas les confondre.
setup.ts
Plomberie Express : middlewares (bodyParser, morgan, cors), routing-controllers.
Sait quels controllers enregistrer.
AppKernel.ts
Le container Inversify : TypeORM DataSource, Builds, event sourcing.
Sait quelles dépendances enregistrer par domaine.
⚠️ Piège : setup.ts appelle AppKernel, mais ils ne font pas la même chose. setup = HTTP, AppKernel = DI.
Exemple : ajouter un domaine billing. Trois étapes.
// 1️⃣ Créer le Build
export class BillingBuild implements Build {
build(container: Container) {
container.bind(TYPES.BillingRepository).to(PostgresBillingRepository);
container.bind(TYPES.CreateInvoiceUseCase).to(CreateInvoice);
}
}
// 2️⃣ L'ajouter dans AppKernel
private builds = [
new UserBuild(),
new BillingBuild(), // ← nouveau
];
// 3️⃣ Enregistrer le controller dans setup.ts
useExpressServer(app, {
controllers: [..., BillingController], // ← nouveau
});
❌ Se perdre dans la quantité de code
Rester focalisé sur le flux principal. Ne pas lire chaque fichier en détail aujourd'hui.
⚠️ Confondre setup.ts et AppKernel.ts
setup.ts = Express middleware. AppKernel.ts = DI container. Deux mondes différents.
❌ Paniquer devant la taille du codebase
Chaque fichier a un rôle unique et clair. On les trace un par un.
✅ main.ts est simple : créer le serveur et écouter. Toute la complexité est dans setup/AppKernel.
✅ AppKernel est le coeur : il initialise TypeORM, charge les Builds, configure l'event sourcing.
✅ Chaque Build enregistre les dépendances de son domaine dans le container partagé.
✅ Les étudiants voient pour la première fois comment tout s'assemble dans un vrai projet.
Demain : les controllers routing-controllers 🚀