You are probably writing service classes wrong

Rahul Saha
Jun 8 · 5 min read
Photo by Clark Van Der Beken on Unsplash

Java EE along with Spring has become the de-facto standard of enterprise-grade java applications, especially web apps. But this article does not talk about Java EE or Spring, but the layers of a standard application.

In this article, I am going to challenge how most people design their applications, from the controller down to dao layer. However, my primary target will be the service layer, as I often see this layer to be abused the most.

To make it more interesting and relatable I will be going with an example problem. First I will show you a traditional design and inspect it. Next, I will show you a supposedly better design and point out what is better about it. Let’s begin.

It is a banking system that holds the accounts and money of people. The requirements are,

  1. Account needs to maintain a minimum balance.
  2. Money can be transferred between accounts via cheques complying to account balances.

The traditional implementation (anemic model)

Assuming we are using JPA, let’s create an entity class.

Similarly a DTO class,

And most importantly the service class (business logic),

If the above design seems okay to you, let me ask you the following questions,

Looking at the business problem can we agree that ‘Account’ is the primary domain entity here (and I don’t mean hibernate entity)? but in the design, the Account class does not have any behavior. The behavior is pulled out of the domain entity and written in service class.

balance is a property of the domain entity Account, however we are manipulating it from outside.

An object having only properties and no behavior is perfectly okay. Like the value object (AccountDTO) for example. But taking away an object’s behaviors from the object, partially or fully, is against the object oriented design principle.

There are 4 basic concepts of OOP: APIE!

  1. Abstraction
  2. Polymorphism
  3. Inheritance
  4. Encapsulation

Out of these 4, we have successfully destroyed abstraction and encapsulation.

a. The balance property of Account the class should have been encapsulated, but it is publicly accessible from the outside.

b. How balance, an encapsulated property of Account class, is updated is not at all abstracted from users of Account class. Rather the behavior is outside the class, so there is absolutely no abstraction.

You might say you don’t like OOP, you don’t follow it. That is fine. But abstraction and encapsulation are encouraged across all paradigms.

The service class is a stateless singleton, and that is by design. It is supposed to be a thin layer that orchestrates the business.

Controller layer: Responsible for communicating with the web client. The APIs exposed the contracts, HTTP response codes, etc. No business logic is supposed to be here.

Repository / DAO layer: Communicates with the data store. No business logic. Think of it as APIs for your data store.

Service layer: A thin stateless layer that coordinates the domain layer. It has no business logic inside.

This design of a thick service layer and domain objects without any behavior is called an anemic model.

A Domain Driven Design

As we saw the drawbacks of the traditional anemic model, let us rewrite the solution following the OOP paradigm.

A thin service layer. Unaware of any business logic.

The Cheque class,

And the Account class,

Following are the fundamental changes in this design,

  1. The Account class is encapsulating the balance property and abstracting the balance updating process.
  2. The implementation of processing a cheque is also abstracted in the Cheque class.
  3. The service class is free of any business logic or rules.

Now let us check what benefits our new design brings to the table,

Since our domain objects are very capable now, it is now significantly easier to add new features. At the same time, the domain objects enforce abstraction and encapsulation to protect the behavior from the outside. Read more about the Open-Closed principle https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle.

Suppose we introduce many more transaction services e.g. demand Draft service, online banking service. All these services deal with the account balance. Now if there is a change in balance deposit/withdrawal logic we will only be changing in the Account class.

The change will be transparent to these services. However, with an anemic design, we would be changing the code in all these services individually. This translates to significantly more development effort, testing effort, and more chances of introducing a bug.

We can now apply polymorphism and inheritance to our domain objects. And since I mentioned inheritance I would like to note on https://en.wikipedia.org/wiki/Composition_over_inheritance

Did you notice how I used interfaces in the domain-driven design? This was to reduce coupling among the service layer and domain layer (where domain objects reside) and as well as within the domain layer. In most cases, these classes know each other by interfaces i.e. contracts (interfaces are beautiful aren’t they?). So Cheque a class can work with any kind of Account as long as it satisfies the contract of IAccount. And there can be different types of accounts with different implementation/business logic. E.g. current account that charges 0.5% on every deduction and has no minimum balance requirement.

And the existing cheque service and Cheque class will work with this seamlessly. The same goes for poly-morphing the Cheque class.

Similarly, we can create an abstract class AbstractAccount with balance and account and extend our account classes from it i.e apply inheritance. But there are some drawbacks of using inheritance and thus it is often advised to use composition over inheritance.

And this is just for this business problem. Bigger projects which adhere to Domain Driven Design benefit in many more ways e.g. better communication among teams with ubiquitous language, minimal change impact, flexibility, only to name a few.

What is domain driven design?

The above solution is an example of Domain Driven Design, coined and popularized by Eric Evans in the famous book Domain-Driven Design: Tackling Complexity in the Heart of Software.

Domain-driven design is the concept that the structure and language of software code should match the business domain. — Wikipedia

It's a huge concept and the goal of this article is not to cover domain driven design but to give you a taste of it.

Conclusion

The anemic model is basically an antipattern and most people are following it without thinking about it. We should design our applications more consciously. In the long run, it will surely reward us. If this article got you interested in DDD, that is a win for me.

Javarevisited

Medium’s largest Java publication, followed by 13500+ programmers. Follow to join our community.