This article is an excerpt from my book Advanced Web Application Architecture. It contains a couple of sections from the conclusion of Part I: Decoupling from infrastructure.
This chapter covers:
- A deeper discussion on the distinction between core and
infrastructure code
- A summary of the strategy for pushing infrastructure to the sides
- A recommendation for using a domain- and test-first approach to
software development
- A closer look at the concept of "pure" object-oriented programming
A lot can happen in 9 years. Back then I was still advocating that you should unit-test your controllers and that setter injection is very helpful when replacing controller dependencies with test doubles. I've changed my mind: constructor injection is the right way for any service object, including controllers. And controllers shouldn't be unit tested, because:
- Those unit tests tend to be a one-to-one copy of the controller code itself. There is no healthy distance between the test and the implementation.
- Controllers need some form of integrated testing, because by zooming in on the class-level, you don't know if the controller will behave well when the application is actually used. Is the routing configuration correct? Can the framework resolve all of the controller's arguments? Will dependencies be injected properly? And so on.
The alternative I mentioned in 2012 is to write functional tests for your controller. But this is not preferable in the end. These tests are slow and fragile, because you end up invoking much more code than just the domain logic.
Where should it go?
If you're one of those people who make a separation between an application and a domain layer in their code base (like I do), then a question you'll often have is: does this service go in the application or in the domain layer? It sometimes makes you wonder if the distinction between these layers is superficial after all. I'm not going to write again about what the layers mean, but here is how I decide if a service goes into Application or Domain:
Is it going to be used in the Infrastructure layer? Then it belongs in the Application layer.