NestJS — это мощная платформа, которая превосходно реализует внедрение зависимостей. Однако, когда дело доходит до индивидуальных сценариев, таких как динамическое переключение источников данных, нам нужно копнуть глубже и изучить наши варианты.

Испытание

Представьте себе сценарий, в котором у вас есть несколько источников данных, таких как базы данных или внешние API, и вам необходимо динамически переключаться между ними на основе параметров запроса в URL-адресе.

Это может быть легко реализовано путем переключения источника данных в методе в контроллере, но это уводит нас от принципов внедрения зависимостей, особенно от того, что контроллер не должен нести ответственность за принятие решения о том, какую службу вызывать.

Следовательно, нам нужно решение, которое динамически выбирает соответствующий источник данных на основе параметров запроса.

Решение

Мы будем использовать шаблон Factory Provider, который позволяет нам динамически создавать и предоставлять экземпляры сервисов в зависимости от условий выполнения. Комбинируя этот шаблон с подходящей конфигурацией модуля и внедрением зависимостей, мы можем беспрепятственно переключать источники данных без ущерба для преимуществ внедрения зависимостей.

Настраивать

1- Создайте новое приложение NestJS с помощью CLI.

nest new test01

# In this demo we have used yarn in the next step 
# where it asks for the package manager selection

2- Создайте фабричный модуль, который будет отвечать за предоставление правильного источника данных, назовем его DynDS

# Ensure to run this within the `test01` project folder
# g is short for generate
# mo is short for module

nest g mo dynds

2- Создайте модуль API, который будет использовать DynDS

nest g mo api
nest g co api

Выполнение

Создайте интерфейс для служб;
что гарантирует, что все службы имеют одинаковую структуру, а затем будут использоваться в контроллере для обеспечения безопасности типов и завершения кода (создайте любые папки, если они не существуют):

// src/dynds/interfaces/datasource.interface.ts;

export interface DataSource {
    getData(): any;
}

Создайте сервисы (это примеры, в реальных сценариях они могут быть получены из других модулей)

// src/dynds/services/source01.service.ts

import { Injectable } from '@nestjs/common';
import { DataSource } from '../interfaces/datasource.interface';

@Injectable()
export class Source01Service implements DataSource {
    getData() {
        return 'Data from Source01';
    }
}
// src/dynds/services/source02.service.ts

import { Injectable } from '@nestjs/common';
import { DataSource } from '../interfaces/datasource.interface';

@Injectable()
export class Source02Service implements DataSource {
    getData() {
        return 'Data from Source02';
    }
}
// src/dynds/services/source03.service.ts

import { Injectable } from '@nestjs/common';
import { DataSource } from '../interfaces/datasource.interface';

@Injectable()
export class Source03Service implements DataSource {
    getData() {
        return 'Data from Source03';
    }
}

Создайте токен для модуля, он просто содержит имя модуля, он будет использоваться для объявления нашего пользовательского провайдера в следующих нескольких шагах.

// src/dynds/constants/dynds.constants.ts

export const DataSourceToken = 'DynDS';

Теперь мы создадим основной модуль Factory:

// src/dynds/dynds.factory.ts

import { Injectable, Inject, Scope } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
import { Source01Service } from './services/source01.service';
import { Source02Service } from './services/source02.service';
import { Source03Service } from './services/source03.service';

@Injectable({ scope: Scope.REQUEST })
export class DataSourceFactory {
    constructor(
        @Inject(REQUEST) private request: Request,
        private source01Service: Source01Service,
        private source02Service: Source02Service,
        private source03Service: Source03Service,
    ) {}

    create() {
        const sourceParam = this.request?.query?.source;
        switch (sourceParam) {
            case 'source01':
                return this.source01Service;
            case 'source02':
                return this.source02Service;
            case 'source03':
                return this.source03Service;
            default:
                throw new Error(`Invalid data source: ${sourceParam}`);
        }
    }
}

В приведенном выше классе DataSourceFactory мы создаем экземпляры различных сервисов и переключаем их на основе параметра запроса source в методе create(). Обратите внимание, что объект Request импортируется из express здесь, если вы используете fastify, обновите его соответствующим образом.

Теперь давайте обновим модуль DynDS, чтобы использовать DataSourceFactory.

// src/dynds/dynds.module.ts

import { Module, Scope } from '@nestjs/common';
import { DataSourceToken } from 'src/dynds/constants/dynds.constants';
import { DataSourceFactory } from 'src/dynds/dynds.factory';
import { Source01Service } from './services/source01.service';
import { Source02Service } from './services/source02.service';
import { Source03Service } from './services/source03.service';

@Module({
    providers: [
        {
            provide: DataSourceToken,
            scope: Scope.REQUEST,
            useFactory: (dataSourceFactory: DataSourceFactory) => {
                return dataSourceFactory.create();
            },
            inject: [DataSourceFactory],
        },
        DataSourceFactory,
        Source01Service,
        Source02Service,
        Source03Service,
    ],
    exports: [DataSourceToken],
})
export class DynDSModule {}

Ключевые моменты, которые следует отметить в приведенном выше коде:

  • В провайдерах мы зарегистрировали пользовательский провайдер для NestJS, используя useFactory для регистрации DataSourceFactory.
  • Затем экспортировали наш пользовательский провайдер, используя DataSourceToken (вы, возможно, помните константу, которую мы объявили ранее)
  • Мы предоставили различные классы услуг, например. Source01Service

Наконец, давайте используем созданный нами модуль, мы обновим наш модуль api и его контроллер, внедрив DataSourceToken, чтобы использовать для этого наш модуль DynDS:

// src/api/api.module.ts

import { Module, Scope } from '@nestjs/common';
import { ApiController } from './api.controller';
import { DynDSModule } from 'src/dynds/dynds.module';

@Module({
    imports: [DynDSModule], // Import our DynDS module
    controllers: [ApiController],
})
export class ApiModule {}
// src/api/api.controller.ts

import { Controller, Get, Inject } from '@nestjs/common';
import { DataSourceToken } from 'src/dynds/constants/dynds.constants';
import { DataSource } from 'src/dynds/interfaces/datasource.interface';

@Controller('api')
export class ApiController {
    constructor(@Inject(DataSourceToken) private readonly dataSource: DataSource) {}

    @Get('/data')
    fetchData() {
        return this.dataSource.getData();
    }
}

Вот и все, мы закончили создание нашего демонстрационного приложения.

Тест

Запустите приложение, используя

yarn start:dev

Теперь посетите следующие URL-адреса, которые должны получить ответ от разных служб:

Вы можете найти полный код для этой демонстрации в следующем репозитории:



Ваше здоровье!