Services - Please, make 'em SOLID

Among many dirty things I've seen in my years as a "Java Software Developer", badly cooked services were perhaps one of the most common. Something that makes me cringe every single time I see them, to this very day. At the moment of writing this post I have only 2+ years of Java enterprise experience and I've already seen so many of those damned abominations that if I never see one again - it will be one too many.

Now before anyone starts to throw tomatoes at me, I want to clarify one thing - I don't really have any problem with services per se. What I'm talking about here is their poor implementation that I repeatedly see again and again, no matter what type of dependency injection framework is used - Spring, OSGi, Guice - you name it. So what's exactly so wrong with those services that I decided to rant about them? Long story short - they're doing too many things by themselves.

Imagine having something like FooBarServiceImpl, with dozens of methods to work with entities of FooBar and perhaps some stuff related to them:

class FooBarServiceImpl extends FooBarService {
 // createFooBar(...)
 // deleteFooBar(...)
 // updateFooBar(...)
 // selectFooBar(...)
 // ...
}

And of course, most of those methods have variations. Like selectById or deleteByName. And perhaps at some point we'd like to have batch operations too and our poor FooBarServiceImpl becomes even bigger. Now there is nothing exactly wrong with that - except that following "Interface Segregation Principle" you'd probably want to consider ... well, segregation. But that's not the big bad problem I'm talking about here. Having many methods for your service is something we can probably live with. Until we try to implement all or most of them in that very service, that is.

This is a pattern I've seen many times. For some reason people just fail to realize the importance of first 2 SOLID principles. Now I'm sure you know them, but here they are:

  1. Single Responsibility Principle - software entities should have only one responsibility and reason to change.
  2. Open Closed Principle - software entities should be open for extension, but closed for modification.

Those are best practices. They should be followed. And it's not even that hard to apply them to services as long as one actually thinks how he or she is going to test the damn thing. The problem here is that FooBarServiceImpl is not supposed to implement all of those methods. He is supposed to delegate the calls to those components we inject, each with their single responsibility. One can use composition for that, but - ideally - we use aggregation with application of "Dependency Inversion Principle". It's seems like a bit more work, but it's totally worth it, especially when it comes to testing.

The bottom line is: FooBarServiceImpl should have only one responsibility - to act like a Facade , aggregating all the necessary components and delegating all the actual work to them. It should hever become a monstrosity with dozens of methods and implementations for them, because it quickly becomes huge, hard to reason about and very hard to test. So please, don't hesitate to create new components for your service whenever you need them and use whatever DI-framework you use - if any - to inject those small and reusable pieces into your services. Please, make 'em SOLID.

Learn Something New Everyday,
Connect With The Best Developers!

Sign Up Now!

& 500k+ others use Hashnode actively.

No Comments Yet