Should all the dependencies be resolved the same way?
Should all the dependencies be resolved by the same party?
I wrote this post because I am seeing more and more projects in which all the dependencies for every class are injected through the class's constructor. Developers blindly apply dependency injection for every dependency saying it is the "best practice".
Dependency injection is a best practice in some cases. The purpose of this post is to help you not to abuse it.
Keep in mind that all the dependencies your class' constructor accepts must be resolved by the user of the class (e.g. some other developer), which means that in order for someone to reuse your one class, they have to resolve X dependencies, and since dependency count grows exponentially, X can be pretty large in some cases. Also keep in mind that the user of your class might not have the access to your dependency resolver (e.g. it has it's own process and entry point), so they will have to resolve the whole dependenciy graph by themselves, which can be challenging and affect the reusability of the class.
If you have a class that accepts all its dependencies through the constructor, all those dependencies must have the same level of accessibility as the constructor. So, if it is a public class with a public constructor, which is intended to be used by some external modules, all its the dependencies will have to be public. As a result, developers that are using that class will be able to access and depend on all the dependencies, even if some of them are just implementation details and should be internal. Moreover, since those developers are invoking the constructor of the class constructor, they will have to resolve and provide all the dependencies. This means that other developers will not only be able to depend on all the dependencies, they will HAVE TO depend on them, because it is their responsibility to resolve them and pass them to the constructor. This can drastically affect the encapsulation of the class. Those developers can also decide not to resolve to implementations from our module, but to implement their own. This way they can customize our class to fit their needs. However, since we are accepting all the dependencies through the constructor, including the ones that are just implementation details, they can inject badly implemented dependencies and make our class misbehave.
So, if you inject all the dependencies of a class through the constructor, you might get a risk of decreasing reusability, encapsulation and integrity of that class.
If we used some dependency container to resolve all those dependencies, there will be less code when instantiating A, but we would still have to map all those dependencies in the container. All of this, just to use one class.
Imagine the following situation: You are looking for some library you can download and include in your project that will help you solve some problem. Now imagine you found a library that seems useful, then you start reading the documentation and it says that in order to use one of the classes, you need to resolve 18 dependencies first. What would you do? I bet you would continue looking for some other, more (re)usable one.
We should decide which dependencies should we hide from the user and which ones should we leave to them to resolve. In order to do so, we should ask the following questions?
Which dependencies should be public?
Which dependencies can be used to customize or extend our class?
Which dependencies are just implementation details?
Dependencies that fall into first AND second category (dependencies that should be visible from the outside of the module and can be used to customize or extend the containing class or module) should be injected through the constructor of the class.
Dependencies that do not fall into first two categories, and especially dependencies that fall into the third category, should not be injected through the constructor of the class. By making the user of our class resolve those dependencies, we reduce the reusability of the class. If the dependency falls into the third category, making the user resolve it will also increase the fragility of the code.
Let's examine possible solutions on an example. We have BlogService class that we want to use to publish blog posts.
UserManagementService for handling things like who the user is, whether they are allowed to publish post etc.
IBlogRepository for retrieving and persisting post state.
INotificationSender for sending notifications about post publishing to blog owners and other people.
PostPublisher for preparing the post for publishing and publishing it.
I left out some of dependencies for the sake of simplicity. I also think that for the purpose of this example, it doesn't make a difference whether some dependencies are interfaces (whether we are inverting them) or concrete classes. For example, I could have added IPostPublisher interface and make BlogService depend on it instead of concrete class, but it wouldn't make any difference and it is a different topic.
Even if UserManagementService and PostPublisher are not implementation details, we don't need to make the user resolve them. Those dependencies are vital parts of BlogService and if we let user customize those parts (e.g. change the way user permissions work by providing different implementation of UserManagementService) we could violate the integrity of BlogService.
This way, we are letting the user of BlogService to use various notification mediums and database providers.
This way, we exposed some dependencies as public and allowed the user to customize the behavior of BlogService, but we didn't force the user to resolve our dependencies. E.g, if the user wants to provide a custom implementation of INotificationSender, they can do it, but they can also use one of the existing implementations without depending on them.
The code in the constructor that resolves dependencies based on options can be extracted to separate methods or organized to your liking. You could also make the constructor put data from BlogOptions to some place where factories can find it (e.g. static class) so it doesn't have to pass it to them.
There are also trade-offs with this approach, because you are making the class (in this case BlogService) aware of factories or dependency containers, so you should decide what is more important to you in which situation.
will use the mock of the repository factory, which will create a mock of IBlogRepository.
This approach is not perfect because it forces you to expose IoC or factories as static properties and access those properties from unit tests. However, in some situations it might be a lower price to pay than injecting all the dependencies through the constructor.
In order to preserve reusability, encapsulation and integrity of a class, you might want to move the resolving of some dependencies inside the class instead of leaving that responsibility to the user of the class. However, there are some trade-offs with this approach, so you should carefully decide when to use which approach and what is more important to you. The purpose of this post was not to make you think dependency injection is bad, but to help you realize if you are abusing it and what are the trade-offs.