Есть ли в 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();
}
}
Я мог бы придумать такие же небольшие классы для большинства проблем, перечисленных выше, что привело бы к существенно более простой логике в целом из-за уменьшения зависимости / ответственности.
Я ищу способы для достижения этой цели без необходимости создания значительного количества настраиваемого кода инфраструктуры для его поддержки.