L'application backend

main.ts → setup.ts → AppKernel.ts

Bootcode IWA-S04 — Semaine 20, Jour 2

Objectifs de la leçon

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

Plan du cours

1

main.ts : le point d'entrée

Crée Express, appelle setup(), écoute sur le port

2

setup.ts : la chaîne de middlewares

bodyParser, morgan, cors → container → routing-controllers

3

AppKernel.ts : le container Inversify

TypeORM DataSource, les Build de chaque domaine

4

Tracer le flux de démarrage de A à Z

Dans VS Code, suivre l'exécution pas à pas

5

Intégrer un nouveau domaine

Ajouter un Build dans AppKernel

main.ts — le point d'entrée

Simple : créer le serveur et écouter

« main.ts est volontairement simple. Toute la complexité est déléguée à setup() et AppKernel. »

Le code de main.ts

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().

setup.ts — la chaîne de middlewares

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.

AppKernel.ts — le coeur

Le container Inversify + TypeORM + les Builds

« AppKernel initialise TypeORM, charge les Builds de chaque domaine, configure l'event sourcing. »

Le code d'AppKernel

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);

}

}

}

Le pattern Build

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.

Le flux de démarrage

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)

setup.ts vs AppKernel.ts

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.

Intégrer un nouveau domaine

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

});

Pièges courants

❌ 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.

À retenir !

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 🚀