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:
So when you say:
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.