Dependency Injection — Composition Root

Chris Fryer
6 min readApr 20, 2018

The Dependency Injection Pattern — Composition Root

Photo by Andrew Preble on Unsplash

Part 1: Dependency Injection — A general breakdown

Note: The contents of this article are my opinions, not the views or opinions of my employer or other entity.

The Composition Root Pattern

The composition root pattern is (in my opinion) the next evolutionary step after learning dependency injection. Once you fully understand dependency injection, you are able to introduce the next logical steps; predictability and structure around how dependencies are managed. Composition Root brings this to the front of the line, and gives engineers confidence around the structure of their application and how data flows. This is one of those patterns that give you the feeling “oh my god, why didn’t I think of this, why isn’t this how everything is structured?”.

All dependencies are defined, constructed, and injected into the container as soon as possible in a process. This may slow down the init of your service by a few seconds, but it gains you one incredible positive: On request, the dependency chain is 100% pre-calculated and in some cases, pre-constructed. This keeps response times incredibly low and consumes very little memory. You also fail fast, in the sense that you will know before users are brought into the service, if something is not working correctly.

Depending on the container you chose, there are additional features that become available to you when you adopt this solution. One of these is circular dependency detection. This may be difficult to catch or debug when you simply keep a running list of registers in a cs file. Heck, a whole new level of unit testing becomes available to you which we will also cover below.

Note: There is an example github project at the bottom.

Through the course of this post, keep one thing in mind:

The API knows about the container, and nothing else. The container knows about everything else.

Principals

  • Single location for object construction: Entire dependency graph is discover-able in one location.You can reference installers from other assemblies.
  • Assembly Scanning allows the container to discover your dependency graph for you: Ensures all dependencies of a certain type are located and constructed identically.
  • Build a Lean Container: The composition project is a map of how your dependencies are constructed, how they interact, and how they are resolved. Do not register items in your container that may be optionally consumed. Avoid auto-wires if possible.
  • As close to Init or Entry Point as possible: As soon as we have an active process, we want to construct the container. The only dependency construction allowed before Composition is logging.
  • Pass configuration to the container for object construction: The container has all configuration necessary to build ALL dependencies.
  • Predictable Dependency Graph: All calls to the project should have a pre-constructed, pre-discovered dependency chain. No dependencies are built on the fly.
An example solution where an API uses a Composition project to construct the container, and leverages the Installers/Builders to register dependencies.

Things to avoid

  • Stick to constructor injection: Do not mix injection strategies. Where you cannot use constructor, use a Dependency Resolver.
  • Avoid one-off registrations: Register using an installer or builder. (depending on your container)
  • Never register an item in your container outside of your Composition Root project. Certainly not during units of work.

Lean Architecture

A predictable dependency graph is incredibly important when you start building lean and scalable services. Just knowing how/when your dependencies are constructed and how their dependencies behave is crucial. A valuable piece of knowledge is having visibility into how newly added dependencies are co-mingling with pre-existing dependencies, and if they are not, failing fast if dependencies fail to resolve correctly or are registered incorrectly.

The biggest gain is upon request, a good portion of your dependency graph should be pre-constructed and ready for your consumption. Resolving is expensive, especially if you have a lot of components registered.

Artillery.io test showing an average 8ms response time against a .net core micro-service using the Composition Root pattern. This test hit a controller for 60 seconds, with 30 new virtual users arriving every second. Layers: Controller <-> Manager <-> Entity Framework <-> Sql Database

Assembly Scanning

A common practice in (DI) Composition Root is assembly scanning. This means reading the assemblies in your solution for concrete implementations that match a specific signature so the container can register them automatically. Example: Instead of registering all of your managers individually, you register all classes that implement IManager. Any engineer who creates a new manager, as long as they use the correct interface, the container will handle everything for them. No need to go add a registration for that class!

One common issue when using Assembly Scanning is fighting JIT. When a .net process is running, it does some clever stuff to save memory and optimize itself. One of those things is how JIT handles referenced assemblies. Only assemblies that have a physically referenced class will be loaded into memory. So if you reference another assembly in your project, unless you call something within it, it will not be present in memory for Assembly Scanning to find it. Below is an example of Assembly scanning which also takes into consideration the JIT issue.

Example: Here is an AutoFac example of assembly scanning to register items that inherit from the BaseService class. I placed an Init class in the assembly and called it. This is done to ensure JIT has loaded the assembly in memory. This is a highly generic example to illustrate the issue, many containers such as AutoFac solve this by poking referenced assemblies for you if you use a function such as GetReferencedAssemblies();

Unit Testing

One of the benefits to the Composition Root pattern testing your code, the same way its implemented. A cost to mocking out methods, dependencies and objects is you may make a mistake in your mocking that allows the test to pass, without accurately testing the functionality. Having a single point of registration, as well as following the Installer/Builder pattern allows us to unit test our code with the same exact container in the same state as the application we are testing. This eliminates (for the most part) false passes due to poorly mocked tests.

Example: We build a container for our tests and then attempt to resolve a controller.

You also have the freedom to replace registrations in your container, with mock versions. For example, instead of using my IDbContextFactory to resolve Entity Framework DB Contexts, I may want to replace it with one that always returns a Mock DBContext or one with the default provider to to InMemory.

Another aspect to unit testing that becomes available to you, is unit testing the dependency graph and/or the container itself. Imagine testing that the container is able to construct the entire graph, in the correct order, and resolves dependencies in an adequate amount of time.

Controlling Configuration

One of the coolest functions of this pattern is controlling how configuration is handled in your application. You can easily use it to standardize how configuration is handled, or who has access to it. Your container should have access to configuration to construct all the necessary dependencies and register them. The challenge, only include configuration in memory for a handful of milliseconds:

Once your configuration is handed to your container and the dependencies are constructed… Why not just dispose of your configuration in memory? Always protect any internal variables that contain keys, connection strings, or sensitive configuration. If you cannot protect the variable (you don’t have src access to the object), abstract it behind a factory.

There is no silver bullet, but this is a fun challenge I love to solve or watch others solve!

Conclusion

As I said earlier, the composition root pattern is (in my opinion) the next evolutionary step after learning dependency injection. It takes your understanding of Dependency Injection and builds on those principals without changing directions. This pattern should approachable to those who have a real interest in creating lean structures.

This pattern gives engineers an easy to adopt framework where things “just work” while also providing a depth at which they choose. If they want to learn how a dependency is constructed, how changing a dependency creates a ripple effect, or how two dependencies may work together; they can choose how deep they want to go.

Here is an example project for you to explore on your own: GITHUB

--

--