清洁架构下无需直接项目引用如何将基础设施服务注册到服务集合?
Great question! This is a key concern in clean architecture—maintaining strict layer boundaries while still enabling the cross-layer service registration you need. Luckily, there are two reliable approaches using reflection/assembly scanning that let you register your Infrastructure layer services without adding a direct project reference to Presentation.API, keeping your dependency graph clean.
Approach 1: Direct Reflection to Call AddInfrastructure
If you want to stick with your existing Add{ProjectName} method pattern, you can use reflection to load the Infrastructure assembly and invoke the static AddInfrastructure method directly.
Step-by-Step Implementation
Ensure Infrastructure Assembly is Accessible: First, make sure the Infrastructure project's assembly gets copied to Presentation.API's output directory. You can do this by adding a project dependency (not a project reference):
- Right-click Presentation.API in Solution Explorer → Project Dependencies → Check the box for your Infrastructure project. This ensures Infrastructure builds first and its assembly is copied to Presentation's output, without adding a compile-time reference.
Reflectively Load and Invoke the Method: In your Presentation.API's startup code (e.g.,
Program.cs), use reflection to locate and callAddInfrastructure:using System.Reflection; // In your builder.Services configuration section var infrastructureAssemblyName = "YourSolution.Infrastructure"; // Match your actual assembly name var infrastructureAssembly = Assembly.Load(infrastructureAssemblyName); // Locate the class containing AddInfrastructure (e.g., InfrastructureExtensions) var extensionClass = infrastructureAssembly.GetType("YourSolution.Infrastructure.InfrastructureExtensions"); if (extensionClass != null) { // Get the static AddInfrastructure method that accepts IServiceCollection var addMethod = extensionClass.GetMethod( "AddInfrastructure", BindingFlags.Public | BindingFlags.Static, new[] { typeof(IServiceCollection) } ); // Invoke the method with your services collection addMethod?.Invoke(null, new object[] { builder.Services }); }
Pros & Cons
- Pros: Minimal changes to your existing code (reuses your
AddInfrastructuremethod directly). - Cons: Hardcodes assembly and class names—you’ll need to update these if you rename the Infrastructure project or the class containing
AddInfrastructure.
Approach 2: Marker Interface with Assembly Scanning (Recommended)
A more maintainable, scalable approach is to define a marker interface in your Domain layer (which Presentation already references) and have your Infrastructure layer implement it. This avoids hardcoding names and follows clean architecture principles.
Step-by-Step Implementation
Define a Marker Interface in Domain: Add an interface to your Domain project that all dependency registration modules will implement:
// In Domain project public interface IDependencyRegistrationModule { void RegisterServices(IServiceCollection services); }Implement the Interface in Infrastructure: Update your Infrastructure layer to wrap its registration logic in a class that implements this interface:
// In Infrastructure project public class InfrastructureDependencyModule : IDependencyRegistrationModule { public void RegisterServices(IServiceCollection services) { // All your existing Infrastructure registration logic goes here services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddScoped<IUserRepository, UserRepository>(); // ... any other Infrastructure services } }Note: You can reuse your existing
AddInfrastructuremethod here by calling it insideRegisterServicesif you prefer.Scan and Execute Modules in Presentation: In Presentation.API's startup code, scan all loaded assemblies for types implementing
IDependencyRegistrationModuleand invoke their registration methods:using System.Reflection; // In your builder.Services configuration section var moduleType = typeof(IDependencyRegistrationModule); var registrationModules = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes()) .Where(type => moduleType.IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) .Select(Activator.CreateInstance) .Cast<IDependencyRegistrationModule>(); foreach (var module in registrationModules) { module.RegisterServices(builder.Services); }
Pros & Cons
- Pros: No hardcoded names, follows open/closed principle (add new registration modules without changing Presentation code), and aligns with clean architecture's focus on domain-driven abstractions.
- Cons: Requires adding a small interface to your Domain layer (a minor, acceptable tradeoff for maintainability).
Key Notes
- Assembly Loading: Ensure the Infrastructure assembly is loaded into the AppDomain. The project dependency setup mentioned earlier will handle this by copying the assembly to Presentation's output directory.
- Type Safety: While reflection sacrifices some compile-time type safety, both approaches keep Presentation completely isolated from Infrastructure's implementation details—you won’t be able to reference Infrastructure classes directly in Presentation, which is exactly what you want.
内容的提问来源于stack exchange,提问作者Ryan Gaudion

