A few months back, I had a conversation with my colleague, Lorenzo Dee, about Service classes. “In many cases, you don’t need them… you can just have inject your Repositories straight into the Controller.” I was shocked! What kind of heresy is this! This is a violation of Separation of Concerns! Your codebase will be cursed with rigid, tightly-coupled code that would be hard to test and change! I argued all those points with him… well in a more polite way than I just wrote. Neither of us could convince the other.
Is the Service Layer Obsolete?
One advantage Lorenzo had in our debate was that he has much more recent project experience than me. The last time I contributed any code to an enterprise system that went to production was in 2006. I had started Orange & Bronze Software Labs the year before, and I quickly got so busy running the company that I couldn’t contribute code to real projects in a productive way. Up until that point, however, I wrote my projects along the three application layers of Presentation, Service & Integration (or Persistence).
I started web development using just plain old Servlets, which was all we had at the time. Then Struts came along. The problem that persisted even with Struts was in order to test your application, you had to deploy it to a Servlet Container. This made Test-Driven Development very slow and cumbersome, even specialized tools like ServletUnit or Apache Cactus.
The solution was to move as much logic as possible into ordinary classes that did not need to be deployed to a container, and thus could be tested with plain-old JUnit. This was, for me, one of the most tangible benefits of the Services.
To survive change, the core code of the system needed to be somewhere that wasn’t tied to any specific framework that could be changed next year. Again, this was where the Service Layer came in.
Even back then, you needed a way to support multiple client technologies – HTML, desktop (Swing or Eclipse RCP), RMI, then later web services (SOAP, JSON). Back then we didn’t have view resolvers, so we had to code a different Controller, or write a ton of if-blocks, to handle multiple views.
Again, coupling the rest of your logic to one specific view would be a disaster, so you needed push out your logic to independent layer, and the various Controllers would just sit on top of that layer. This, again, more than justified the Service Layer.
The biggest change were standards – formal standards like JPA, and de facto standards like the Spring Framework. Because of standardization, you now see the same technology stack (usually Spring + JPA/Hibernate) across tens of thousands of large enterprise applications, often going on a decade, and forseeably a decade more. The economic justification for a technology-independent layer became less relevant when those technologies look like they will never bee replaced.
And these technologies brought so much goodness – AOP, view resolution, declarative programming via annotations, convention-over-configuration… so much so that today, we find that the Service Layer has little to nothing to do, other than delegate to other classes – the very definition of the Lazy Class Code Smell!
What convinced me was when I found myself with a little free time. There was an internal application that needed to be rewritten, but none of the devs had the time to do it. I decided to give it a shot, knowing full well that I would probably not be able to finish before getting busy again. This was my first time using Spring MVC, and after writing my first Controller, and seeing how much of the usual work was now handled declaratively, it hit me!
Controllers are now very thin, and you can use the same controller for multiple views. They were also testable with just plain-old JUnit since you didn’t need to deploy them to a Container.
With both Controllers and Services having very little to do, my object-oriented training was screaming at me to just merge the two!
But one question for me remained – transactions. If you needed a transactional operation, can I add the @Transactional annotation to the Controller?
“Yeah, that’s the only time I make a Service.” Lorenzo said. “When I find that an operation needs to be transactional, that’s the only time I create a Service.”.
But even that, I now question. Check out this answer to a StackOverflow discussion on this very topic. There seems to be no technical hurdle to making Controllers @Transactional. This is something I need to verify with some code, but when I do, I’ll post my findings, and if things check out, I’ll change the title of this article to simply, “The Service Layer is Obsolete!”… no more question mark!