We are building an application which has a number integration touch points with other systems. We are effectively using Unity for all our dependency injection needs. The whole business layer has been built with an interface driven approach, with the actual implementation being injected at an outer composition root during bootstrap of the application.
We wanted to handle the integration layer in an elegant manner. The business classes and repositories are dependent on IIntegrationController<A, B>
interfaces. Several IIntegrationController<A, B>
implementations together represent integration with one target system in the background - forming an integration layer. Currently, we hook up everything in the composition root, one shot, at the beginning. The consumers of this interface are also registered with an appropriate InjectionConstrutor
and ResolvedParameter
up front. Most types are operating with a PerResolveLifetime
and the business classes consuming the IIntegrationController
are also resolved for each request context separately.
Refer code below.
// IIntegrationController Family 1
// Currently the default registration for IIntegrationController types injected into the business classes
container.RegisterType<IIntegrationController<A, B>, Family1-IntegrationController<A, B>>();
container.RegisterType<IIntegrationController<C, D>, Family1-IntegrationController<C, D>>();
// IIntegrationController Family 2 (currently not registered)
// We want to be able to register this, or manage this set of mapping registrations separately from Family 1,
// and be able to hook these up dynamically instead of Family-1 on a per-resolve basis
container.RegisterType<IIntegrationController<A, B>, Family2-IntegrationController<A, B>>();
container.RegisterType<IIntegrationController<C, D>, Family2-IntegrationController<C, D>>();
// Repository/Business Class that consume IIntegrationControllers.
// There is a whole family of IIntegrationController classes being hooked in,
// and there are multiple implementations for the family (as shown above). A typical AbstractFactory scenario.
container.RegisterType(typeof(Repository<Z>), new PerResolveLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IIntegrationController<A, B>>(),
new ResolvedParameter<IIntegrationController<C, D>>())
);
Problem Statement:
We want the ability to switch an entire family of IIntegrationController<A, B>
at runtime. When the business class is being resolved, we want it to be injected with the right version of IIntegrationController<A, B>
, based on a request parameter available in the context.
IIntegrationController
is also resolved through Unity, as it is injected into another class dynamically.DependencyOverride
and ResolveOverride
class during resolution, but that would require the whole set of Family-2 IIntegrationController
resolutions to be overriden, instead of just being able to switch the whole layer.InjectionFactory
? This link suggests an approach, but we were unable to get it to work smoothly.What's nice about your design is that you already have the right abstractions in place. You use generic abstractions, so the problem can be solved simply by applying the right patterns on top of your already SOLID design.
In other words, use a proxy:
// This class should be considered part of your composition root.
internal class IntegrationControllerDispatcher<TRequest, TResult>
: IIntegrationController<TRequest, TResult>
{
private readonly IUserContext userContext;
private readonly Family1_IntegrationController<A, B> family1Controller;
private readonly Family2_IntegrationController<A, B> family2Controller;
public IntegrationControllerDispatcher(
IUserContext userContext,
Family1_IntegrationController<A, B> family1Controller,
Family2_IntegrationController<A, B> family2Controller) {
this.userContext = userContext;
this.family1Controller = family1Controller;
this.family2Controller = family2Controller;
}
public TResult Handle(TRequest request) {
return this.GetController().Handle(request);
}
private IIntegrationController<TRequest, TResult> GetController() {
return this.userContext.IsInFamily("family1"))
? this.family1Controller
: this.family2Controller;
}
}
With this class you whole configuration can be reduced to about this:
container.RegisterType<IUserContext, AspNetUserContext>();
container.RegisterType(
typeof(IIntegrationController<,>),
typeof(IntegrationControllerDispatcher<,>));
container.RegisterType(typeof(Repository<>), typeof(Repository<>));
Note the following:
IntegrationControllerDispatcher
depends on them directly. This
class is a piece of infrastructure logic and should be placed inside
your Composition Root.IUserContext.IsInFamily
in this case, but that's just an example of course).