💉 OOP trong NestJS (Phần 4): Dependency Injection
🎯 Mục tiêu: Hiểu bản chất DI, IoC Container, và hoàn thiện inject
IUserRepositoryvàoUserService.
📌 Recap từ Phần 1-3
| Phần | Đã học | UserService evolution |
|---|---|---|
| 1 | Class vs Function | Functional → Class-based |
| 2 | Encapsulation, Decorators | Thêm @Injectable(), private methods |
| 3 | Interface, Abstract Class | Tạo IUserRepository, BaseRepository |
Vấn đề cuối Phần 3: Làm sao inject IUserRepository vào UserService?
@Injectable()
export class UserService {
constructor(
private readonly userRepository: IUserRepository // ← Làm sao inject?
) {}
}
1. TẠI SAO CẦN DEPENDENCY INJECTION?
1.1 Vấn đề với Manual Instantiation
// ❌ Không có DI - Hard dependencies
class UserService {
private userRepository: InMemoryUserRepository;
private logger: ConsoleLogger;
private emailService: SendGridEmailService;
constructor() {
// Tự tạo dependencies
this.userRepository = new InMemoryUserRepository();
this.logger = new ConsoleLogger();
this.emailService = new SendGridEmailService('api-key-hardcoded');
}
async create(dto: CreateUserDto) {
this.logger.log('Creating user...');
const user = await this.userRepository.create(dto);
await this.emailService.send(user.email, 'Welcome!');
return user;
}
}
// Khi test:
const service = new UserService();
// ❌ Không thể mock userRepository
// ❌ Không thể mock emailService (thực sự gửi email!)
// ❌ Muốn đổi sang PostgreSQL? Phải sửa code!
1.2 Với Dependency Injection
// ✅ Có DI - Dependencies được inject
class UserService {
constructor(
private userRepository: IUserRepository,
private logger: ILogger,
private emailService: IEmailService
) {}
async create(dto: CreateUserDto) {
this.logger.log('Creating user...');
const user = await this.userRepository.create(dto);
await this.emailService.send(user.email, 'Welcome!');
return user;
}
}
// Production:
const service = new UserService(
new PostgresUserRepository(db),
new CloudWatchLogger(),
new SendGridEmailService(process.env.SENDGRID_KEY)
);
// Testing:
const service = new UserService(
new MockUserRepository(),
new MockLogger(),
new MockEmailService() // Không gửi email thật!
);
1.3 Lợi ích của DI
| Lợi ích | Giải thích |
|---|---|
| Loose Coupling | UserService không biết về implementation cụ thể |
| Testability | Dễ dàng inject mocks |
| Flexibility | Đổi implementation không sửa code |
| Single Responsibility | Tạo dependencies không phải việc của UserService |