Информация об изменениях

Сообщение Re: Точка создания и инициализации конкретики приложения от 08.05.2024 17:35

Изменено 09.05.2024 9:42 RushDevion

Re: Точка создания и инициализации конкретики приложения
Z>Получается, в начальной точке приложения должен быть какой-то супер-класс, который знает про особенности инициализации всех инфраструктурных слоев приложения.
Z>Верно?
Да. Это место так и называется "точка сборки", "корень композиции", composition root и т.п.

Z>Но тогда получается, что это "плохой" God-объект.

It depends.

Z>Как же быть?

Z>Как делать инициализацию? В какой точке? И в каком месте указанной картинки эта точка должна располагаться?
Инициализация делается в той точке где приложение стартует. Что, если подумать, логично, где еще ее делать-то?
А вот как именно ее делать — тут возможны варианты.
Далее я даю примеры на .NET/C#, потому что работаю в основном с этим окружением, но, думаю, в Java принципиальных отличий не будет.

1. Прямолинейный — собираем граф зависимоcтей руками (aka pure man DI)
void Main() {
  IConfig config = ReadConfigFromSomewhere();
  IDbConnectionManager dbConManager = new dbConManager(config.DbConnectionString);
  IRepository1 repository1 = new MyRepository1(dbConManager);
  IRepository1 repository2 = new MyRepository2(dbConManager);
  IApplicationService1 appService1 = new ApplicationUseCaseService1(repository1, repository2);
  IApplicationService1 appService2 = new ApplicationUseCaseService1(repository1);
  // И т.д. и т.п. собираем весь граф зависимостей руками
  // Затем запускаем приложение. 
  // В данном случае некий сервис, которые принимает запросы по сети.
  using var server = new ApiServer(appService1, appService2);
  server.Start();

  // При этом не обязательно создавать абсолютно все объекты сразу.
  // Можно сделать фабрику, которая будет делать это по требованию, e.g.
  var appServiceFactory = new ApplicationServiceFactory(config);
  
  using var server = new ApiServer(appServiceFactory);
  
  // Внутри реализации ApiServer
  private void OnRequest(string uri, object paload) {
    switch(uri) {
      case "useCase1": _appServiceFactory.CreateUseCase1Service().Process(payload.ParseAs<UseCase1RequestData>());
      ...
    }
  }
}


2. Используем контейнер внедрения зависимоcтей (aka DI-контейнер, IOC-контейнер)
void Main() {
  IConfig config = ReadConfigFromSomewhere();
  using var di = new Container();
  di.AddSingleton(config);
  // RepositoriesModule - эта штука, которая лежит в сборке с имплементациями и регистрирует конкретные конкретные типы 
  // e.g.
  //   ioc.AddSingleton<IDbConnectionManager>(new MySqlDbConnectionManager(config.DbConnectionString)); 
  //   ioc.AddTransient<IRepo1, Repo1Impl>();
  di.Add<RepositoriesModule>(config); // Здесь можно как передать config и взять из него значения напрямую
  di.Add<ApplicationServiceModule>(); // Так и позволить сервису принять IConfig напрямую и самостоятельно зачитать что ему нужно
  di.AddSingleton<ApiServer>();
  di.Resolve<ApiServer>().Start();
}
Re: Точка создания и инициализации конкретики приложения
Z>Получается, в начальной точке приложения должен быть какой-то супер-класс, который знает про особенности инициализации всех инфраструктурных слоев приложения.
Z>Верно?
Да. Это место так и называется "точка сборки", "корень композиции", composition root и т.п.

Z>Но тогда получается, что это "плохой" God-объект.

It depends.

Z>Как же быть?

Z>Как делать инициализацию? В какой точке? И в каком месте указанной картинки эта точка должна располагаться?
Инициализация делается в той точке где приложение стартует. Что, если подумать, логично, где еще ее делать-то?
А вот как именно делать — тут возможны варианты.
Далее я даю примеры на .NET/C#, потому что работаю в основном с этим окружением, но, думаю, в Java принципиальных отличий не будет.

1. Прямолинейный — собираем граф зависимоcтей руками (aka pure man DI)
void Main() {
  IConfig config = ReadConfigFromSomewhere();
  IDbConnectionManager dbConManager = new dbConManager(config.DbConnectionString);
  IRepository1 repository1 = new MyRepository1(dbConManager);
  IRepository2 repository2 = new MyRepository2(dbConManager);
  IApplicationService1 appService1 = new ApplicationUseCaseService1(repository1, repository2);
  IApplicationService2 appService2 = new ApplicationUseCaseService1(repository2);
  // И т.д. и т.п. собираем весь граф зависимостей руками
  // Затем запускаем приложение. 
  // В данном случае некий сервис, которые принимает запросы по сети.
  using var server = new ApiServer(appService1, appService2);
  server.Start();

  // При этом не обязательно создавать абсолютно все объекты сразу.
  // Можно сделать фабрику, которая будет делать это по требованию, e.g.
  var appServiceFactory = new ApplicationServiceFactory(config);
  
  using var server = new ApiServer(appServiceFactory);
  
  // Внутри реализации ApiServer
  private void OnRequest(string uri, object payload) {
    switch(uri) {
      case "useCase1": _appServiceFactory.CreateUseCase1Service().Process(payload.ParseAs<UseCase1RequestData>());
      ...
    }
  }
}


2. Используем контейнер внедрения зависимостей (aka DI-контейнер, IOC-контейнер)
void Main() {
  IConfig config = ReadConfigFromSomewhere();
  using var di = new Container();
  di.AddSingleton(config);
  // RepositoriesModule - эта штука, которая лежит в сборке с имплементациями и регистрирует конкретные типы 
  // e.g.
  //   ioc.AddSingleton<IDbConnectionManager>(new MySqlDbConnectionManager(config.DbConnectionString)); 
  //   ioc.AddTransient<IRepo1, Repo1Impl>();
  di.Add<RepositoriesModule>(config); // Здесь можно как передать config и взять конкретные значения из него для передачи в конструкторы классов 
  di.Add<ApplicationServiceModule>(); // Так и позволить классу принять весь IConfig в конструкторе и самостоятельно зачитать необходимое
  di.AddSingleton<ApiServer>();
  di.Resolve<ApiServer>().Start();
}