Как следовать принципам SOLID в классах Startup? - PullRequest
0 голосов
/ 06 ноября 2018

Есть ли в AspNet Core собственный механизм, позволяющий разделить работу, выполняемую внутри монолитного класса Startup, таким образом, чтобы улучшить читаемость / ремонтопригодность / масштабируемость в долгосрочной перспективе? Если да, то как это работает?

У нас есть несколько небольшой проект .Net Core MVC WebAPI, который абстрагирует некоторые проблемы каталога товаров, но класс Startup быстро растет, и, на мой взгляд, его трудно читать и поддерживать.

Вот немного статистики:

  • 244 строки кода
  • 32 с использованием директив пространства имен
  • ~ 50 строк ручной регистрации контейнеров на уровне домена

Хотя это может показаться не таким уж большим делом, по сравнению с несколькими классами, которые следуют принципам SOLID в остальной части проекта, это может быть пугающим (особенно количество включенных различных пространств имен, которые являются хорошим показателем нарушения SRP).

Я мог бы создать несколько дополнительных .AddX() методов расширения, чтобы уменьшить значительную часть кода регистрации DI вручную (например, что-то по принципу «на модуль» или близко напоминающее Registry / Module от Autofac или Structuremap), как описано здесь , но даже тогда у меня останется хороший кусок несвязанной и несколько сложной логики для регистрации / настройки, например:

  • Mvc (включая пользовательские фильтры, опции сериализации, маршруты OData, построитель модели OData EDM)
  • Swagger (снова включая настройки и различные настройки)
  • ApiVersioning
  • Конфигурация Cors
  • Сложный IConfiguration строитель с использованием внешней системы конфигурации
  • явный IsDevelopment проверка настройки страницы исключений по умолчанию

Все это кажется совершенно изолированными, независимыми проблемами, и я чувствую, что нарушаю SRP, объединяя их в один класс.

Есть ли известный механизм, который я мог бы использовать, чтобы разделить работу, выполняемую внутри Startup, на отдельные классы, чтобы, например, более точно следовать SRP? Это было бы желательно?

Даже если ядро ​​aspnet поддерживает только один класс Startup (я не нашел подтверждения этому), я думаю, что мог бы придумать какую-то составную реализацию с дочерними Startup классами, каждый из которых имеет дело с одной из этих проблем, но я не хотел изобретать велосипед или увеличивать сложность, если подобный механизм уже был широко доступен и создан для этой цели.

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

Например, у нас есть небольшой фрагмент кода внутри Configure метода:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // lots of code here

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    // ...and lots of code here
}

Если бы эта логика была абстрагирована в полностью изолированном классе конфигурации, у нас могло бы быть что-то вроде этого:

public class ErrorPageConfigurationStartup
{   
    private readonly IApplicationBuilder _app;

    public ErrorPageConfigurationStartup(IApplicationBuilder app)
    {
        _app = app;
    }

    public void Configure()
    {
        app.UseExceptionHandler("/Home/Error");           
    }

    public void ConfigureDevelopment()
    {
        app.UseDeveloperExceptionPage();
    }
}

Или даже это, используя инъекцию уровня метода:

public class ErrorPageConfigurationStartup
{   
    public void Configure(IApplicationBuilder app)
    {
        app.UseExceptionHandler("/Home/Error");           
    }

    public void ConfigureDevelopment(IApplicationBuilder app)
    {
        app.UseDeveloperExceptionPage();
    }
}

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

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

1 Ответ

0 голосов
/ 06 ноября 2018

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

DI> Автозагрузка имеет метод configure> идет к загрузчику DI> идет к файлу I с именем IocConfig.cs, который содержит составной корень. Замена контейнера заняла пару часов в прошлый раз, когда я сделал это с бонусом.

Для .NET Core конфигурация вызывается напрямую при сборке контейнера, см .: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1

Swagger> когда вы устанавливаете nuget, он уже должен был предоставить вам файл конфигурации, снова запустив строку config 1 при запуске.

Если это не так в .net Core, я все равно создаю файл конфигурации вручную и перемещаю свой код.

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

Здесь нет стандартного из того, что я могу сказать, как далеко вы решите зайти на абстрагирование кода. Например, мои методы настройки oauth - это методы в нижней части файла startup.cs (они затем вызывают больше классов), каждая из которых весит около десятка строк, поэтому перемещение их в свои собственные классы не имеет большого смысла, однако кэширование Синглтон немного сложнее, поэтому он получает файл cachingprovider.cs.

...