Инверсия контейнера управления (контейнер IoC) - мощный принцип программирования для обработки зависимостей классов. Он используется для увеличения модульности и расширения возможностей программы за счет абстрагирования реализаций через интерфейсы.

Одним из типов IoC является внедрение зависимостей, при котором зависимости вводятся во время выполнения по сравнению с традиционным потоком управления. Все это встроено в Adonis Framework.

Идея

То, что мы собираемся построить, довольно просто, но, надеюсь, достаточно, чтобы доказать элегантность и простоту реализации этой архитектуры. Мы создаем сервис с единственной целью - печатать «привет» и «до свидания» в консоли при посещении определенного маршрута.

Давайте код

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

Стоит отметить, что все примеры кода написаны на TypeScript.

Интерфейс

Интерфейс играет важную роль в отделении деталей реализации от абстракции и дает нам большую гибкость в долгосрочной перспективе.

export default interface GreetingInterface {
     sayHello(name: string): void
     sayGoodbye(name: string): void
}

Теперь мы создали интерфейс с двумя методами, которые принимают имя в качестве аргумента. Нет никакого возвращаемого типа, так как вывод будет в консоли.

Интерфейс сохраняется в «контрактах / интерфейсах / Greeting.interface.ts».

Реализация

В каталоге «app» создается новая подпапка «Services», которая содержит все детали реализации. В данном случае у меня есть файл GreetingService.ts, реализующий только что созданный интерфейс.

import GreetingInterface from 'Contracts/interfaces/Greeting.interface'
export default class GreetingService implements GreetingInterface {
  public sayHello (name: string): void {
    console.log(`Hi ${name}`)
  }
  public sayGoodbye (name: string): void {
    console.log(`Goodbye ${name}`)
  }
}

Зарегистрируйте привязку

В «Provider / AppProvider.ts» у нас есть возможность зарегистрировать все привязки, используемые в приложении. Существуют разные типы привязок, но в этом примере мы используем шаблон singleton, в котором только один экземпляр создается и используется во всем приложении.

import { IocContract } from '@adonisjs/fold'
import GreetingService from 'App/Services/GreetingService'
export default class AppProvider {
  constructor (protected $container: IocContract) {
  }
  public register () {
    // Register your own bindings')
    this.$container.singleton('MyProject/GreetingService', () => new GreetingService())
  }
  public boot () {
    // IoC container is ready
  }
  public shutdown () {
    // Cleanup, since app is going down
  }
  public ready () {
    // App is ready
  }
}

Чтобы было легче отличать Adonis Core от других привязок, мы используем собственное имя в качестве пространства имен. В этом примере пространство имен называется «MyProject», но это может быть что угодно.

Создайте внешний модуль

Мы создаем внешний модуль, чтобы в полной мере использовать TypeScript и использовать преимущества, которые он дает нам (например, проверка типа). Мы создаем декларацию в папке контракты и называем ее Hello.ts
Чтобы узнать больше об этой концепции, я рекомендую прочитать документацию: https://www.typescriptlang.org/docs/handbook/ modules.html # ambient-modules

declare module '@ioc:MyProject/GreetingService' {
  import GreetingInterface from 'Contracts/interfaces/Greeting.interface'
  const GreetingService: GreetingInterface
  export default GreetingService
}

Эта концепция также используется в Adonis Core.

Последний, но тем не менее важный

Мы завершили настройку минимального контейнера IoC, и пора использовать его в контроллере.
Контроллер создается с помощью «node ace make: controller» и следует стандартной реализации.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import GreetingService from '@ioc:MyProject/GreetingService'
export default class TestController {
  public async index ({ response }: HttpContextContract) {
    GreetingService.sayHello('Bob')
    // more code ....
    return response.ok({
      data: {},
    })
  }
}