Use Types from a Project without Referencing It

By September 19, 2014C#, News

falafelcon_thumb6I’m excited to be speaking at FalafelCON this weekend, and my two sessions both demonstrate how to decouple applications from infrastructure concerns. This approach leads to more maintainable, modular, and testable software in my experience.  I recommend breaking up your solution into at least three projects (follows the Dependency Inversion Principle):

  • Core – no dependencies
  • Infrastructure – depends on Core, plus other libraries
  • UI – web, WPF, console, whatever the front end is; depends on Core but uses Infrastructure at runtime

The simplest way to get a solution structure like this to run is to set up the following project references:

Core – None
Infrastructure -> Core
UI -> Core
UI -> Infrastructure

When you build your solution, all of the DLLs will end up where they need to be so that your application can run.  However, this approach means that the UI layer “knows about” Infrastructure, which isn’t ideal. We don’t wan our UI to work with Infrastructure directly, we want it to work with abstractions defined in the Core project.  If we have a great deal of trust in our team, we can just establish this as “how we do things” and hope that, plus maybe some code reviews, is sufficient. However, we can actually enforce this (unless someone resorts to reflection) at compile time, by using types from a project without referencing it.  This can also be a great way to detect where your UI layer is directly referencing Infrastructure when it shouldn’t be.

Step One – Identify Problems

To find all the places where your UI project is directly referencing your Infrastructure project, simply delete the project’s reference to Infrastructure and lean on the compiler. Building the solution will show you a list of errors corresponding to your references. Ideally there will be few, and they will be in one place (your IOC container’s configuration class, for instance).  I’ll show how to address this using StructureMap in a moment – for any other direct references to Infrastructure, you’ll want to refactor these to use Core abstractions (interfaces, typically, injected via dependency injection).

Step Two – Move Type Registration to Infrastructure

StructureMap can be configured using a variety of techniques, but the current recommendation is to use one or more Registry types to do so.  If you’re new to StructureMap, see how to get started using StructureMap with ASP.NET MVC for reference.  Installing the nuget package StructureMap.MVC5, for instance, will provide you with a DefaultRegistry class.  You might set it up like this if you’re using a direct reference to Infrastructure:

Now that you’ve deleted the project reference to Infrastructure, you’ll see errors anywhere in this class that you’re directly referring to Infrastructure types (or namespaces).  Copy all of the Infrastructure-specific registration into a new InfrastructureRegistry class and put it in your Infrastructure project:

Remove the lines from the DefaultRegistry.  Instead, we now have to use another means of loading the assembly (in this case I’m simply using its name), and we need to tell StructureMap to find additional registries, which we do by calling LookForRegistries():

Now you can run your project and… watch it crash. We still have one last step to do.

Step Three – Post Build Action

StructureMap can’t load the Infrastructure assembly because it’s not in the UI project’s bin folder. Visual Studio takes care of copying referenced assemblies as needed, but once we eliminate the reference, the DLL is deleted and no longer copied when we build our solution. We can take care of this manually, though, with a Build Event.  Go to the Infrastructure project’s properties and choose the Build Events tab. Then add the following to the Post-build event command line:

Change “UI” above to be the name of your UI project’s folder.  This script works for ASP.NET projects; you may need to add a \debug or \release for other project types.  Build your solution and if you get no errors, your Infrastructure DLL should now be copied into your UI project’s bin folder and thus accessible by StructureMap.  At this point, your UI layer has no compile-time knowledge of the Infrastructure project, and thus you can leverage the compiler to ensure no errant references to Infrastructure leak into your UI layer.

Summary

Most software applications are built with tight coupling and dependencies on infrastructure. These poor design choices often result in short term development speed at the expense of long term velocity – the cost of poor design choices are not felt until an application needs to change. Following the approach shown here, you can eliminate dependencies on infrastructure concerns from both your user interface layer and your core business layer, and enforce this design approach via the compiler.

If you’d like to learn more, I’ve published several courses on Pluralsight, or let me know if you’d like to schedule an assessment of your project or training for you or your team. You can also follow me on twitter if you’d like to learn more about these kinds of topics.  Thanks for reading!

 

The following two tabs change content below.