Когда конфигурация разбросана по всему коду, проект становится хрупким. Строки вроде
_configuration["Stripe:ApiKey"] встречаются в контроллерах, сервисах, хелперах — и каждый раз с риском опечатки, без типизации, без валидации. Поменяли ключ в appsettings — ищете, где всё сломалось.Что не так с таким подходом
Прямое обращение к
IConfiguration через магические строки создаёт несколько реальных проблем. Нет единого места, где видно всю конфигурацию приложения. Парсинг типов (int.Parse, bool.Parse) повторяется в разных местах. Опечатка в ключе не вызовет ошибку компиляции — только падение в рантайме. Тестировать такой код неудобно:
// Так делать не стоит — магические строки, парсинг вручную
var apiKey = _configuration["Stripe:ApiKey"];
var timeout = int.Parse(_configuration["Timeout"] ?? "30");
Одна точка входа для конфигурации
Решение: завести типизированные классы под каждую область конфигурации и привязать их один раз при старте приложения.
// Infrastructure/Configuration/ApplicationOptions.cs
public sealed class ApplicationOptions
{
public PaymentOptions Payment { get; set; } = new();
public DatabaseOptions Database { get; set; } = new();
public CacheOptions Cache { get; set; } = new();
public FeatureFlags Features { get; set; } = new();
}
public sealed class PaymentOptions
{
public string StripeApiKey { get; set; } = string.Empty;
public string StripeWebhookSecret { get; set; } = string.Empty;
public int RetryAttempts { get; set; } = 3;
}
В
Program.cs одна привязка для всего:builder.Services.AddOptions<ApplicationOptions>()
.Bind(builder.Configuration)
.ValidateDataAnnotations()
.ValidateOnStart();
ValidateOnStart выбрасывает исключение при запуске, если конфигурация невалидна. Не в рантайме, не при первом запросе — сразу при старте.Чистое внедрение через extension method
Чтобы не засорять
Program.cs, выносим регистрацию в extension method и сразу открываем под-опции для инжекции:public static class OptionsExtensions
{
public static IServiceCollection AddApplicationOptions(
this IServiceCollection services,
IConfiguration configuration)
{
services.AddOptions<ApplicationOptions>()
.Bind(configuration)
.ValidateDataAnnotations()
.ValidateOnStart();
services.AddSingleton(sp =>
sp.GetRequiredService<IOptions<ApplicationOptions>>().Value.Payment);
services.AddSingleton(sp =>
sp.GetRequiredService<IOptions<ApplicationOptions>>().Value.Database);
return services;
}
}
Теперь в сервисах не нужна обёртка
IOptions<T>. Инжектируем напрямую:public class OrderService
{
private readonly PaymentOptions _paymentOptions;
private readonly DatabaseOptions _dbOptions;
public OrderService(
PaymentOptions paymentOptions,
DatabaseOptions dbOptions)
{
_paymentOptions = paymentOptions;
_dbOptions = dbOptions;
}
}
В итоге конфигурация живёт в одном месте и имеет чёткую структуру. Все ключи типизированы и никаких магических строк.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view