Tìm Hiểu Circular Dependency Trong NestJS
Circular Dependency (phụ thuộc vòng tròn) là một vấn đề thường gặp trong lập trình, đặc biệt là khi làm việc với NestJS. Đây là một khái niệm không chỉ quan trọng mà còn thường xuyên xuất hiện trong các cuộc phỏng vấn về NestJS. Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu về circular dependency và những cách giải quyết hiệu quả để xử lý vấn đề này.
Khái Niệm Circular Dependency Trong NestJS
Trong NestJS, circular dependency xảy ra khi hai hoặc nhiều module hoặc provider phụ thuộc vào nhau, tạo thành một vòng lặp. Điều này có thể dẫn đến các vấn đề nghiêm trọng như lỗi runtime hoặc hành vi không mong muốn, vì NestJS không thể giải quyết các phụ thuộc một cách chính xác.
Ví dụ:
- Module A phụ thuộc vào Module B.
- Module B lại phụ thuộc vào Module A.
Ví Dụ Thực Tế Về Circular Dependency
Dưới đây là một ví dụ đơn giản về circular dependency giữa hai service trong NestJS:
service-a.service.ts
typescript
import { Injectable } from '@nestjs/common';
import { ServiceB } from './service-b.service';
@Injectable()
export class ServiceA {
constructor(private readonly serviceB: ServiceB) {}
getMessageFromB(): string {
return this.serviceB.getMessage();
}
}
service-b.service.ts
typescript
import { Injectable } from '@nestjs/common';
import { ServiceA } from './service-a.service';
@Injectable()
export class ServiceB {
constructor(private readonly serviceA: ServiceA) {}
getMessage(): string {
return `Hello from ServiceB. Called by: ${this.serviceA.getMessageFromB()}`;
}
}
Như bạn thấy, cả ServiceA
và ServiceB
đều phụ thuộc lẫn nhau, gây ra vấn đề circular dependency.
Thông Báo Lỗi Thường Gặp
Khi chạy đoạn mã trên, NestJS sẽ trả về một lỗi tương tự như sau:
Error: A circular dependency has been detected (ServiceA -> ServiceB -> ServiceA).
Các Giải Pháp Để Giải Quyết Circular Dependency
1. Sử Dụng forwardRef
NestJS cung cấp hàm forwardRef
để xử lý vấn đề circular dependency. Bạn có thể sử dụng forwardRef
trong constructor để tránh lỗi.
Ví dụ:
service-a.service.ts
typescript
import { Injectable, forwardRef, Inject } from '@nestjs/common';
import { ServiceB } from './service-b.service';
@Injectable()
export class ServiceA {
constructor(@Inject(forwardRef(() => ServiceB)) private readonly serviceB: ServiceB) {}
getMessageFromB(): string {
return this.serviceB.getMessage();
}
}
service-b.service.ts
typescript
import { Injectable, forwardRef, Inject } from '@nestjs/common';
import { ServiceA } from './service-a.service';
@Injectable()
export class ServiceB {
constructor(@Inject(forwardRef(() => ServiceA)) private readonly serviceA: ServiceA) {}
getMessage(): string {
return `Hello from ServiceB. Called by: ${this.serviceA.getMessageFromB()}`;
}
}
2. Tái Cấu Trúc Code Để Tránh Circular Dependency
Một phương pháp khác là tái cấu trúc code để tránh việc các service phụ thuộc trực tiếp vào nhau. Bạn có thể tạo một service trung gian (Mediator) để điều phối các tương tác.
Tạo mediator.service.ts
typescript
import { Injectable } from '@nestjs/common';
@Injectable()
export class MediatorService {
getSharedMessage(): string {
return 'Shared Message';
}
}
Cập Nhật service-a.service.ts
typescript
import { Injectable } from '@nestjs/common';
import { MediatorService } from './mediator.service';
@Injectable()
export class ServiceA {
constructor(private readonly mediatorService: MediatorService) {}
getMessageFromMediator(): string {
return this.mediatorService.getSharedMessage();
}
}
Cập Nhật service-b.service.ts
typescript
import { Injectable } from '@nestjs/common';
import { MediatorService } from './mediator.service';
@Injectable()
export class ServiceB {
constructor(private readonly mediatorService: MediatorService) {}
getMessageFromMediator(): string {
return this.mediatorService.getSharedMessage();
}
}
3. Tổ Chức Lại Cấu Trúc Module
Nếu circular dependency phát sinh từ việc các module import lẫn nhau, hãy cân nhắc tổ chức lại cấu trúc module. Đưa logic dùng chung vào một module riêng để cả hai module có thể cùng phụ thuộc mà không gặp vấn đề về circular dependency.
Tóm Tắt
- Sử dụng
forwardRef
là một cách xử lý nhanh chóng nhưng nên cẩn thận, không nên lạm dụng để tránh làm phức tạp thiết kế ứng dụng. - Cách tốt nhất là tái cấu trúc code để tránh hoàn toàn circular dependency.
- Xem xét sử dụng mediator hoặc tổ chức lại module để quản lý phụ thuộc một cách rõ ràng hơn.
Đọc Thêm
Để tìm hiểu thêm về circular dependency và cách giải quyết, bạn có thể tham khảo tài liệu chính thức của NestJS tại đây. Ngoài ra, bạn có thể xem đoạn code mẫu của mình về cách giải quyết circular dependency trên GitHub. Cùng tham khảo thêm các khái niệm cơ bản trong series NestJS Cơ Bản và bài viết gốc của mình tại đây.
source: viblo