Avoid Referencing Infrastructure in Visual Studio Solutions

Avoid Referencing Infrastructure in Visual Studio Solutions

The dependency inversion principle states that your application’s abstractions should not depend on implementation details, but rather implementation should depend on abstractions. In Clean DDD architected applications, you’ll typically have a class library called Core (or something similar) which houses the domain model, including the main abstractions. Your implementation libraries (“Infrastructure”) will reference this project or assembly and implement the abstractions found within it (e.g. IFooRepository, IEmailSender). This works extremely well and produces modular, testable, loosely-coupled software in both libraries. But what about the front end of the application, the user interface layer?

At some point, you need to have the application wire up the abstractions to the implementations it will be using at runtime – you can’t run abstractions. You may use an IOC container to help with this, since they make it much easier to work with applications that follow the DI principle and the Explicit Dependencies principle. One of my favorite containers is StructureMap, which can easily be configured to support the decoupling even in the UI layer.

Normally, to wire up implementations from the Infrastructure project to interfaces in the Core project, you would have some code in the UI layer that would configure the IOC container. It would include something like this:

In the above example, IDoSomething would be defined in Core, and DoSomethingSpecific would be defined in Infrastructure. Of course, the only way for this to compile is if the UI project references both Core and Infrastructure, as otherwise the compiler won’t be able to evaluate the two types referenced. This has the unfortunate side effect of making all of Infrastructure available to the UI layer at compile time (just to enable this bit of IOC container wireup logic). New developers, or experienced ones who slip, might make use of Infrastructure types directly from the UI project, introducing unwanted coupling and harming testability.

What you really want is a project dependency structure like this one:

cleanguestbook references

In this solution, neither the UI layer (CleanGuestbookMvc5) nor the Infrastructure projects have any knowledge of one another. If a developer tries to use a type from Infrastructure directly within the UI project, they will get a compile error (that should remind them to instead use an abstraction from Core and some dependency injection).

There are two things you need to make this work:

  1. StructureMap has support for registries, which can hold rules for configuring the container when the application starts up. Put one registry in the UI project and a separate one in the Infrastructure project (so that the UI registry doesn’t need to know about any Infrastructure types).
  2. Configure a post-build step to manually copy the Infrastructure dll to the UI project’s output folder. Normally project references are automatically updated/copied as part of the build process. Without the project reference, you’re responsible for getting the assembly to the /bin folder, and a post-build step is an easy way to do it.

The DefaultRegistry.cs Class in UI:

Note in the above code listing the the “Infrastructure” assembly is referenced by name, rather than by using a generic (the generic reference won’t work without a project reference).

Now in the Infrastructure project, you need a registry as well (InfrastructureRegistry.cs);

Finally, in the Infrastructure project’s properties, on the Build Events tab, edit the Post-build event command line to add this:

infrastructurepostbuild

To demonstrate the solution working, reference an abstraction from the Core project in the UI project, and run the application:

cleanguestbookrunning

You can view the sample code for this example on GitHub.

  • Dave van Herten

    Just to make sure I’m understanding, the only thing this is actually stopping is the ability to reference the Implementation class from infrastructure specifically. The concrete implementation should not have been able to be injectable as the concrete shouldn’t have ever made it into the container and would likely have thrown a run-time exception. Is this correct?

    It is still a nice separation, but when I read through this initially (too quickly apparently) I was hoping it was preventing IRepositories from being referenced in controllers at all (likely in favor of a service interface).

    • ardalis

      I don’t personally have much issue with using repositories from controllers, provided there’s no other logic going on (e.g. CRUD methods on an API). This isn’t meant to prevent that, exactly, but rather to prevent the nastier code smell of referencing a specific implementation of a repository, or of a data access layer, in a controller. So, if you have a DbContext defined in Infrastructure, and you normally want your controllers (and services) to only use it through repository interfaces, this approach would keep someone from having a controller with var db = new Infrastructure.AppDbContext(); in it.

      To do something like what you want for preventing a particular interface from being used in a particular type of class, I would probably create an NDepend rule and use static analysis. It’s great for that kind of thing.

  • snielsson

    What about declaring infrastructure classes internal and then make a separate “Bootstrapper” assembly a friend assembly to the infrastructure assembly, by using an [assembly: InternalsVisibleTo(“BootstrapperAssembly”)] attribute in the infrastructure assembly. This way the bootstrapper assembly can register implementations, but the code in the UI assembly will not be able to access the infrastructure classes. Just an idea, have not tested it.

    • ardalis

      You could do that as well, if you want your IOC to be outside of your front-end/UI project. I’ve seen that done, too.

  • johnkors

    Instead of a hacky post-build step, just use this.

    “how-to-have-a-project-reference-without-referencing-the-actual-binary:”

    https://blogs.msdn.microsoft.com/kirillosenkov/2015/04/04/how-to-have-a-project-reference-without-referencing-the-actual-binary/

    • ardalis

      Nice, that’s a good option, too.

  • Driven-It

    I’ve come across this discussion before, and I’m sorry but using the “new/junior developer” argument is one of the worse reasons for doing anything. What exactly prevents a new developer from simply adding a direct reference to the unwanted assembly and use it? ..right nothing.. If you are concerned about new developers you need to guide them,mentoring, code reviews, peer programming, knowledge sessions, coding dojo’s whatever. Not adding more complexity to strong-arm some desired condition.

    In my view the UI project needs all these assemblies to function correctly, having explicit references even to those indirectly required is a bonus and makes it clear what moving parts are involved.
    I’ve seen solutions to this perceived problem involving adding IOC configurator to each layer of the application, making it ten times harder to change IOC or influence object lifetimes

    If you still dislike the references in the UI project for whatever reasons, the simplest solution is to add a IOC Configurator project, it can contain all the wiring, and it can be referenced by the UI project without burdening the UI project