I'm working on an application that will run on multiple systems and may use different modules to communicate with external systems, but on each system, only one module will be used at a time. As it should be possible to change the used module on a specific system, the same application with all modules should be deployed to all systems. For simplicity, let's assume that there are two modules called Foo
and Bar
.
Both modules have their own module descriptor that registers the module components to the dependency injection container:
public class FooModule : IModule
{
public void Configure(IServiceCollection services)
{
services.AddTransient<IService, FooService>();
// Register dependencies of FooService
}
}
I know that Autofac supports modules out of the box (even with support for configuration) and there are several libraries that add such a feature to Microsoft.Extensions.DependencyInjection
, but I want to ask this question with a general look at the concept of dependency injection.
If the services of all modules should be used at the same time, I would be done. Given they implement the same service, I could inject them using IEnumerable<IService>
. But in my use case, there is a component that requires a single IService
and I want to select the implementation based on a configuration (e.g. from a file).
Now there are several approaches where to apply that configuration and I'm not sure which one should be preferred:
1st approach - load single assembly
I could read the configuration and then load only the external assembly that contains the IModule
that should be used. This would require the introduction of some "magic" link between the configuration values and the names of the module assemblies, as the core application should not know the extension modules beforehand.
2nd approach - call single module
All the assemblies are loaded, but using a link between the configuration values and the names of the module classes (or namespaces), only the one module that should be used will be called to setup the passed IServiceCollection
.
3rd approach - let the module decide
The modules decide on their own if they are configured and therefor should provide their service implementation. Instead of evaluating the configuration in the core application, the configuration gets passed to the modules:
public class FooModule : IModule
{
public void Configure(IServiceCollection services, IConfiguration configuration)
{
if (configuration.GetSection("foo").Exists())
{
services.AddTransient<IService, FooService>();
}
}
}
4th approach - use some DI container feature
I know that Autofac or other DI containers support named / keyed service registrations that would basically solve this problem and therefor answer this question for me. However, I guess there is a reason why other DI containers like Microsoft.Extensions.DependencyInjection
do not provide this feature.
What approach would you prefer? Is there an approach or some variation that I missed? Is there anything else I should keep in mind when designing a system in that way?
Microsoft.Extensions.Configuration
, but then I would be bound to their keys for registering modules or components.