Injection de Dépendances en JavaScript : guide pratique pour Node.js

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : Le problème du couplage fort du code

\n\n

Chaque développeur Node.js rencontre tôt ou tard une situation où une modification dans un module entraîne une cascade de corrections dans une dizaine d'autres. Cela se manifeste particulièrement lors de l'écriture de tests unitaires : pour tester un seul service, il faut démarrer une base de données, des API externes et un cache. La racine du problème est le couplage fort (tight coupling).

\n\n

L'Injection de Dépendances (DI) est un patron de conception qui résout ce problème. Au lieu que la classe ou la fonction crée ses propres dépendances, elle les reçoit de l'extérieur. Cela rend le code flexible, testable et conforme au principe d'inversion des dépendances (Dependency Inversion Principle) de SOLID.

\n\n

Dans cet article, nous allons explorer la DI en pratique : de l'injection manuelle simple à l'utilisation de conteneurs professionnels dans Node.js.

\n\n

1. Injection manuelle des dépendances : premier pas

\n\n

Mauvaise approche : couplage fort

\n

Considérons un exemple typique de service d'envoi d'email :

\n\n
// userService.js\nconst emailService = require('./emailService');\n\nclass UserService {\n  async registerUser(email) {\n    // Dépendance forte à une implémentation spécifique\n    await emailService.sendWelcomeEmail(email);\n    console.log('Utilisateur enregistré');\n  }\n}\n\nmodule.exports = UserService;
\n\n

Problème : lors du test de UserService, il est impossible de remplacer emailService par un mock. Il faudrait utiliser des outils complexes comme proxyquire ou jest.mock.

\n\n

Bonne approche : injection via le constructeur

\n

Réécrivons le service en passant la dépendance via le constructeur :

\n\n
// userService.js\nclass UserService {\n  constructor(emailService) {\n    this.emailService = emailService;\n  }\n\n  async registerUser(email) {\n    await this.emailService.sendWelcomeEmail(email);\n    console.log('Utilisateur enregistré');\n  }\n}\n\nmodule.exports = UserService;
\n\n

Maintenant, nous pouvons facilement tester le service :

\n\n
// userService.test.js\nconst UserService = require('./userService');\n\nconst mockEmailService = {\n  sendWelcomeEmail: jest.fn().mockResolvedValue(true)\n};\n\ntest('doit envoyer un email de bienvenue', async () => {\n  const service = new UserService(mockEmailService);\n  await service.registerUser('test@test.com');\n  expect(mockEmailService.sendWelcomeEmail).toHaveBeenCalledWith('test@test.com');\n});
\n\n

2. Types d'Injection de Dépendances en JavaScript

\n\n

En JavaScript/Node.js, il existe trois principales façons d'injecter des dépendances :

\n\n

2.1. Injection via le constructeur

\n

La méthode la plus courante et prévisible. Les dépendances sont passées lors de la création de l'objet :

\n\n
class DatabaseService {\n  constructor(connection) {\n    this.connection = connection;\n  }\n\n  async query(sql) {\n    return this.connection.execute(sql);\n  }\n}\n\n// Utilisation\nconst db = new DatabaseService(new MySQLConnection());
\n\n

2.2. Injection via les setters

\n

Convient lorsque les dépendances peuvent être modifiées pendant le fonctionnement de l'objet :

\n\n
class LoggerService {\n  setLogger(logger) {\n    this.logger = logger;\n  }\n\n  log(message) {\n    this.logger.write(`[${new Date().toISOString()}] ${message}`);\n  }\n}\n\nconst logger = new LoggerService();\nlogger.setLogger(new ConsoleLogger());\nlogger.log('Application démarrée');
\n\n

2.3. Injection via les paramètres de fonction

\n

Idéal pour l'approche fonctionnelle et les utilitaires :

\n\n
function sendNotification(user, emailService, smsService) {\n  emailService.send(user.email, 'Notification');\n  smsService.send(user.phone, 'Notification');\n}\n\n// Test\nconst mockEmail = { send: jest.fn() };\nconst mockSms = { send: jest.fn() };\nsendNotification({ email: 'a@b.com', phone: '123' }, mockEmail, mockSms);
\n\n

3.

Blogs

Book Recommendations