Как лучше всего разрешить регистрацию DI в ConfigureServices и сделать этот код повторно используемым?

Вопрос или проблема

Мы используем Azure Key Vault, который хранит секреты (например, пароли). Мы реализовали клиент под названием KeyVaultClient, который запрашивает необходимые секреты из Azure Key Vault. В Key Vault есть один секрет, который требуется во время конфигурации в нашем классе Startup.cs. Вот код:

public class Startup
{
    [...]
    
    public void ConfigureServices(IServiceCollection services)
    {
        [...]
    
        // регистрация DI и зависимой конфигурации
        services.AddSingleton<IKeyVaultClient, KeyVaultClient>().Configure<KeyVaultClientSettings>(Configuration.GetSection(KeyVaultClientSettings.Section));
        
        // код, который разрешает IKeyVaultClient, потому что нам нужно запросить секрет
        var serviceProvider = services.BuildServiceProvider();
        var keyVaultClient = serviceProvider.GetService<IKeyVaultClient>();
        var secretName = "Имя моего секрета";
        var secret = keyVaultClient.GetSecret(secretName);

        // Наш собственный метод расширения, который аутентифицирует это приложение по секрету
        services.AddFrontendAuthentication(secret);
        
        [...]
    }
}

Регистрация IKeyVaultClient для DI и его непосредственное разрешение выглядит как-то неаккуратно. Каковы лучшие практики, чтобы сделать код более чистым и переиспользуемым в других проектах?

Ответ или решение

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

Шаг 1: Использование фабрики для получения секрета

Один из способов улучшить код — это создать фабричный класс для получения секрета из KeyVaultClient. Таким образом, вы сможете инкапсулировать логику получения секрета и сделать ее переиспользуемой.

public interface ISecretProvider
{
    string GetSecret(string secretName);
}

public class SecretProvider : ISecretProvider
{
    private readonly IKeyVaultClient _keyVaultClient;

    public SecretProvider(IKeyVaultClient keyVaultClient)
    {
        _keyVaultClient = keyVaultClient;
    }

    public string GetSecret(string secretName)
    {
        return _keyVaultClient.GetSecret(secretName);
    }
}

Шаг 2: Регистрация зависимостей

Теперь в классе Startup вы можете зарегистрировать ISecretProvider, используя IKeyVaultClient. Этот процесс будет выглядеть гораздо более чисто.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IKeyVaultClient, KeyVaultClient>()
                .Configure<KeyVaultClientSettings>(Configuration.GetSection(KeyVaultClientSettings.Section));

        // Регистрация SecretProvider
        services.AddSingleton<ISecretProvider, SecretProvider>();

        // Использование ISecretProvider для получения секрета
        var serviceProvider = services.BuildServiceProvider();
        var secretProvider = serviceProvider.GetService<ISecretProvider>();

        var secretName = "My Secret Name";
        var secret = secretProvider.GetSecret(secretName);

        // Регистрация аутентификации с использованием секрета
        services.AddFrontendAuthentication(secret);
    }
}

Причины, почему это лучшее решение:

  1. Чистота кода: Логика получения секрета инкапсулирована в одном классе, что делает код в Startup более читабельным и упрощает его понимание.
  2. Повторное использование: Вы можете использовать SecretProvider в других местах вашего приложения без дублирования кода.
  3. Тестирование: Легче писать модульные тесты для SecretProvider, так как вы можете замокировать IKeyVaultClient и проверять, как ваш код реагирует на различные сценарии получения секрета.

Заключение

Создание фабрики для получения секретов и использование её в ConfigureServices улучшает чистоту, тестируемость и повторное использование вашего кода. Это практическое решение поможет сделать ваше приложение более устойчивым и гибким. Вы также сможете легко расширять данный подход для добавления новых зависимостей или функциональности в будущем.

Оцените материал
Добавить комментарий

Капча загружается...