Создание динамического модуля на nestjs

Вопрос или проблема

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

Я пробовал различные подходы:

import {DynamicModule, Inject, Injectable, Module} from '@nestjs/common';
import {ModuleRef} from '@nestjs/core';

@Injectable()
export class Client {
  constructor(@Inject('company') private readonly company: string) {}

  async handle(payload: any): Promise<void> {
    console.log(`Обработать полезную нагрузку ${payload} для компании ${this.company}`);
  }
}

@Injectable()
export class Service {
  constructor(private readonly clientService: Client) {}

  async handle(payload: any): Promise<void> {
    this.clientService.handle(payload);
  }
}

const config = {
  imports: [],
  providers: [Client, Service],
  exports: [Service]
};

@Module(config)
export class MyModule {
  static register(company?: string): DynamicModule {
    return {
      ...config,
      module: MyModule,
      providers: [
        ...config.providers,
        {
          provide: 'company',
          useValue: company
        }
      ]
    };
  }
}

@Injectable()
export class HandlerService {
  constructor(private readonly moduleRef: ModuleRef) {}

  protected async handle(company: string, payload: any): Promise<void> {
    const module = MyModule.register(company);
    // эта часть ломается
    const service = await this.moduleRef.resolve(Service, module.module);
    await service.handle(payload);
  }
}

Ответ или решение

Создание динамического модуля в NestJS является важной задачей, чтобы обеспечить гибкость в разработке приложений. В вашем примере вы пытаетесь создать модуль, который может принимать параметр, а затем использовать этот параметр для инъекции в зависимости. Давайте подробно разберем ваш код и исправим его.

Проблема и Контекст

Вы создали модуль MyModule, который включает в себя класс Client, в конструктор которого вы пытаетесь внедрить строку (параметр company). Однако дело в том, что вы делаете это в методе handle класса HandlerService, что недопустимо, так как DynamicModule не может быть динамически зарегистрирован в уже существующем контексте модуля. Это одна из проблем архитектуры, которая требует более тонкой доработки.

Решение

Вместо попытки создания модуля во время выполнения, вы можете использовать ModuleRef для получения экземпляра вашего класса Service после того, как динамический модуль был зарегистрирован в вашем приложении. Чтобы это сделать, вам необходимо убедиться, что MyModule был имплементирован на уровне корневого модуля приложения или на уровне родительского модуля.

Вот один из подходов к решению этой задачи:

Шаг 1: Регистрация динамического модуля

В вашем коде MyModule должен предоставлять метод register, который будет возвращать динамический модуль с нужной конфигурацией. Убедитесь, что модуль зарегистрирован в корневом приложении.

@Module({})
export class AppModule {
  static register(company: string): DynamicModule {
    return {
      module: AppModule,
      imports: [MyModule.register(company)],
    };
  }
}

Шаг 2: Используйте ModuleRef для получения сервиса

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

Вот переписанный код HandlerService, который правильно разрешает Service:

@Injectable()
export class HandlerService {
  constructor(private readonly moduleRef: ModuleRef) {}

  async handle(company: string, payload: any): Promise<void> {
    // Вызывайте регистрацию модуля на уровне корневого модуля, это будет настройка
    const service = await this.moduleRef.resolve(Service);

    // Убедитесь, что company была инъектирована
    service.handle(payload);
  }
}

Итоговый Код

Общий вид кода, включая ваш динамический модуль и классы, будет выглядеть следующим образом:

import { DynamicModule, Inject, Injectable, Module, ModuleRef } from '@nestjs/common';

@Injectable()
export class Client {
  constructor(@Inject('company') private readonly company: string) {}

  async handle(payload: any): Promise<void> {
    console.log(`Handle payload ${payload} for company ${this.company}`);
  }
}

@Injectable()
export class Service {
  constructor(private readonly clientService: Client) {}

  async handle(payload: any): Promise<void> {
    await this.clientService.handle(payload);
  }
}

@Module({})
export class MyModule {
  static register(company: string): DynamicModule {
    return {
      module: MyModule,
      providers: [
        Client,
        Service,
        {
          provide: 'company',
          useValue: company,
        },
      ],
      exports: [Service],
    };
  }
}

@Injectable()
export class HandlerService {
  constructor(private readonly moduleRef: ModuleRef) {}

  async handle(company: string, payload: any): Promise<void> {
    const service = await this.moduleRef.resolve(Service);
    await service.handle(payload);
  }
}

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...