Как стать автором
Обновить

Модульность регистрации зависимостей .net

C# *
Ожидает приглашения

Материал для юных разработчиков стека dot net. Я постарался максимально просто изложить данный материал. Надеюсь у меня получилось.

Пролог

Я заметил, что очень много статей о DI и о том, как регистрировать зависимости. Но нет ни слова о модульности. Мне кажется эта тема будет особенна полезна нашим будущим коллегам, которые пока не имеют коммерческого опыта разработки.

Поехали

Допустим есть приложение на asp net core. Некий бекенд. Например для учета автомобилей. Пусть это трехслойное приложение, и каждый слой это своя сборка (свой проект):

  1. cлой клиента (веб приложение)

  2. cлой логики\домена

  3. cлой данных

На слое данных у нас есть AutoRepository (который реализует интерфейс IAutoRepository)

На слое логики у нас есть AutoManager (который реализует интерфейс IAutoManager) который зависит от IAutoRepository.

На слое веб приложения у нас контроллер который зависит от IAutoManager.

в StartUp.cs или в Program.cs у нас будет вот такой код:

...
serviceCollection.AddScoped<IAutoRepository, AutoRepository>(); 
serviceCollection.AddScoped<IAutoManager, AutoManager>(); 
...

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

Что же мне не нравится?

В данном случае и AutoRepository и AutoManager у нас будут публичными. Что в может повлечь за собой проблемы. Зачем нам отдавать наружу наши реализации?

Решение:

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

Нам потребуется интерфейс IModule и метод расширения для IServiceCollection.

/// <summary>
/// Модуль регистраций зависимостей.
/// </summary>
public interface IModule
{
    /// <summary>
    /// Регистриация зависимостей.
    /// </summary>
    /// <param name="serviceCollection"><see cref="IServiceCollection"/>.</param>
    public void RegisterServices(IServiceCollection serviceCollection);
}
/// <summary>
/// Расширения для <see cref="IServiceCollection"/>
/// </summary>
public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Зарегистрировать модуль в коллекции сервисов.
    /// </summary>
    /// <param name="serviceCollection"><see cref="IServiceCollection"/>.</param>
    /// <param name="module"><see cref="IModule"/>.</param>
    /// <returns><see cref="IServiceCollection"/>.</returns>
    public static IServiceCollection RegisterModule(this IServiceCollection serviceCollection, IModule module)
    {
        module.RegisterServices(serviceCollection);
        return serviceCollection;
    }
}

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

например (модуль для слоя логики):

 public class DomainModule : IModule
 {
     public void RegisterServices(IServiceCollection serviceCollection)
     {
         serviceCollection.AddScoped<IAutoManager, AutoManager>();
     }
  }

Ок, модули сделали. Теперь перейдем к нашему WebApp приложению и заменим там регистрацию конкретных сервисов на регистрацию модулей.

builder.Services.RegisterModule(new DataModule());
builder.Services.RegisterModule(new DomainModule());

Таким образом, нам ничего не мешает классы AutoManager и AutoRepository сделать "internal" и таким образом инкапсулировать реализацию внутри слоя.

Дополнительный плюс: Если у нас появится еще одно приложение, которое работает со слоем дата (например планировщик, или постоитель отчетов), то мы можем подсунуть ему наш модуль "DataModule", а не копировать все наши регистрации репозиториев.

Эпилог

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

Кстати многие DI контейнеры из коробки поддерживают модульность, например simpleInjector или Autofac.

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.