Dependency Injection – Should Dependencies Be Divided Between Tiers and Layers?

dependency-injection

I'm developing a microservice application divided into 4 layers (API, Application, Domain and Infrastructure).

The composition root of my application is an assembly in the API layer. From what I understand all the binding and registering of dependencies in an application using a DI framework should be done in the composition root (aka the API layer).

If this assumption is right, then wouldn't the composition root knows too much about the application? Dependencies to the Infrastructure layer will be set at the API layer. Where is the encapsulation there?

However if each layer could register its own internal dependencies, then wouldn't it couple a specific DI framework to the whole application? I would also need to pass the container between the layers so each layer can register its stuff.

So, how should that be done correctly?

Best Answer

Why don't we ask the guy who invented the term Composition Root, Mark Seemann:

Where should we compose object graphs?

It's easy to understand that each class should require its dependencies through its constructor, but this pushes the responsibility of composing the classes with their dependencies to a third party. Where should that be?

It seems to me that most people are eager to compose as early as possible, but the correct answer is:

As close as possible to the application's entry point.

This place is called the Composition Root of the application and defined like this:

A Composition Root is a (preferably) unique location in an application where modules are composed together.

This means that all the application code relies solely on Constructor Injection (or other injection patterns), but is never composed. Only at the entry point of the application is the entire object graph finally composed.

The appropriate entry point depends on the framework:

In console applications it's the Main method
In ASP.NET MVC applications it's global.asax and a custom IControllerFactory
In WPF applications it's the Application.OnStartup method
In WCF it's a custom ServiceHostFactory
etc.
(you can read more about framework-specific Composition Roots in chapter 7 of my book.)

The Composition Root is an application infrastructure component.

So when you say:

From what I understand all the binding and registering of dependencies in an application using a DI framework should be done in the composition root

If this assumption is right, then wouldn't the composition root knows too much about the application? Dependencies to the Infrastructure layer will be set at the API layer. Where is the encapsulation there?

You're right. Kinda. When construction happens in one place that place ends up knowing how to construct everything. But that place doesn't have to know how to talk to anything. Well except maybe a lone objectGraph.run() method. What's being separated here isn't your layers. It's construction logic from using logic.

Now sure, this can be an unachievable fantasy. How do you construct timestamps like this? No there are ephemeral objects that have to be built later. But even then it's still best to put them as high up the call stack as you can.

What you're proposing leads to each layer knowing how to build itself. Which causes exactly the same problem we have with classes that know how to build themselves. With no way to override each layers opinion of how to build itself we're only left with one way to build. Not very flexible.

With a fair bit of effort you can make each layers opinion an overridable default. You can then let main, or whatever you called the composition root, ask each layer to build itself, either in the default way or some different way. This gives you a form of convention over configuration.

You'll know if you've done this correctly if unit tests can easily swap out layers and objects within layers without using mocking tools or reflection magic to sneak past access modifiers.

The danger is you can end up letting the convention do so much of the hard work that building configuration style becomes far to difficult. Writing unit tests early helps keep you from doing that.

But even done this way construction code is still volatile code. It changes often. We want to separate that which changes from that which stays the same.

So yes, you can decompose the composition root so it's not a flat run through procedural code. But keep construction and use logic separated or your DI isn't buying you anything.