2

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?

3
  • If this assumption is right, then wouldn't the composition root knows too much about the application? Why do you think that this is a problem? The composite root is singular in the whole code and nothing is pointing / depending on it.
    – Laiv
    Aug 6, 2021 at 16:08
  • @Laiv from what I understand, because I see this as one layer (in my case the API) violating boundary lines of other layers. The whole point of design your software divided in layers and tiers is isolate concerns. If API knows hows to construct small little components of other layers, I see this as a clear boundary violation. What am I missing? Aug 6, 2021 at 16:13
  • Well, you are assuming that the composite root is part of the API layer, but it's not. You might think so because you initiate the root there, but it's casual. If there were no API layer, it would belong to the application or any other layer. Usually the one closer to the application entry point.
    – Laiv
    Aug 6, 2021 at 16:18

4 Answers 4

1

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.

1

No you shouldn't.

If you're working with objects, all objects receive their dependencies through their constructors. That's a given. So that means, you only need a single place where the object graph is defined. There's no reason to litter DI-framework-related stuff all over the application.

Another way to say this is: DI frameworks are infrastructure. Your remark about coupling to a DI framework is valid, objects should not be hardcoded to use a DI framework, nor any DI stuff.

2
  • What about DI frameworks that allows marking dependencies through properties for instance (via reflection)? I know one should avoid determine dependencies through anything rather than the constructor, but this is something I see in a lot of projects using different DI frameworks all around, which kinda contradicts what you said, doesn't it? Aug 6, 2021 at 16:17
  • @underthevoid It does. In short, there's the way a lot of people are doing it, and there's the way that will let you own your design, use the capabilities of your language much better, allow you to statically check for correctness, doesn't hide dependencies, etc. In most cases there is no reason to use a DI framework at all. Constructing an object graph explicitly is not that difficult, nor is it more code, but it is infinitely more readable. Aug 7, 2021 at 16:53
0

I'm not sure there is a generally accepted way for this to be done correctly. But I might describe how I have used a DI framework for several years.

This consisted of an application with several independent domains sharing a common infrastructure layer. Each part would register its own types in the container, and the application would manage everything to accomplish some task. We used unity container as the framework, and used child containers to some degree to separate the different components. So when starting a task the following might happen

  1. Create a subcontainer
  2. Let the domain for the task do its registrations
  3. Resolve the main class for the task and let it do its thing.
  4. Once done, the subcontainer would be destroyed, ensuring all resources are freed etc.

To you specific questions:

If this assumption is right, then wouldn't the composition root knows too much about the application?

This can be avoided by using a plugin-architecture, but in many cases it is perfectly fine for the 'composition root' to depend on everything else.

Dependencies to the Infrastructure layer will be set at the API layer. Where is the encapsulation there?

Not exactly sure what you mean here, but dependencies on the infrastructure layer can be made thru interfaces. Some layer, like the composition root, would need to decide on the actual implementation to use. But it is possible to have different composition roots, like one for testing that mocks out all of the infrastructure.

However if each layer could register its own internal dependencies, then wouldn't it couple a specific DI framework to the whole application?

Sort of, but as we used it there where not that many classes that directly depended on the DI framework. And the layers/parts do not have to map 1:1 to the project structure. So while some projects directly represented a domain, other where just for grouping common functionality. So you would not have to use the DI framework to reuse functionality. And while replacing the DI framework would be a lot of work, it would not be impossible.

0

One thing that hasn't been mentioned is that your Application's main layers aren't those that you mentioned. The main layers are two: Domain and Infrastructure. Under Infrastructure you get API and Application. Your Domain is where your abstracts live: interfaces, domain logic, etc. Under Infrastructure is where your concretes live: actual repositories, connectors, etc.

Under this view it's clear that the DI module goes in Infrastructure, because we are basically assigning concrete instances to the interfaces. Where exactly depends on your personal preference, but I'd suggest requiring the module as soon as possible. The main DI module can require different DI modules that you can place closer to your actual implementations.

For example, your main script requires the DI's entry point. DI's entry point then requires other submodules: if you had an Invoicing namespace, the Invoicing namespace could have it's own DI file where the different instances related to Invoicing are built. The fact that these submodules are in a namespace/subfolder doesn't mean that they don't belong to the Infrastructure layer. In fact, folder organization has nothing to do with application layers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.