Composition root looks very strange pattern. We have a single very large God object that knows everything about anything.
What's the correct way to split up a Composition Root to some modules that will encapsulate initialization of its own part of the object graph?
What about hierarchical dependency injection?
I don't agree with the premise that a Composition Root is a God Object. While, conceptually, a Composition Root may contain many lines of code, it has only a single responsibility: compose an object graph.
A Composition Root can contain much data, and itself be coupled to the entire application, but it'd conceptually have only one method. It has only one reason to change, and that is if you want to change the way your application is composed.
Also, while the Composition Root is coupled to the rest of the application, the application code knows nothing of the Composition Root. Thus, the coupling goes in only one direction, as it should, according to the Dependency Inversion Principle.
A way to put it is that a God object violates all five SOLID principles, while a Composition Root at the very least follows both SRP, ISP, and DIP. I don't think the OCP or LSP apply here.
All that said, if a Composition Root composes a big application, it could still contain much code. If you find that it becomes unmanageable, then how should you break it down into smaller chunks of code?
The same way you decompose any other code. It Depends™.
I hope I never wrote or said that a Composition Root can only be a singe class. If I did, I'd like to retract that. When writing Composition Roots, I routinely split its responsibilities over more than a single class (although rarely more than a handful). You can apply any design pattern or other OOD technique you'd like in order to achieve that goal. The Composition Root then becomes a Facade over your implementation.
I can't give much more guidance than that, because it depends on the application architecture. If you do vertical slicing, you'd most likely need to structure the Composition Root differently than if you do horizontal layering, and so on.
That said, as a general rule I do not find it useful to attempt to decompose a dependency graph into sub-trees. First of all because a dependency graph is a graph, and not a tree, but also because you often need to interleave multiple independent concerns. You could, for example, have sub-graphs that are responsible for various sub-parts of your application, but then you'd need to interleave the same logging mechanism, or the same caching mechanism, or the same security mechanism, etc. throughout the entire graph. You can't easily do that if you try to separate them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With