refactor(application): introduce MessagingServiceApplicationConfiguration builder

Replace the implicit IConfiguration-based DI registration with an explicit
builder that validates required steps at startup.

- Add MessagingServiceApplicationConfiguration with mandatory steps:
    SetLuckyPennySoftLicenseKey(), ConfigureEmailAccounts() / PublishOnly()
- EnsureConfigured() throws at registration time if either step is missing,
  surfacing misconfiguration before the app starts
- Keep the IConfiguration convenience overload as a thin wrapper over the
  builder for backward-compatible call sites
- Move DependencyInjection.cs into a DependencyInjection/ subfolder alongside
  the new configuration class
This commit is contained in:
2026-11-02 15:02:07 +01:00
parent 8e397d8119
commit 084ddc4b56
2 changed files with 94 additions and 11 deletions

View File

@@ -1,46 +1,51 @@
#if NET
using DigitalData.MessagingService.Application.Common.Options;
using FluentValidation;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace DigitalData.MessagingService.Application;
namespace DigitalData.MessagingService.Application.DependencyInjection;
/// <summary>
/// Dependency injection configuration for Application layer.
/// </summary>
public static class DependencyInjection
{
public static IServiceCollection AddMessagingServiceApplication(this IServiceCollection services, IConfiguration configuration)
public static IServiceCollection AddMessagingServiceApplication(this IServiceCollection services, Action<MessagingServiceApplicationConfiguration> configure)
{
var assembly = Assembly.GetExecutingAssembly();
// Read LuckyPennySoft license key from appsettings.json
var licenseKey = configuration.GetValue<string>("LuckyPennySoftLicenseKey")
?? throw new InvalidOperationException("LuckyPennySoftLicenseKey not found in configuration");
var configuration = new MessagingServiceApplicationConfiguration(services);
configure(configuration);
configuration.EnsureConfigured();
// MediatR - Register all handlers
services.AddMediatR(config =>
{
config.LicenseKey = licenseKey;
config.LicenseKey = configuration.LuckyPennySoftLicenseKey;
config.RegisterServicesFromAssembly(assembly);
});
// AutoMapper - Use built-in DI extension (AutoMapper 16.2.0+)
services.AddAutoMapper(config =>
{
config.LicenseKey = licenseKey;
config.LicenseKey = configuration.LuckyPennySoftLicenseKey;
config.AddMaps(assembly);
});
// FluentValidation - Register all validators
services.AddValidatorsFromAssembly(assembly);
// Register EmailAccounts configuration (IOptions<EmailAccountsOptions>)
services.Configure<EmailAccountsOptions>(configuration.GetSection(EmailAccountsOptions.SectionName));
return services;
}
public static IServiceCollection AddMessagingServiceApplication(this IServiceCollection services, IConfiguration configuration)
{
return services.AddMessagingServiceApplication(cfg =>
{
cfg.SetLuckyPennySoftLicenseKey(configuration);
cfg.ConfigureEmailAccounts(configuration);
});
}
}
#endif

View File

@@ -0,0 +1,78 @@
#if NET
using DigitalData.MessagingService.Application.Common.Options;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace DigitalData.MessagingService.Application.DependencyInjection;
public partial class MessagingServiceApplicationConfiguration
{
private IServiceCollection Services { get; }
private (bool LuckyPennySoftLicenseKey, bool EmailAccounts) _configured = (LuckyPennySoftLicenseKey: false, EmailAccounts: false);
internal MessagingServiceApplicationConfiguration(IServiceCollection services)
{
Services = services;
}
internal void EnsureConfigured()
{
if (!_configured.LuckyPennySoftLicenseKey)
{
throw new InvalidOperationException("LuckyPennySoftLicenseKey is not configured. Please call SetLuckyPennySoftLicenseKey() or SetLuckyPennySoftLicenseKey(IConfiguration) in DI configuration.");
}
if (!_configured.EmailAccounts)
{
throw new InvalidOperationException("EmailAccountsOptions is not configured. Please call ConfigureEmailAccountsOptions() or ConfigureEmailAccounts(IConfiguration) in DI configuration.");
}
}
#region LuckyPennySoftLicenseKey
internal string? LuckyPennySoftLicenseKey { get; set; } = null;
public MessagingServiceApplicationConfiguration SetLuckyPennySoftLicenseKey(string licenseKey)
{
if (string.IsNullOrWhiteSpace(licenseKey))
throw new InvalidOperationException("LuckyPennySoftLicenseKey is not configured. Please call SetLuckyPennySoftLicenseKey() or SetLuckyPennySoftLicenseKey(IConfiguration) in DI configuration.");
LuckyPennySoftLicenseKey = licenseKey;
_configured.LuckyPennySoftLicenseKey = true;
return this;
}
public MessagingServiceApplicationConfiguration SetLuckyPennySoftLicenseKey(IConfiguration configuration, string key = "LuckyPennySoftLicenseKey")
{
return SetLuckyPennySoftLicenseKey(configuration.GetValue<string>(key) ?? string.Empty);
}
#endregion LuckyPennySoftLicenseKey
#region EmailAccountsOptions
/// <summary>
/// Ignores whether EmailAccountsOptions is configured or not. Use this method if you just want to publish messages to the messaging service; not to send
/// emails. If you want to consume the messaging service to send emails, you must configure EmailAccountsOptions using ConfigureEmailAccountsOptions() or
/// ConfigureEmailAccounts(IConfiguration).
/// </summary>
/// <returns></returns>
public MessagingServiceApplicationConfiguration PublishOnly()
{
_configured.EmailAccounts = true;
return this;
}
public MessagingServiceApplicationConfiguration ConfigureEmailAccountsOptions(Action<EmailAccountsOptions> configureOptions)
{
Services.Configure(configureOptions);
_configured.EmailAccounts = true;
return this;
}
public MessagingServiceApplicationConfiguration ConfigureEmailAccounts(IConfiguration configuration, string sectionName = EmailAccountsOptions.SectionName)
{
Services.Configure<EmailAccountsOptions>(configuration.GetSection(sectionName));
_configured.EmailAccounts = true;
return this;
}
#endregion EmailAccountsOptions
}
#endif