.NET 6清洁架构中如何合规注册基础设施层依赖?
解决清洁架构下基础设施层依赖注册的问题
核心矛盾是清洁架构要求表现层(Web API)仅依赖应用层,不能直接引用基础设施层,但基础设施层的服务注册逻辑又需要被触发。以下是两种合规的解决方案:
方案一:基于接口的依赖注册扩展(推荐)
通过在应用层定义抽象注册接口,让基础设施层实现该接口,再由应用层统一扫描执行注册,完全隔离表现层与基础设施层的直接依赖。
步骤1:在应用层定义抽象注册接口
namespace JwtWithIdentityDemo.Application.IoC { public interface IDependencyRegistrar { void RegisterServices(IServiceCollection services, IConfiguration configuration); } }
步骤2:在基础设施层实现该接口
把原有的AddInfrastructure逻辑迁移到接口实现中:
namespace JwtWithIdentityDemo.Infrastructure.IoC { public class InfrastructureDependencyRegistrar : IDependencyRegistrar { public void RegisterServices(IServiceCollection services, IConfiguration configuration) { services.Configure<JwtSetings>(configuration.GetSection(JwtSetings.SectionName)); services.AddSingleton<IJwtTokenGenerator, JwtTokenGenerator>(); services.AddTransient<IUserManagerWrapper, UserManagerWrapper>(); // Identity配置 services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.AddScoped<UserManager<IdentityUser>>(); // EF Core配置 services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(configuration.GetConnectionString("JwtWithIdentityDemoDb"), sqlServerOptions => sqlServerOptions.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName) .EnableRetryOnFailure( maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null) ), ServiceLifetime.Transient ); } } }
步骤3:在应用层实现扫描与注册逻辑
创建应用层的扩展方法,自动扫描所有实现IDependencyRegistrar的类型并执行注册:
namespace JwtWithIdentityDemo.Application.IoC { public static class ApplicationDependencyRegistration { public static void AddApplicationWithDependencies(this IServiceCollection services, IConfiguration configuration) { // 注册应用层自身服务(比如MediatR) services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ApplicationDependencyRegistration>()); // 扫描并执行所有依赖注册器 var registrars = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes()) .Where(type => typeof(IDependencyRegistrar).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) .Select(Activator.CreateInstance) .Cast<IDependencyRegistrar>(); foreach (var registrar in registrars) { registrar.RegisterServices(services, configuration); } } } }
步骤4:表现层仅调用应用层方法
在Program.cs中只需调用应用层的扩展方法,无需引用基础设施层:
var builder = WebApplication.CreateBuilder(args); { builder.Services.AddApplicationWithDependencies(builder.Configuration); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); }
注意:确保基础设施层的程序集在运行时能被加载(部署时包含该DLL,或在启动项目依赖中设置为「复制到输出目录」)。
方案二:反射直接调用基础设施层注册方法
若不想定义接口,可通过反射直接调用基础设施层的AddInfrastructure方法,同样避免直接引用:
var builder = WebApplication.CreateBuilder(args); { builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<Program>()); builder.Services.AddApplication(builder.Configuration); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // 反射调用基础设施层的注册方法 var infrastructureAssembly = AppDomain.CurrentDomain.Load("JwtWithIdentityDemo.Infrastructure"); var registerType = infrastructureAssembly.GetType("JwtWithIdentityDemo.Infrastructure.IoC.RegisterInfrastructure"); var addInfrastructureMethod = registerType.GetMethod("AddInfrastructure", BindingFlags.Public | BindingFlags.Static); addInfrastructureMethod?.Invoke(null, new object[] { builder.Services, builder.Configuration }); }
方案对比
- 方案一:符合面向接口设计,扩展性强,新增基础设施实现(如替换数据库)时只需新增接口实现,无需修改上层代码。
- 方案二:实现简洁,适合小型项目,但需硬编码程序集和类型名称,灵活性较差。
内容的提问来源于stack exchange,提问作者Rakesh Kumar
相关产品推荐
相关产品推荐

