Question

I have several apps witch build from one code base (using common modules). And my question: how to write composition root in this case?

Let's imagine simple dependency graph:

ClassBase --> ClassBase1 --> ClassA
    |
     --> ClassBase2 --> ClassB

1) First guess is to have one big composition root with all dependencies from all apps.

register ClassBase
register ClassBase1
register ClassBase2
register ClassA
register ClassB

2) Second guess is to have different composition root for each app. But in this case I have to carefully check what dependencies to register.

Compositon root 1:
    register ClassBase
    register ClassBase1
    register ClassA

Compositon root 2:
    register ClassBase
    register ClassBase2
    register ClassB

i.e. if I change ClassBase2 to depend on ClassBase1 I'll get runtime error.

So what approach is better to use in case of hundreds dependencies?

Was it helpful?

Solution

There is no secret sauce to solve these problems. When you do dynamic things like configuring an object graph at runtime, it is unavoidable that some errors may only appear at runtime. There is a tradeoff between the flexibility of IOC and static guarantees in your code. In general, you pay this price for any object-oriented design.

The best way to mitigate this is to recognize that the set of dependencies you choose in the composition root is code, even if it may appear as a configuration file. How can we prevent the code we wrote from blowing up in production? By testing it first. Specifically, this means that you should test the exact dependency container setup that you plan to run in production. This kinds of DI configuration is not comparable to configuration variables like “where should I put my logfile” that you can safely change during deployment. Any change to the DI setup is a code change that should be QA'd first.

This doesn't render IOC useless. This can still make it easier to quickly produce a reconfigured release. The main value for application-level dependency injection is that you can inject mocks for unit testing. Inversion of Control as a smaller-scale design strategy is still valuable (often, in the form of callbacks).

Back to your main question: should you have one big composition root or multiple smaller ones? Three thoughts:

  • If that configuration is code, then structure it appropriately. This often means splitting a big chunk of things into smaller chunks of things that are more cohesive. If your two applications are essentially separate, then configuring them separately sounds sensible.

  • We can also ask under which conditions this configuration would change. Would both always change together, or might they change separately? Code that changes together should stay together.

  • In many applications, you're never doing significant changes to the production DI configuration, but only use DI for testing. Then, having a single big composition root can be very inconvenient for your tests, and you might benefit from a finer structure.

Licensed under: CC-BY-SA with attribution
scroll top