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-адреса, которые должны получить ответ от разных служб:
- http://localhost:3000/api/data?source=source01
- http://localhost:3000/api/data?source=source02
- http://localhost:3000/api/data?source=source03
Вы можете найти полный код для этой демонстрации в следующем репозитории:
Ваше здоровье!