Круговая зависимость возникает, когда два класса зависят друг от друга. Например, класс A нуждается в классе B, а класс B также нуждается в классе A. Циркулярные зависимости могут возникать в Nest между модулями и между провайдерами.

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

Мы также опишем разрешение круговых зависимостей между модулями.

При использовании файлов "barrel files (не понял как это перевести)"/index.ts для группировки импорта также может возникнуть круговая зависимость. Файлы barrel следует опускать, когда речь идет о классах модулей/провайдеров. Например, файлы barrel не должны использоваться при импорте файлов в том же каталоге, что и файл barrel, т.е. cats/cats.controller не должен импортировать cats для импорта файла cats/cats.service.

Прямая ссылка

Прямая ссылка позволяет Nest ссылаться на классы, которые еще не определены, используя функцию утилиты forwardRef(). Например, если CatsService и CommonService зависят друг от друга, обе стороны отношений могут использовать @Inject() и утилиту forwardRef() для разрешения круговой зависимости. В противном случае Nest не будет инстанцировать их, потому что все необходимые метаданные будут недоступны. Вот пример:

cats.service.ts

@Injectable()
export class CatsService {
  constructor(
    @Inject(forwardRef(() => CommonService))
    private commonService: CommonService,
  ) {}
}

Функция forwardRef() импортируется из пакета @nestjs/common.

Это охватывает одну сторону отношений. Теперь давайте сделаем то же самое с CommonService:

@Injectable()
export class CommonService {
  constructor(
    @Inject(forwardRef(() => CatsService))
    private catsService: CatsService,
  ) {}
}

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

Альтернатива классу ModuleRef

Альтернативой использованию forwardRef() является рефакторинг вашего кода и использование класса ModuleRef для получения провайдера с одной стороны круговой связи.

Прямая ссылка на модуль

Чтобы разрешить круговые зависимости между модулями, используйте одну и ту же служебную функцию forwardRef() в обоих взаимозависимых модулях. Например:

common.module.ts

@Module({
  imports: [forwardRef(() => CatsModule)],
})
export class CommonModule {}