Background
I am working on a class library in support of a web site. The library combines related APIs from several different vendors, all of which have their own particular nuances and domain objects. As an example, imagine there is a credit card provider that exposes a BankAccount
object, a loans provider that exposes an Account
, and a checking account system exposes an AccountDto
. The primary design goal of the library is to hide these various implementations and allow the web site to work with a single concept of Account
, with a set of services that decide which vendor to call and how to coordinate the data from the various sources.
To hide the confusion and keep the API clean, only my own domain objects and services are public
. Inside the library, each vendor has its own repository and domain objects. The vendor's classes are all marked internal
to keep the web developers from circumventing the services layer (either intentionally or unintentionally).
So the flow goes like this:
Web Site >> My API >> Service Layer (public) >> Repository Layer (internal)
This solution uses dependency injection and the Unity IoC container. My APIs' services are registered in the composition root and injected directly into the web sites' controllers as constructor arguments. All of this works fine.
The problem
Here is the problem I am struggling with. My services have constructor arguments that allow the repositories to be injected into them. But the repositories are internal
so the web site cannot register them in the composition root (without doing something truly bizarre with Reflection). Plus I don't want the web site developers to worry about injecting them.
So the question is: How do I inject the internal/private repositories into the public service layer, while adhering to the accepted conventions in a DI approach?
My proposed solution
The way I am doing this right now is as follows:
I have added an abstract RepositoryLocator
interface and class to the library. It has its own Unity container. It is left unsealed so that a unit testing project can derive its own version and substitute stub repositories.
public interface IRepositoryLocator
{
T GetRepository<T>() where T : IRepository;
}
public abstract class BaseRepositoryLocator : IRepositoryLocator
{
protected readonly UnityContainer _container = new UnityContainer();
public virtual T GetRepository<T>() where T : IRepository
{
return _container.Resolve<T>();
}
}
Then I added a DefaultRepositoryLocator
to my library that is hardcoded to provide the production runtime repositories.
public sealed class DefaultRepositoryLocator : BaseRepositoryLocator
{
public DefaultRepositoryLocator()
{
RegisterDefaultTypes();
}
private void RegisterDefaultTypes()
{
_container.RegisterType<IVendorARepository, VendorARepository>();
_container.RegisterType<IVendorBRepository, VendorBRepository>();
_container.RegisterType<IVendorCRepository, VendorCRepository>();
}
}
Then in my services classes, instead of injecting repositories, I inject the repository locator, and pull out the repositories that are needed in the constructor.
public class AccountService : IAccountService
{
internal readonly IVendorARepository _vendorA;
internal readonly IVendorBRepository _vendorB;
public AccountService(IRepositoryLocator repositoryLocator)
{
_vendorA = repositoryLocator.GetRepository<IVendorARepository>();
_vendorB = repositoryLocator.GetRepository<IVendorBRepository>();
}
}
The web development team's only task
In the web site's composition root, they just need to register the default repository locator in their Unity container.
container.RegisterType<IRepositoryLocator, DefaultRepositoryLocator>();
Once they do that, the Unity container injects the repository locator into all the services that are injected into controllers, and the services can retrieve the repositories they need.
Unit testing
In a unit testing project, the unit testing assembly will be given access to the internal members using the InternalsVisibleTo attribute, and the test project would substitute its own StubRepositoryLocator
conformed to the internal-but-now-public interfaces.
Things that are troublesome about this design
So, all you IoC experts, what sort of problems have I created? Here's what I worry about
- There are two Unity containers, one in the web application and one in the library's
RepositoryLocator
. I hear there really ought to be only one, but I also read that it is an anti-pattern to pass it around. - The RepositoryLocator is hardcoded for production runtime. I don't think harcoding is a cardinal sin like many engineers do, but I do regard it with suspicion.
The unit testing project will need InternalsVisibleToAttribute added to the library. This is definitely a code smell and I wonder how we would deal with it if the unit testing project changes its name or if there needs to be several of them.
The constructors for my services take one injection for the
RepositoryLocator
itself, instead of separate injections for the individual repositories. This conceals the individual dependencies at compile time.
Is there an approach that resolves these issues and is consistent with IoC best practices, while still concealing the repository layer behind internal
scope?
InternalsVisibleTo
is an important tool for unit tests, only using it outside the scope of tests is a code smell.