Builders Academy
SOLID — Les 5 Principes de la Conception OO
3 of 3

Chapitre 1 · Leçon 3

Dependency Inversion

Dépendre des abstractions, pas des implémentations

Section 1

Inverser les dépendances

Step 1

Le problème des couplages directs

DIP : les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Tous deux doivent dépendre d'abstractions.

snippet.typescript

typescript
1// ❌ Violation DIP — couplage fort
2class UserService {
3  // UserService dépend directement d'une implémentation concrète
4  private repo = new PostgresUserRepository();   // ← couplage direct
5  private mailer = new SendgridEmailService();    // ← couplage direct
6
7  async register(dto: RegisterDTO): Promise<User> {
8    const user = await this.repo.save(dto);
9    await this.mailer.sendWelcome(user.email);
10    return user;
11  }
12  // Impossible de tester sans une vraie base de données et Sendgrid !
13}
14
15// ✅ Respect DIP — interfaces + injection
16interface IUserRepository {
17  save(dto: RegisterDTO): Promise<User>;
18  findByEmail(email: string): Promise<User | null>;
19}
20
21interface IEmailService {
22  sendWelcome(email: string): Promise<void>;
23}
24
25class UserService {
26  // Dépend des abstractions (interfaces), pas des implémentations
27  constructor(
28    private readonly repo: IUserRepository,
29    private readonly mailer: IEmailService,
30  ) {}
31
32  async register(dto: RegisterDTO): Promise<User> {
33    const existing = await this.repo.findByEmail(dto.email);
34    if (existing) throw new ConflictError('Email déjà utilisé');
35    const user = await this.repo.save(dto);
36    await this.mailer.sendWelcome(user.email);
37    return user;
38  }
39}
40
41// En production
42const service = new UserService(
43  new PostgresUserRepository(db),
44  new SendgridEmailService(apiKey),
45);
46
47// En test : on injecte des fakes
48const service = new UserService(
49  new InMemoryUserRepository(),
50  new FakeEmailService(),
51);

snippet.typescript

typescript
1// ❌ Violation DIP — couplage fort
2class UserService {
3  // UserService dépend directement d'une implémentation concrète
4  private repo = new PostgresUserRepository();   // ← couplage direct
5  private mailer = new SendgridEmailService();    // ← couplage direct
6
7  async register(dto: RegisterDTO): Promise<User> {
8    const user = await this.repo.save(dto);
9    await this.mailer.sendWelcome(user.email);
10    return user;
11  }
12  // Impossible de tester sans une vraie base de données et Sendgrid !
13}
14
15// ✅ Respect DIP — interfaces + injection
16interface IUserRepository {
17  save(dto: RegisterDTO): Promise<User>;
18  findByEmail(email: string): Promise<User | null>;
19}
20
21interface IEmailService {
22  sendWelcome(email: string): Promise<void>;
23}
24
25class UserService {
26  // Dépend des abstractions (interfaces), pas des implémentations
27  constructor(
28    private readonly repo: IUserRepository,
29    private readonly mailer: IEmailService,
30  ) {}
31
32  async register(dto: RegisterDTO): Promise<User> {
33    const existing = await this.repo.findByEmail(dto.email);
34    if (existing) throw new ConflictError('Email déjà utilisé');
35    const user = await this.repo.save(dto);
36    await this.mailer.sendWelcome(user.email);
37    return user;
38  }
39}
40
41// En production
42const service = new UserService(
43  new PostgresUserRepository(db),
44  new SendgridEmailService(apiKey),
45);
46
47// En test : on injecte des fakes
48const service = new UserService(
49  new InMemoryUserRepository(),
50  new FakeEmailService(),
51);

Topics and skills covered

  • SOLID
  • DIP
  • Injection de dépendances
  • IoC