Brain-Storm uses NestJS's built-in dependency injection system combined with a custom DI container for advanced scenarios.
import { Injectable } from '@nestjs/common';
@Injectable()
export class CourseService {
constructor(
private readonly courseRepository: CourseRepository,
private readonly validationService: ValidationService
) {}
async create(dto: CreateCourseDto) {
// Services are automatically injected
return this.courseRepository.save(dto);
}
}import { Inject } from '@nestjs/common';
@Injectable()
export class CourseService {
@Inject(CourseRepository)
private courseRepository: CourseRepository;
}import { Optional } from '@nestjs/common';
@Injectable()
export class CourseService {
constructor(@Optional() private readonly cacheService?: CacheService) {}
}import { Module } from '@nestjs/common';
import { CourseService } from './services/course.service';
import { CourseController } from './controllers/course.controller';
@Module({
controllers: [CourseController],
providers: [CourseService],
exports: [CourseService], // Export for other modules
})
export class CourseModule {}@Module({
imports: [TypeOrmModule.forFeature([Course]), ValidationModule],
controllers: [CourseController],
providers: [CourseService],
exports: [CourseService],
})
export class CourseModule {}import { Global, Module } from '@nestjs/common';
@Global()
@Module({
providers: [ValidationService],
exports: [ValidationService],
})
export class ValidationModule {}@Module({
providers: [CourseService],
})
export class CourseModule {}
// Or explicit
@Module({
providers: [
{
provide: CourseService,
useClass: CourseService,
},
],
})
export class CourseModule {}const mockCourseService = {
create: jest.fn(),
};
@Module({
providers: [
{
provide: CourseService,
useValue: mockCourseService,
},
],
})
export class CourseModule {}@Module({
providers: [
{
provide: CourseService,
useFactory: (repository: CourseRepository) => {
return new CourseService(repository);
},
inject: [CourseRepository],
},
],
})
export class CourseModule {}@Module({
providers: [
{
provide: DatabaseConnection,
useFactory: async () => {
return await createDatabaseConnection();
},
},
],
})
export class DatabaseModule {}For advanced scenarios, use the custom DI container:
import { DIContainer } from '@common/di';
@Injectable()
export class MyService {
constructor(private diContainer: DIContainer) {}
registerService() {
this.diContainer.register('MyService', new MyService());
}
getService() {
return this.diContainer.get('MyService');
}
}// Register instance
container.register('CourseService', courseService);
// Register singleton
container.registerSingleton('CourseService', () => new CourseService());
// Get service
const service = container.get('CourseService');
// Check if registered
if (container.has('CourseService')) {
// ...
}
// Remove service
container.remove('CourseService');
// Clear all
container.clear();Use sparingly - prefer constructor injection:
import { ServiceLocator } from '@common/di';
@Injectable()
export class MyService {
constructor(private serviceLocator: ServiceLocator) {}
doSomething() {
const courseService = this.serviceLocator.getService('CourseService');
// ...
}
}// Good
@Injectable()
export class CourseService {
constructor(private repository: CourseRepository) {}
}
// Avoid
@Injectable()
export class CourseService {
@Inject(CourseRepository)
private repository: CourseRepository;
}@Module({
providers: [CourseService],
exports: [CourseService], // Allow other modules to use
})
export class CourseModule {}export interface ICourseRepository {
create(dto: CreateCourseDto): Promise<Course>;
findOne(id: string): Promise<Course>;
}
@Injectable()
export class CourseService {
constructor(private repository: ICourseRepository) {}
}// Problem: Circular dependency
// CourseModule imports UserModule
// UserModule imports CourseModule
// Solution: Use forwardRef
@Module({
imports: [forwardRef(() => UserModule)],
})
export class CourseModule {}import { ConfigService } from '@nestjs/config';
@Injectable()
export class CourseService {
constructor(private configService: ConfigService) {}
getSetting() {
return this.configService.get('COURSE_SETTING');
}
}describe('CourseService', () => {
let service: CourseService;
let repository: CourseRepository;
beforeEach(() => {
repository = {
create: jest.fn(),
findOne: jest.fn(),
} as any;
service = new CourseService(repository);
});
it('should create course', async () => {
const dto = { title: 'Test' };
await service.create(dto);
expect(repository.create).toHaveBeenCalledWith(dto);
});
});describe('CourseModule', () => {
let app: INestApplication;
let courseService: CourseService;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [CourseModule, DatabaseModule],
}).compile();
app = moduleFixture.createNestApplication();
courseService = moduleFixture.get<CourseService>(CourseService);
});
it('should create course', async () => {
const result = await courseService.create({ title: 'Test' });
expect(result).toBeDefined();
});
});@Injectable()
export class CourseRepository {
constructor(
@InjectRepository(Course)
private repository: Repository<Course>
) {}
async create(dto: CreateCourseDto): Promise<Course> {
return this.repository.save(dto);
}
}
@Injectable()
export class CourseService {
constructor(private repository: CourseRepository) {}
async create(dto: CreateCourseDto): Promise<Course> {
return this.repository.create(dto);
}
}export interface PaymentStrategy {
pay(amount: number): Promise<void>;
}
@Injectable()
export class StripePayment implements PaymentStrategy {
async pay(amount: number): Promise<void> {
// Stripe implementation
}
}
@Injectable()
export class PaymentService {
constructor(private strategy: PaymentStrategy) {}
async processPayment(amount: number): Promise<void> {
return this.strategy.pay(amount);
}
}@Injectable()
export class ServiceFactory {
create(type: string): any {
switch (type) {
case 'course':
return new CourseService();
case 'user':
return new UserService();
default:
throw new Error(`Unknown service type: ${type}`);
}
}
}Solution: Ensure module is imported in parent module
@Module({
imports: [CourseModule], // Add missing import
controllers: [AppController],
})
export class AppModule {}Solution: Use forwardRef or reorganize modules
@Module({
imports: [forwardRef(() => UserModule)],
})
export class CourseModule {}Solution: Ensure provider is registered and exported
@Module({
providers: [CourseService],
exports: [CourseService], // Add export
})
export class CourseModule {}