03 - Modules et Build Tools : Architecture Moderne et Performance
3.1 Introduction aux Modules TypeScript
Évolution des Modules JavaScript
L’écosystème JavaScript a connu une évolution remarquable dans la gestion des modules. Des scripts simples chargés globalement aux systèmes de modules sophistiqués, cette transformation a fondamentalement changé la manière dont nous structurons nos applications.
En 2025, les modules sont devenus la pierre angulaire du développement JavaScript moderne. Ils permettent de :
- Organiser le code en unités logiques et réutilisables
- Gérer les dépendances de manière déclarative
- Optimiser le chargement avec le code splitting
- Faciliter la maintenance à travers l’encapsulation
TypeScript et les Modules
TypeScript apporte une couche supplémentaire de sécurité aux modules JavaScript en ajoutant :
- Typage statique des imports/exports
- Analyse de dépendances à la compilation
- Refactoring sécurisé à travers les modules
- Auto-complétion intelligente des imports
Philosophie de l’Architecture Modulaire
Une bonne architecture modulaire suit plusieurs principes :
- Single Responsibility : Chaque module a une seule responsabilité claire
- Loose Coupling : Les modules dépendent d’interfaces, pas d’implémentations
- High Cohesion : Les fonctionnalités liées sont regroupées
- Explicit Dependencies : Toutes les dépendances sont déclarées explicitement
3.2 ES Modules (ESM) : Le Standard Moderne
Syntaxe des ES Modules
Les ES Modules (ESM) sont devenus le standard officiel pour la modularisation en JavaScript. TypeScript supporte nativement cette syntaxe depuis ses premières versions.
Export Nommé
// utils/math.ts
export const PI = 3.14159;
export function addition(a: number, b: number): number {
return a + b;
}
export function multiplication(a: number, b: number): number {
return a * b;
}
export class Calculatrice {
constructor(private valeurInitiale: number = 0) {}
ajouter(valeur: number): this {
this.valeurInitiale += valeur;
return this;
}
resultat(): number {
return this.valeurInitiale;
}
}
// Export d'un type
export type Operation = 'addition' | 'multiplication' | 'division';
Export par Défaut
// services/calculatrice.ts
import { Calculatrice, Operation } from '../utils/math';
export default class ServiceCalculatrice {
private calculatrice: Calculatrice;
constructor(valeurInitiale: number = 0) {
this.calculatrice = new Calculatrice(valeurInitiale);
}
executer(operation: Operation, valeur: number): number {
switch (operation) {
case 'addition':
return this.calculatrice.ajouter(valeur).resultat();
case 'multiplication':
// Implémentation...
return this.calculatrice.resultat() * valeur;
default:
throw new Error(`Opération non supportée: ${operation}`);
}
}
}
Import des Modules
// main.ts
// Import nommé
import { PI, addition, multiplication, Calculatrice } from './utils/math';
import type { Operation } from './utils/math';
// Import par défaut
import ServiceCalculatrice from './services/calculatrice';
// Import combiné
import ServiceCalculatrice, { Calculatrice as Calc } from './services/calculatrice';
// Import de tout l'espace de noms
import * as MathUtils from './utils/math';
// Utilisation
const resultat = addition(10, 20);
const service = new ServiceCalculatrice();
const calc = new Calc(5);
Types et Modules
TypeScript permet d’exporter et d’importer des types de manière optimisée :
// types/user.ts
export interface Utilisateur {
id: number;
nom: string;
email: string;
dateCreation: Date;
}
export type StatutUtilisateur = 'actif' | 'inactif' | 'suspendu';
export type Permission = 'lire' | 'ecrire' | 'supprimer' | 'administrer';
// Import dans un autre fichier
import type { Utilisateur, StatutUtilisateur } from './types/user';
import { Permission } from './types/user'; // Import de valeur
Modules Dynamiques
Les imports dynamiques permettent de charger des modules à la demande, ce qui est essentiel pour le code splitting :
// Chargement dynamique d'un module
async function chargerModuleUtilisateur() {
const { Utilisateur, StatutUtilisateur } = await import('./types/user');
const utilisateur: Utilisateur = {
id: 1,
nom: 'Alice',
email: 'alice@example.com',
dateCreation: new Date(),
};
return utilisateur;
}
// Import conditionnel
async function chargerService(service: 'auth' | 'database' | 'api') {
switch (service) {
case 'auth':
return await import('./services/auth');
case 'database':
return await import('./services/database');
case 'api':
return await import('./services/api');
default:
throw new Error(`Service inconnu: ${service}`);
}
}
3.3 Configuration TypeScript pour Modules
tsconfig.json pour Modules Modernes
Une configuration appropriée est essentielle pour tirer le meilleur parti des modules TypeScript :
{
"compilerOptions": {
// Cible moderne pour les modules
"target": "ES2024",
// Système de modules
"module": "ESNext",
"moduleResolution": "bundler",
// Support des chemins absolus
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/utils/*": ["src/utils/*"],
"@/types/*": ["src/types/*"],
"@/services/*": ["src/services/*"]
},
// Génération des déclarations
"declaration": true,
"declarationMap": true,
// Génération des source maps
"sourceMap": true,
// Options de sortie
"outDir": "./dist",
"rootDir": "./src",
// Interopérabilité
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
// Options strictes
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
// Options de module
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"isolatedModules": true,
"verbatimModuleSyntax": false
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
}
Configuration Multi-Projets
Pour les projets complexes, TypeScript supporte les workspaces :
// tsconfig.json (racine)
{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/ui" },
{ "path": "./packages/utils" },
{ "path": "./apps/web" },
{ "path": "./apps/mobile" }
]
}
// packages/core/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"composite": true
},
"include": ["src/**/*"],
"exclude": ["dist", "node_modules"]
}
Import Maps
Les import maps permettent de mapper des URLs à des modules spécifiques :
// importmap.json
{
"imports": {
"react": "/node_modules/react/index.js",
"react-dom": "/node_modules/react-dom/index.js",
"lodash": "/node_modules/lodash/lodash.js",
"@mon-app/utils": "/src/utils/index.js",
"@mon-app/components": "/src/components/index.js"
}
}
3.4 Build Tools Modernes pour TypeScript
Vite : Le Build Tool Ultra-Rapide
Vite est devenu le standard de facto pour les projets TypeScript en 2025 grâce à sa vitesse exceptionnelle et son excellente intégration TypeScript.
Installation et Configuration
# Création d'un projet Vite + TypeScript
npm create vite@latest mon-app -- --template react-ts
# Installation manuelle
npm install -D vite @vitejs/plugin-react typescript
Configuration Vite
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
export default defineConfig({
plugins: [react()],
// Configuration des chemins
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@/components': resolve(__dirname, 'src/components'),
'@/utils': resolve(__dirname, 'src/utils'),
'@/types': resolve(__dirname, 'src/types'),
'@/services': resolve(__dirname, 'src/services'),
},
},
// Configuration TypeScript
esbuild: {
target: 'es2024',
},
// Optimisation du build
build: {
target: 'es2024',
minify: 'esbuild',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash', 'date-fns'],
},
},
},
},
// Configuration du serveur de développement
server: {
port: 3000,
open: true,
cors: true,
},
});
esbuild : Compilation Ultra-Rapide
esbuild est un compilateur JavaScript extrêmement rapide écrit en Go, excellent pour les projets TypeScript :
// build.js
import esbuild from 'esbuild';
import path from 'path';
const build = async () => {
try {
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js',
format: 'esm',
target: 'es2024',
sourcemap: true,
minify: true,
treeShaking: true,
define: {
'process.env.NODE_ENV': '"production"',
},
external: ['react', 'react-dom'],
loader: {
'.ts': 'ts',
'.tsx': 'tsx',
},
});
console.log('Build terminé avec succès !');
} catch (error) {
console.error('Erreur de build:', error);
process.exit(1);
}
};
build();
SWC : Compilation Rust pour Performance Maximale
SWC (Speedy Web Compiler) est un compilateur écrit en Rust qui offre des performances exceptionnelles :
// .swcrc
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"target": "es2024",
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true,
"dynamicImport": true
},
"transform": {
"react": {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
"throwIfNamespace": true,
"development": false,
"useBuiltins": false
},
"optimizer": {
"globals": {
"vars": {
"process.env.NODE_ENV": "\"production\""
}
}
}
},
"loose": false,
"externalHelpers": false
},
"module": {
"type": "es6",
"strict": true,
"strictMode": true
},
"sourceMaps": true,
"inlineSourcesContent": true
}
3.5 Code Splitting et Lazy Loading
Stratégies de Code Splitting
Le code splitting permet de diviser l’application en morceaux plus petits qui peuvent être chargés à la demande, améliorant ainsi les performances de chargement initial.
Splitting par Route
// App.tsx
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import LoadingSpinner from './components/LoadingSpinner';
// Lazy loading des composants de route
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
const ContactPage = lazy(() => import('./pages/ContactPage'));
const DashboardPage = lazy(() => import('./pages/DashboardPage'));
const App: React.FC = () => {
return (
<BrowserRouter>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/contact" element={<ContactPage />} />
<Route path="/dashboard" element={<DashboardPage />} />
</Routes>
</Suspense>
</BrowserRouter>
);
};
export default App;
Splitting par Composant
// components/HeavyComponent.tsx
import { lazy } from 'react';
const HeavyChart = lazy(() => import('./HeavyChart'));
const HeavyTable = lazy(() => import('./HeavyTable'));
interface HeavyComponentProps {
showChart: boolean;
showTable: boolean;
}
const HeavyComponent: React.FC<HeavyComponentProps> = ({
showChart,
showTable
}) => {
return (
<div>
{showChart && (
<Suspense fallback={<div>Chargement du graphique...</div>}>
<HeavyChart />
</Suspense>
)}
{showTable && (
<Suspense fallback={<div>Chargement du tableau...</div>}>
<HeavyTable />
</Suspense>
)}
</div>
);
};
export default HeavyComponent;
Splitting par Fonctionnalité
// utils/dynamicImports.ts
export const loadAuthModule = () => import('./modules/auth');
export const loadAdminModule = () => import('./modules/admin');
export const loadReportingModule = () => import('./modules/reporting');
// Utilisation
class ModuleLoader {
private loadedModules = new Map<string, any>();
async loadModule(moduleName: string) {
if (this.loadedModules.has(moduleName)) {
return this.loadedModules.get(moduleName);
}
let module;
switch (moduleName) {
case 'auth':
module = await loadAuthModule();
break;
case 'admin':
module = await loadAdminModule();
break;
case 'reporting':
module = await loadReportingModule();
break;
default:
throw new Error(`Module inconnu: ${moduleName}`);
}
this.loadedModules.set(moduleName, module);
return module;
}
}
Préchargement Intelligent
// utils/preloader.ts
class Preloader {
private preloadQueue: Set<string> = new Set();
private preloadedModules = new Map<string, any>();
// Précharger un module
async preload(moduleName: string): Promise<void> {
if (this.preloadQueue.has(moduleName) || this.preloadedModules.has(moduleName)) {
return;
}
this.preloadQueue.add(moduleName);
try {
// Simuler un préchargement après un délai
setTimeout(async () => {
const module = await this.loadModule(moduleName);
this.preloadedModules.set(moduleName, module);
this.preloadQueue.delete(moduleName);
}, 2000);
} catch (error) {
console.error(`Erreur de préchargement pour ${moduleName}:`, error);
this.preloadQueue.delete(moduleName);
}
}
// Précharger en fonction de l'utilisateur
async preloadForUser(userRole: string): Promise<void> {
const modules = this.getModulesForRole(userRole);
await Promise.all(modules.map((module) => this.preload(module)));
}
private getModulesForRole(role: string): string[] {
switch (role) {
case 'admin':
return ['admin', 'reporting', 'analytics'];
case 'user':
return ['dashboard', 'profile'];
default:
return ['home'];
}
}
private async loadModule(moduleName: string) {
// Implémentation du chargement...
return {};
}
}
export const preloader = new Preloader();
3.6 Gestion des Dépendances
Types pour les Packages Externes
TypeScript utilise DefinitelyTyped pour fournir les types des packages JavaScript :
# Installation des types pour un package
npm install --save-dev @types/node
npm install --save-dev @types/react
npm install --save-dev @types/lodash
# Installation globale des types
npm install -g @types/node
Déclaration de Types Personnalisés
Pour les packages sans types disponibles ou pour les projets internes :
// types/global.d.ts
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
API_URL: string;
DATABASE_URL: string;
JWT_SECRET: string;
}
}
interface Window {
gtag: (command: string, targetId: string, config?: any) => void;
dataLayer: any[];
}
}
export {};
// types/vendor.d.ts
// Déclaration pour une librairie sans types
declare module 'custom-lib' {
export function customFunction(input: string): number;
export class CustomClass {
constructor(options: { name: string; value: number });
getName(): string;
getValue(): number;
}
}
// Extension des types existants
declare module 'lodash' {
interface LoDashStatic {
customMethod<T>(array: T[], predicate: (item: T) => boolean): T[];
}
}
Gestion des Versions de Types
// types/versions.ts
// Gestion des versions compatibles
export const COMPATIBLE_VERSIONS = {
react: '^18.0.0',
'react-dom': '^18.0.0',
typescript: '^5.0.0',
vite: '^5.0.0',
} as const;
// Vérification de compatibilité
export function checkCompatibility(packageName: string, version: string): boolean {
const compatibleVersion = COMPATIBLE_VERSIONS[packageName as keyof typeof COMPATIBLE_VERSIONS];
if (!compatibleVersion) {
console.warn(`Version non spécifiée pour ${packageName}`);
return true;
}
// Logique de vérification de version...
return true;
}
3.7 Performance et Optimisation
Optimisation de la Compilation
// vite.config.ts - Optimisation avancée
export default defineConfig({
plugins: [react()],
// Optimisation du build
build: {
target: 'es2024',
minify: 'esbuild',
sourcemap: true,
// Configuration du chunking
rollupOptions: {
output: {
// Chunking par vendor
manualChunks: {
'react-vendor': ['react', 'react-dom'],
router: ['react-router-dom'],
utils: ['lodash', 'date-fns', 'clsx'],
ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
},
// Noms de fichiers optimisés
chunkFileNames: 'assets/[name]-[hash].js',
entryFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]',
},
},
// Optimisation du tree shaking
treeshake: {
preset: 'recommended',
moduleSideEffects: false,
},
},
// Optimisation des dépendances
optimizeDeps: {
include: ['react', 'react-dom', 'react-router-dom'],
exclude: ['@types/react'],
},
});
Analyse des Bundles
// scripts/analyze-bundle.js
import { analyzeBundle } from 'vite-bundle-analyzer';
import { build } from 'vite';
async function analyze() {
const result = await build({
configFile: 'vite.config.ts',
mode: 'production',
});
if (result instanceof Error) {
console.error('Erreur de build:', result);
return;
}
await analyzeBundle({
bundleDir: './dist',
reportFilename: './bundle-analysis.html',
open: true,
});
}
analyze();
Monitoring des Performances
// utils/performance.ts
interface PerformanceMetrics {
buildTime: number;
bundleSize: number;
chunkCount: number;
dependenciesCount: number;
}
class PerformanceMonitor {
private metrics: PerformanceMetrics = {
buildTime: 0,
bundleSize: 0,
chunkCount: 0,
dependenciesCount: 0,
};
startBuild(): void {
this.metrics.buildTime = Date.now();
}
endBuild(): void {
this.metrics.buildTime = Date.now() - this.metrics.buildTime;
this.reportMetrics();
}
setBundleSize(size: number): void {
this.metrics.bundleSize = size;
}
setChunkCount(count: number): void {
this.metrics.chunkCount = count;
}
setDependenciesCount(count: number): void {
this.metrics.dependenciesCount = count;
}
private reportMetrics(): void {
console.table(this.metrics);
// Alertes de performance
if (this.metrics.bundleSize > 1024 * 1024) {
// > 1MB
console.warn('⚠️ Bundle size > 1MB - Consider code splitting');
}
if (this.metrics.chunkCount > 50) {
console.warn('⚠️ Too many chunks - Consider bundling strategy');
}
}
}
export const performanceMonitor = new PerformanceMonitor();
3.8 Tests et Qualité
Configuration des Tests avec TypeScript
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.+(ts|tsx|js)', '**/*.(test|spec).+(ts|tsx|js)'],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
},
collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/**/*.d.ts', '!src/index.ts'],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
moduleNameMapping: {
'^@/(.*)$': '<rootDir>/src/$1',
},
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
};
Tests de Modules
// __tests__/utils/math.test.ts
import { addition, multiplication, PI } from '@/utils/math';
describe('Math Utils', () => {
describe('addition', () => {
it('should add two positive numbers', () => {
expect(addition(2, 3)).toBe(5);
});
it('should handle negative numbers', () => {
expect(addition(-2, 3)).toBe(1);
});
it('should return correct type', () => {
const result = addition(1, 2);
expect(typeof result).toBe('number');
});
});
describe('multiplication', () => {
it('should multiply two numbers', () => {
expect(multiplication(3, 4)).toBe(12);
});
});
describe('PI constant', () => {
it('should have correct value', () => {
expect(PI).toBeCloseTo(3.14159, 5);
});
});
});
🎯 TP 3 : Application Modulaire avec Architecture Micro-Frontend
Objectifs
- Maîtriser les modules ES et l’architecture modulaire
- Configurer un build tool moderne (Vite)
- Implémenter le code splitting et le lazy loading
- Créer une architecture micro-frontend
- Optimiser les performances de chargement
Contexte du Projet
Vous allez créer une application d’entreprise avec une architecture micro-frontend. L’application sera composée de plusieurs modules indépendants qui peuvent être développés et déployés séparément.
Architecture Cible
Structure des Modules
micro-frontend-app/
├── packages/
│ ├── shared/
│ │ ├── types/
│ │ ├── utils/
│ │ └── components/
│ ├── shell/
│ │ ├── src/
│ │ └── public/
│ ├── auth/
│ │ ├── src/
│ │ └── public/
│ ├── dashboard/
│ │ ├── src/
│ │ └── public/
│ └── admin/
│ ├── src/
│ └── public/
├── apps/
│ ├── web/
│ └── mobile/
├── tools/
│ ├── build/
│ └── deploy/
└── docs/
Fonctionnalités Requises
1. Module Shared
Créer un module partagé avec les types et utilitaires communs :
// packages/shared/types/user.ts
export interface Utilisateur {
id: string;
email: string;
nom: string;
prenom: string;
role: RoleUtilisateur;
permissions: Permission[];
dateCreation: Date;
derniereConnexion?: Date;
}
export type RoleUtilisateur = 'admin' | 'manager' | 'user' | 'guest';
export type Permission = 'read' | 'write' | 'delete' | 'admin';
// packages/shared/utils/auth.ts
export interface AuthState {
utilisateur: Utilisateur | null;
token: string | null;
isAuthenticated: boolean;
isLoading: boolean;
}
export interface AuthService {
login(email: string, password: string): Promise<AuthState>;
logout(): Promise<void>;
refreshToken(): Promise<string>;
hasPermission(permission: Permission): boolean;
}
// packages/shared/components/Button/Button.tsx
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
icon?: React.ReactNode;
fullWidth?: boolean;
}
2. Module Shell
Créer le shell principal qui orchestre les micro-frontends :
// packages/shell/src/App.tsx
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { MicroFrontendLoader } from './components/MicroFrontendLoader';
const AuthModule = lazy(() => import('auth/App'));
const DashboardModule = lazy(() => import('dashboard/App'));
const AdminModule = lazy(() => import('admin/App'));
const App: React.FC = () => {
return (
<BrowserRouter>
<Suspense fallback={<div>Chargement...</div>}>
<Routes>
<Route path="/auth/*" element={<AuthModule />} />
<Route path="/dashboard/*" element={<DashboardModule />} />
<Route path="/admin/*" element={<AdminModule />} />
<Route path="/" element={<MicroFrontendLoader module="home" />} />
</Routes>
</Suspense>
</BrowserRouter>
);
};
export default App;
3. Configuration des Build Tools
Configurer Vite pour chaque module avec optimisation :
// vite.config.ts (root)
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
shared: ['@mon-app/shared'],
},
},
},
},
resolve: {
alias: {
'@shared': resolve(__dirname, 'packages/shared/src'),
},
},
optimizeDeps: {
include: ['@mon-app/shared'],
},
});
4. Système de Communication Inter-Modules
Implémenter un système de communication entre les micro-frontends :
// packages/shared/utils/eventBus.ts
export interface EventBusEvent {
type: string;
payload: any;
timestamp: number;
source: string;
}
class EventBus {
private listeners = new Map<string, Set<(event: EventBusEvent) => void>>();
subscribe(eventType: string, callback: (event: EventBusEvent) => void): () => void {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, new Set());
}
this.listeners.get(eventType)!.add(callback);
// Retourner une fonction de désabonnement
return () => {
this.listeners.get(eventType)?.delete(callback);
};
}
publish(event: Omit<EventBusEvent, 'timestamp'>): void {
const fullEvent: EventBusEvent = {
...event,
timestamp: Date.now(),
};
const callbacks = this.listeners.get(event.type);
if (callbacks) {
callbacks.forEach((callback) => callback(fullEvent));
}
}
// Communication spécifique entre modules
sendToModule(targetModule: string, event: Omit<EventBusEvent, 'source'>): void {
this.publish({
...event,
source: targetModule,
});
}
}
export const eventBus = new EventBus();
5. Gestion des États Globaux
Créer un système de gestion d’état partagé :
// packages/shared/store/globalStore.ts
export interface GlobalState {
utilisateur: Utilisateur | null;
theme: 'light' | 'dark';
notifications: Notification[];
loading: Record<string, boolean>;
}
export interface Notification {
id: string;
type: 'success' | 'error' | 'warning' | 'info';
message: string;
timestamp: number;
read: boolean;
}
class GlobalStore {
private state: GlobalState = {
utilisateur: null,
theme: 'light',
notifications: [],
loading: {},
};
private subscribers = new Set<(state: GlobalState) => void>();
getState(): GlobalState {
return { ...this.state };
}
setState(updates: Partial<GlobalState>): void {
this.state = { ...this.state, ...updates };
this.notifySubscribers();
}
subscribe(callback: (state: GlobalState) => void): () => void {
this.subscribers.add(callback);
callback(this.getState());
return () => this.subscribers.delete(callback);
}
private notifySubscribers(): void {
this.subscribers.forEach((callback) => callback(this.getState()));
}
// Actions spécifiques
setUtilisateur(utilisateur: Utilisateur | null): void {
this.setState({ utilisateur });
}
addNotification(notification: Omit<Notification, 'id' | 'timestamp'>): void {
const newNotification: Notification = {
...notification,
id: Date.now().toString(),
timestamp: Date.now(),
read: false,
};
this.setState({
notifications: [...this.state.notifications, newNotification],
});
}
setLoading(key: string, loading: boolean): void {
this.setState({
loading: { ...this.state.loading, [key]: loading },
});
}
}
export const globalStore = new GlobalStore();
Instructions d’Implémentation
Phase 1 : Structure Initiale
- Créer la structure monorepo avec les packages de base
- Configurer TypeScript pour les workspaces
- Mettre en place Vite pour chaque module
- Créer le module shared avec types et utilitaires
Phase 2 : Module Shell
- Développer le shell principal avec routing
- Implémenter le lazy loading des modules
- Créer le système de communication inter-modules
- Ajouter la gestion des états globaux
Phase 3 : Modules Spécifiques
- Développer le module auth avec login/logout
- Créer le module dashboard avec widgets
- Implémenter le module admin avec gestion
- Ajouter les tests pour chaque module
Phase 4 : Optimisation
- Optimiser le bundle splitting
- Implémenter le préchargement intelligent
- Ajouter le monitoring des performances
- Configurer le déploiement indépendant
Étapes Détaillées
Étape 1 : Configuration du Monorepo
# Initialisation du projet
npm init -y
# Installation des dépendances de développement
npm install -D typescript vite @vitejs/plugin-react
npm install -D @types/node @types/react @types/react-dom
# Configuration des workspaces
npm install -D lerna nx
# Création de la structure
mkdir -p packages/{shared,shell,auth,dashboard,admin}
mkdir -p apps/{web,mobile}
Étape 2 : Module Shared
- Créer les interfaces et types partagés
- Développer les utilitaires communs
- Créer les composants réutilisables
- Ajouter les tests unitaires
Étape 3 : Configuration Vite
- Configurer Vite pour chaque module
- Mettre en place le code splitting
- Optimiser les dépendances
- Ajouter les source maps
Étape 4 : Communication Inter-Modules
- Implémenter le système d’événements
- Créer le store global
- Ajouter la sécurité inter-modules
- Tester la communication
Critères de Réussite
✅ Architecture Modulaire
- Structure monorepo bien organisée
- Modules indépendants et réutilisables
- Communication inter-modules efficace
- Dépendances clairement définies
✅ Performance
- Build rapide avec Vite
- Code splitting optimisé
- Lazy loading fonctionnel
- Bundle size minimal
✅ Qualité du Code
- TypeScript strict activé
- Tests unitaires complets
- Couverture de code > 80%
- Documentation complète
✅ Déploiement
- Déploiement indépendant des modules
- CI/CD configuré
- Monitoring en place
- Rollback automatique
Extensions Possibles
1. Module Federation
- Intégration avec Webpack Module Federation
- Partage de dépendances entre modules
- Runtime dynamic loading
2. Edge Computing
- Déploiement sur CDN edge
- Fonctions serverless par module
- Optimisation globale
3. Testing Avancé
- Tests E2E inter-modules
- Tests de charge distribués
- Tests de compatibilité
4. Sécurité
- Authentification centralisée
- Permissions granulaires
- Audit de sécurité
5. Monitoring
- Métriques par module
- Alertes automatiques
- Dashboard de monitoring
Ressources Complémentaires
Documentation
Outils
- Lerna : Gestion de monorepos
- Nx : Outils de développement monorepo
- Bundle Analyzer : Analyse des bundles
Bonnes Pratiques
- Modules petits et focalisés
- Interfaces claires et stables
- Tests automatisés continus
- Documentation des APIs
Cette leçon explore l’architecture modulaire moderne avec TypeScript, permettant de créer des applications scalables et performantes grâce aux modules et aux build tools optimisés.
Prêt à passer à la suite ?
Vous avez terminé cette leçon sur 03 - Modules et Build Tools : Architecture Moderne et Performance. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.