I've been using Symfony to develop my web applications but I keep running into one problem. I always end up with too much clutter in the constructor of my services since I want to be able to unit test my services properly.
Let's say I need a service which allows me to process an XML file and save it's contents into the database.
<?xml version="1.0" encoding="UTF-8" ?>
<users>
<user>
<id>1234</id>
<username>Example User</username>
<email>[email protected]</email>
<usergroup>
<id>567</id>
<name>Example User Group</name>
</usergroup>
<permissions>
<item>ALLOWED_TO_CREATE</item>
<item>ALLOWED_TO_UPDATE</item>
<item>ALLOWED_TO_DELETE</item>
<item>ALLOWED_TO_view</item>
</permissions>
</user>
</users>
Already quite a few things come to mind which you need to inject into this service:
The real XML file I'm working with contains much more data which requires me to inject many more repositories and other services which handle certain logic.
Inject the Doctrine service directory into my service and get the repositories via $doctrine->getRepository(User::class)
Pros
Cons
Remove all services and repositories from the constructor and create setter methods and call then in the services.yml
services:
AppBundle\Service\MyImportService:
calls:
- [setUserRepository, ['@app.user_repository']]
Pros
Cons
What are solutions to make the argument lists in my services more maintainable in terms of readability and ability to unit test?
Is it even considered bad practise to have a long list of arguments?
Having many arguments in your constructor is a code smell called Constructor Over-Injection. This code smell is often an indication of the class taking on too much responsibility, meaning it violates the Single Responsibility Principle (SRP). SRP violations cause maintenance problems, which is why you should keep a close eye on them.
Although refactoring to Property Injection might reduce the number of constructor arguments and therefore the amount of clutter in the constructor, it does not solve the underlying problem, which is that this class is becoming too complex. Property Injection is therefore not a solution to Constructor Over-Injection.
The solution to this code smell is to reduce the complexity of the class at hand. There are many ways to do this, but a very common approach is the Facade Services refactoring:
Facade Service [is] closely related to Parameter Objects, but the main difference is that a Parameter Object only moves the parameters to a common root, while a Facade Service hides the aggregate behavior behind a new abstraction. While the Facade Service may start its life as a result of a pure mechanistic refactoring, it often turns out that the extracted behavior represents a Domain Concept in its own right.
These are topics that Mark and I in our book Dependency Injection Principles, Practices, and Patterns. Section 6.1, for instance, specifically talks about refactoring from Constructor Over-Injection to Façade Services or Domain Events, while chapters 9 and 10 do a deep dive into using Decorators.
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