Chapitre 1 · Leçon 3
Dépendre des abstractions, pas des implémentations
Section 1
Step 1
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
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);