refactor(di): add AddApplication entry point and generic AddValidatedOptions

- Add Musify.Application.AddApplication(services, config) as the layer's DI
  entry point: registers Mediator, UploadIntentValidator and all app configs
- Collapse the 5 near-identical AddXConfiguration methods into a single
  generic AddValidatedOptions<T>(config, section)
- Move UploadIntentValidator registration out of Infrastructure into the
  Application layer (fixes layering leak); Infra config binding now uses the
  generic helper
- Api calls AddApplication; Worker uses AddValidatedOptions for its configs

Build: 0 warnings, 0 errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 04:36:46 +02:00
co-authored by Claude Opus 4.8
parent e802c8565d
commit 38f7094fb0
5 changed files with 47 additions and 69 deletions
@@ -1,7 +1,7 @@
using FluentValidation; using FluentValidation;
using Musify.Infrastructure.MassTransit; using Musify.Infrastructure.MassTransit;
using Musify.Infrastructure.Persistence; using Musify.Infrastructure.Persistence;
using Musify.Application.Configuration; using Musify.Application;
using Musify.Infrastructure.Services; using Musify.Infrastructure.Services;
using Musify.Api.Authentication; using Musify.Api.Authentication;
using Musify.Api.DataTransferObjects.PlayLists; using Musify.Api.DataTransferObjects.PlayLists;
@@ -14,16 +14,11 @@ public static class ServiceCollectionExtensions
{ {
public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddApplication(configuration);
services.AddDatabase(configuration); services.AddDatabase(configuration);
services.AddApplicationStorageConfiguration(configuration);
services.AddPlayListConfiguration(configuration);
services.AddTrackConfiguration(configuration);
services.AddStreamGatewayConfiguration(configuration);
services.AddStorageService(configuration); services.AddStorageService(configuration);
services.AddStreamTicketService(configuration); services.AddStreamTicketService(configuration);
services.AddUploadIntentConfiguration(configuration);
services.AddMassTransitClient(configuration); services.AddMassTransitClient(configuration);
services.AddMediator();
services.AddAuthenticationConfiguration(configuration); services.AddAuthenticationConfiguration(configuration);
services.AddOpenApiConfiguration(); services.AddOpenApiConfiguration();
services.AddScalarConfiguration(); services.AddScalarConfiguration();
@@ -0,0 +1,24 @@
using Mediator;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Musify.Application.Configuration;
using Musify.Application.Services;
namespace Musify.Application;
public static class ApplicationDependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services, IConfiguration configuration)
{
services.AddMediator();
services.AddScoped<UploadIntentValidator>();
services.AddValidatedOptions<ApplicationStorageConfiguration>(configuration, ApplicationStorageConfiguration.SectionName);
services.AddValidatedOptions<PlayListConfiguration>(configuration, PlayListConfiguration.SectionName);
services.AddValidatedOptions<TrackConfiguration>(configuration, TrackConfiguration.SectionName);
services.AddValidatedOptions<StreamGatewayConfiguration>(configuration, StreamGatewayConfiguration.SectionName);
services.AddValidatedOptions<UploadIntentConfiguration>(configuration, UploadIntentConfiguration.SectionName);
return services;
}
}
@@ -1,57 +1,25 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Musify.Application.Configuration namespace Musify.Application.Configuration;
public static class ConfigurationDependencyInjection
{ {
public static class ConfigurationDependencyInjection public static IServiceCollection AddValidatedOptions<T>(
this IServiceCollection services,
IConfiguration configuration,
string sectionName)
where T : class
{ {
public static IServiceCollection AddApplicationStorageConfiguration(this IServiceCollection serviceDescriptors, IConfiguration configuration) services
{ .AddOptionsWithValidateOnStart<T>()
serviceDescriptors .Bind(configuration.GetRequiredSection(sectionName))
.AddOptionsWithValidateOnStart<ApplicationStorageConfiguration>() .ValidateDataAnnotations();
.Bind(configuration.GetRequiredSection(ApplicationStorageConfiguration.SectionName))
.ValidateDataAnnotations();
serviceDescriptors.AddSingleton(serviceProvider =>
serviceProvider.GetRequiredService<IOptions<ApplicationStorageConfiguration>>().Value);
return serviceDescriptors; services.AddSingleton(serviceProvider =>
} serviceProvider.GetRequiredService<IOptions<T>>().Value);
public static IServiceCollection AddPlayListConfiguration(this IServiceCollection serviceDescriptors, IConfiguration configuration) return services;
{
serviceDescriptors
.AddOptionsWithValidateOnStart<PlayListConfiguration>()
.Bind(configuration.GetRequiredSection(PlayListConfiguration.SectionName))
.ValidateDataAnnotations();
serviceDescriptors.AddSingleton(serviceProvider =>
serviceProvider.GetRequiredService<IOptions<PlayListConfiguration>>().Value);
return serviceDescriptors;
}
public static IServiceCollection AddTrackConfiguration(this IServiceCollection serviceDescriptors, IConfiguration configuration)
{
serviceDescriptors
.AddOptionsWithValidateOnStart<TrackConfiguration>()
.Bind(configuration.GetRequiredSection(TrackConfiguration.SectionName))
.ValidateDataAnnotations();
serviceDescriptors.AddSingleton(serviceProvider =>
serviceProvider.GetRequiredService<IOptions<TrackConfiguration>>().Value);
return serviceDescriptors;
}
public static IServiceCollection AddStreamGatewayConfiguration(this IServiceCollection serviceDescriptors, IConfiguration configuration)
{
serviceDescriptors
.AddOptionsWithValidateOnStart<StreamGatewayConfiguration>()
.Bind(configuration.GetRequiredSection(StreamGatewayConfiguration.SectionName))
.ValidateDataAnnotations();
serviceDescriptors.AddSingleton(serviceProvider =>
serviceProvider.GetRequiredService<IOptions<StreamGatewayConfiguration>>().Value);
return serviceDescriptors;
}
} }
} }
@@ -5,7 +5,6 @@ using Microsoft.Extensions.Options;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Infrastructure.Configuration; using Musify.Infrastructure.Configuration;
using Musify.Infrastructure.Jobs; using Musify.Infrastructure.Jobs;
using Musify.Application.Services;
using Musify.Application.Contracts; using Musify.Application.Contracts;
namespace Musify.Infrastructure.Services namespace Musify.Infrastructure.Services
@@ -46,15 +45,7 @@ namespace Musify.Infrastructure.Services
public static IServiceCollection AddUploadIntentConfiguration(this IServiceCollection serviceDescriptors, IConfiguration configuration) public static IServiceCollection AddUploadIntentConfiguration(this IServiceCollection serviceDescriptors, IConfiguration configuration)
{ {
serviceDescriptors serviceDescriptors.AddValidatedOptions<UploadIntentConfiguration>(configuration, UploadIntentConfiguration.SectionName);
.AddOptionsWithValidateOnStart<UploadIntentConfiguration>()
.Bind(configuration.GetRequiredSection(UploadIntentConfiguration.SectionName))
.ValidateDataAnnotations();
serviceDescriptors.AddSingleton(serviceProvider =>
serviceProvider.GetRequiredService<IOptions<UploadIntentConfiguration>>().Value);
serviceDescriptors.AddScoped<UploadIntentValidator>();
return serviceDescriptors; return serviceDescriptors;
} }
+3 -3
View File
@@ -10,9 +10,9 @@ var configuration = builder.Configuration;
var services = builder.Services; var services = builder.Services;
services.AddApplicationStorageConfiguration(configuration); services.AddValidatedOptions<ApplicationStorageConfiguration>(configuration, ApplicationStorageConfiguration.SectionName);
services.AddPlayListConfiguration(configuration); services.AddValidatedOptions<PlayListConfiguration>(configuration, PlayListConfiguration.SectionName);
services.AddTrackConfiguration(configuration); services.AddValidatedOptions<TrackConfiguration>(configuration, TrackConfiguration.SectionName);
services.AddStorageService(configuration); services.AddStorageService(configuration);
services.AddAudioTranscoder(configuration); services.AddAudioTranscoder(configuration);
services.AddPictureService(configuration); services.AddPictureService(configuration);