Well, you care when you have an unexpected complex circular dependency or you need to refactor and you suddenly discover how complex your dependency graph has unintentionally become.
By "decoupling" and "modularization", I'm not talking about at a code-level by using interfaces. In my opinion, that kind of decoupling and modularization isn't very important, especially when it comes to application glue--because the glue (Spring) is what decides the backing impl's of all those interfaces. That's where the important dependencies lie, and that's where your big ball of mud accretes.
I just came across a controller in our code that had over 50 spring injected services. Our entire code base is only like 5 years old.
The problem is that this is my first job out of college... even if I know things are messed up, I don't really have an example of an application like this that is actually done right.
The same should apply to repositories unless it is absolutely necessary. It should ideally just know enough to manage itself
A service may reference other services if it needs to coordinate actions. We need to be careful not to make huge services that contains too much logic though
I have found that this pattern makes it not only easier to structure the application, but also much easier to make mocked tests.
I and others have generally found that using Constructor injection greatly decouples a design and simplifies testing.
And the Spring team have been saying so for at least as long as I've paid attention to the question[0]:
> Constructor-based or setter-based DI?
> ...
> The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.
> ...
[0] https://docs.spring.io/spring/docs/current/spring-framework-...
The main reason to favor constructor based injection is that it stops objects becoming 'live' in an inconsistent state. I've seen actual production issues with setter based injection that can only be explained by the object being available for use before all the setters have been called.
Constructor injection in Spring is more test-friendly; in turn that tends to allow us the liberty of more aggressively modularising the design. With other injection types either more bookkeeping is required to cleanly set up tests; or alternatively, "unit" testing becomes more integration-esque.
It's a second-order effect felt in the many small decisions that lead one to the mudpit.
That's why I used the analogy of dark matter. It's not directly observed, but it has gravity.
These are my thoughts too. No reflection or @MoreAnnotations to inject dependencies. I have an aversion to setter/field based injection because it allows you to instantiate an object in an incorrect state.
Oh yes. I recognise that JavaBeans had its reasons for being, but then you get to JPA and it's time for all the boggling.
Annotations are fine for intra-project code contracts, but using them for external validation means that if you, say, deploy on a application server with an older version of Jersey, you're suddenly SOL when none of your validation logic is executed.
If you get in the habit of using the Spring test support, where classes under test get injected, then all kinds of injection are equally friendly, and the tests don't give you feedback on the weight of the dependencies because Spring does the heavy lifting.
And, in my experience, where you do inject dependencies by hand, the design pressure is actually towards coarser-grained objects. Why? Because you need to have enough functionality to test; with fine-grained objects, you have to construct and assemble an object graph in the test, whereas with coarse-grained objects, you just new up one thing.
How do you handle the Spring context start-up time? In Spring projects I've worked on, across hundreds of unit tests it's non-trivial. But I don't make any claim to know if we were doing Spring right or not.
Running a single test is a bit of pain, but running the whole suite is not a lot slower than if it didn't use Spring.
Or you can use mocks and Spring Boot's test annotations.
But with field/setter injection, that choice isn't as easy.
It is the failure of concepts so if the tool brings the right concepts, it helps avoid such design mistakes in the first place.