← Назад к вопросам
Что позволяет пользоваться полиморфизмом в NestJS?
2.2 Middle🔥 121 комментариев
#ООП#Фреймворки и библиотеки
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Полиморфизм в NestJS и его реализация
Это очень хороший вопрос, показывающий глубокое понимание архитектуры NestJS. Расскажу подробно о механизмах, позволяющих использовать полиморфизм в NestJS.
Основа: Dependency Injection контейнер
NestJS построен на мощной системе Dependency Injection (DI) контейнере, которая позволяет реализовывать полиморфизм через интерфейсы и абстрактные классы.
Механизмы полиморфизма в NestJS
1. Dependency Injection через интерфейсы
@Controller('users')
export class UserController {
constructor(@Inject('IUserService') private userService: IUserService) {}
@Get(':id')
async findUser(@Param('id') id: string) {
return this.userService.findById(id);
}
}
@Module({
providers: [
{
provide: 'IUserService',
useClass: process.env.DB === 'postgres' ? UserService : MongoUserService,
},
],
controllers: [UserController],
})
export class UserModule {}
2. Абстрактные классы (более типобезопасно)
export abstract class BaseUserService {
abstract findById(id: string): Promise<User>;
abstract create(data: CreateUserDto): Promise<User>;
}
@Injectable()
export class PostgresUserService extends BaseUserService {
constructor(private db: Database) {}
async findById(id: string): Promise<User> {
return this.db.query('SELECT * FROM users WHERE id = $1', [id]);
}
}
@Injectable()
export class MongoUserService extends BaseUserService {
constructor(private db: MongoClient) {}
async findById(id: string): Promise<User> {
return this.db.collection('users').findOne({ _id: id });
}
}
@Module({
providers: [
{
provide: BaseUserService,
useClass: PostgresUserService,
},
],
})
export class UserModule {}
3. Factory pattern для динамической инъекции
@Injectable()
export class UserServiceFactory {
create(dbType: 'postgres' | 'mongo'): BaseUserService {
if (dbType === 'postgres') {
return new PostgresUserService(this.pgDb);
} else {
return new MongoUserService(this.mongoDb);
}
}
constructor(
private pgDb: PostgresDatabase,
private mongoDb: MongoClient,
) {}
}
@Module({
providers: [
UserServiceFactory,
{
provide: BaseUserService,
useFactory: (factory: UserServiceFactory) =>
factory.create(process.env.DATABASE),
inject: [UserServiceFactory],
},
],
})
export class UserModule {}
4. Strategy pattern с полиморфизмом
interface PaymentStrategy {
pay(amount: number, orderId: string): Promise<PaymentResult>;
}
@Injectable()
export class StripePaymentStrategy implements PaymentStrategy {
async pay(amount: number, orderId: string): Promise<PaymentResult> {
// Интеграция с Stripe
}
}
@Injectable()
export class PayPalPaymentStrategy implements PaymentStrategy {
async pay(amount: number, orderId: string): Promise<PaymentResult> {
// Интеграция с PayPal
}
}
@Injectable()
export class PaymentService {
constructor(
@Inject('PAYMENT_STRATEGY') private strategy: PaymentStrategy,
) {}
async processPayment(amount: number, orderId: string) {
return this.strategy.pay(amount, orderId);
}
}
@Module({
providers: [
{
provide: 'PAYMENT_STRATEGY',
useClass: StripePaymentStrategy,
},
PaymentService,
],
})
export class PaymentModule {}
5. Использование Token провайдеров (рекомендуемый подход)
export const USER_SERVICE_TOKEN = Symbol('USER_SERVICE');
@Controller('users')
export class UserController {
constructor(@Inject(USER_SERVICE_TOKEN) private userService: IUserService) {}
}
@Module({
providers: [
{
provide: USER_SERVICE_TOKEN,
useClass: process.env.DB === 'postgres' ? UserService : MongoUserService,
},
],
})
export class UserModule {}
6. Conditional providers для окружения
@Module({
providers: [
{
provide: 'DATABASE_SERVICE',
useClass: process.env.NODE_ENV === 'production'
? PostgresDatabaseService
: InMemoryDatabaseService,
},
],
})
export class DatabaseModule {}
Практический пример: Полная архитектура
interface IEmailProvider {
send(to: string, subject: string, body: string): Promise<void>;
}
@Injectable()
export class SendGridEmailProvider implements IEmailProvider {
async send(to: string, subject: string, body: string) {
// SendGrid реализация
}
}
@Injectable()
export class MailgunEmailProvider implements IEmailProvider {
async send(to: string, subject: string, body: string) {
// Mailgun реализация
}
}
@Injectable()
export class NotificationService {
constructor(private emailProvider: IEmailProvider) {}
async notifyUser(userId: string, message: string) {
const user = await this.userService.findById(userId);
await this.emailProvider.send(user.email, 'Notification', message);
}
}
@Module({
providers: [
{
provide: 'EMAIL_PROVIDER',
useClass: process.env.EMAIL_PROVIDER === 'sendgrid'
? SendGridEmailProvider
: MailgunEmailProvider,
},
NotificationService,
],
})
export class NotificationModule {}
Ключевые преимущества полиморфизма в NestJS
- Flexibility — легко менять реализации без изменения потребителей
- Testability — можно создавать mock реализации для тестов
- Maintainability — чистое разделение ответственности
- Scalability — легко добавлять новые реализации
- Type Safety — полная поддержка TypeScript типов
Выводы
Полиморфизм в NestJS возможен благодаря мощной системе Dependency Injection контейнера, который позволяет инъектировать разные реализации интерфейсов и абстрактных классов. Это основа архитектуры NestJS и позволяет писать гибкий, тестируемый и поддерживаемый код. Ключевые механизмы: интерфейсы, абстрактные классы, провайдеры с useClass, factory функции и Symbol токены.