Can I Have Too Many Dependency Injections?
Static analysis tools limit the parameters included with a method or class constructor. But are there cases where this needs to be adjusted?
Join the DZone community and get the full member experience.
Join For FreeConsider this scenario ...
You have the opportunity to work on a new API for your customer. The directive (or your decision) is to use Spring Boot and dependency injection. You start coding — inspired and excited for the new project.
You may also like: Spring Core: Dependency Injection
As a developer or designer, you want to make your services as focused as possible. Meaning if you have a CustomerService
class, it is not doing things with some widget in your application. You decide that the WidgetService
class will handle such things. Seems logical.
Your design continues to thrive. Taking a step back, you can easily comprehend the responsibility for all of your service classes — by simply reviewing the list of class names from the file system. You don't even really have to open the Interface
for the classes to have insight into a given classes responsibility.
To illustrate, here is an example:
services/
├─ AccountService.java
├─ CustomerService.java
├─ ...
├─ SalesService.java
├─ UserService.java
└─ WidgetService.java
You are proud. This application will be really easy to support and maintain.
Then Things Get Complicated
Imagine that the list of services is more than the five simple classes noted above. Maybe there are fifty or even one-hundred service classes in place when you finish the initial design.
Now, you get to the point where you need to introduce a service that provides cross-service functionality. Like perhaps some global reporting aspect of the application?
Your ReportService
class is introduced as an interface. The implementation class might look like this:
@Service
public class ReportServiceImpl implements ReportService {
private AccountService accountService;
private CustomerService customerService;
private DService dService;
private EService dService;
private FService fService;
private HService hService;
private IService iService;
private JService jService;
private MoreServicesService moreServicesService;
private SoMuchTypingService soMuchTyingService;
private SalesService salesService;
private UserService userService;
private WidgetService widgetService;
public ReportServiceImpl(@Lazy AccountService accountService, @Lazy CustomerService customerService,
@Lazy DService dService, @Lazy EService eService, @Lazy FService fService,
@Lazy GService gService, @Lazy HService hService, @Lazy IService idService,
@Lazy JService jService, @Lazy MoreServicesService moreServicesService,
@Lazy SooMuchTypingService soMuchTypingService, @Lazy SalesService salesService
@Lazy UserService userService, @Lazy WidgetService widgetService) {
this.accountService = accountService;
this.customerService = customerService;
this.dService = dService;
this.eService = eService;
this.fService = fService;
this.gService = gService;
this.hService = hService;
this.iService = iService;
this.jService = jdService;
this.moreServicesService = moreServicesService;
this.soMuchTypingService = soMuchTypingService;
this.salesService = salesService;
this.userService = userService;
this.widgetService = widgetService;
}
/**
* {@inheritDoc}
*/
@Override
public List<SomeObject> someReportingServiceMethod() {
// Here we go ....
}
}
In the example above, there are quite a few dependency injections taking place. These don't have to all be services. In fact, most of them could be injections to interfaces defined as extensions to the JpaRepository
API.
However, static analysis tools (like CheckStyle) may report that there are too many parameters of a method or a constructor. With CheckStyle the default is 7, which represents about half of the parameters in the ReportServiceImpl()
constructor.
The ReportServiceImpl
class would fail to build if CheckStyle default functionality is required to pass in the CI/CD pipeline.
Approach Justification
For the example ReportService
, assume there is a business need to reach into all of these aspects of the application (either via a service or a repository) in order to provide the expected results to the customer or client.
Time was taken to make sure each service is fine-grained and focused on performing aspects that match the name of the service. Again, making sure the CustomerService
is not doing something the WidgetService
should be doing.
With the reporting service, there is a need to reach into each of the injected services in order to retrieve data that will be reflected in the reporting. As a developer, I could have blurred the lines and said "all of the reporting data will be maintained in the report service" — but I would still end having to inject the repository interfaces to access the actual data.
Thinking outside the box, an alternative approach could place the burden on the client to make the necessary calls into each service of the API and build the report client-side. While this is certainly possible, some items to consider are noted below:
The potential to have duplicate reporting logic across multiple client applications, thus increasing the probability for incorrect data
The impact on network traffic to make several API calls to retrieve the necessary information
The impact on performance for making a client process logic that is probably better suited for (back-end) server-side processing
In the end, the reporting needs (or whatever the driver) must be provided in order to meet the functional needs of the application. Telling the product owner that requirements need to be changed or removed, because there is a goal to not deviate from default properties of a static analysis tool, just may not be acceptable.
Conclusion
Considering the example above, I feel like the best approach toward meeting the needs of the application is to utilize several dependency injections within the public constructor of the service class.
While it may not be common to see as many dependency injections as noted above, I feel like this is a realistic possibility. What I don't know is, what will be the impact if say one-hundred services or repositories were to be injected into a single service? Does the dependency injection start to fail at some point?
Certainly interested in feedback to this concept. Hopefully, I have established a scenario that provides an example where several injections are required to meet the needs of the application.
Have a really great day!
Further Reading
Spring Core: Dependency Injection
Spring DI Patterns: The Good, the Bad, and the Ugly
How Dependency Injection Works in Spring Java Application Development
Opinions expressed by DZone contributors are their own.
Comments