DEV Community

Cover image for Service Design Best Practices - Part I
Sameh Muhammed
Sameh Muhammed

Posted on • Updated on

Service Design Best Practices - Part I

Introduction

By now most softwares separating the server side logic from user interface logic by developing a back end web services, and make them accessible over the network to consumers either web applications or mobile applications or even another back end services, so developing a maintainable and reliable back end service is essential to deliver a good software, here we will discuss a bunch of best practices you should consider when you developing your back end service.

Unify Response Messages

Unifying response message means that you always return same object structure with all your APIs but with content based on requirements as below image.

Image description

This will make consumers' life much way better, because of static structure of your response, now he can put the logic that deals with the response in one isolated place and doing his checks in an organized way.

Use DTO Pattern

Dealing with your domain object (entities) in the request & response is a mistake you must avoid, because

  • API consumer may not need all attributes in your domain, so you will have to do work arounds to send the needed ones only.

  • API consumer may want to send data in different format than that you have in your domain object, and mixing both formats in one object will complex domain objects and make it harder to maintain.

So using DTO pattern will save your back from all this hassle and make your code more maintainable, but consider to use Mapper pattern to encapsulate the conversion back and forth between domain object and DTO.

We talked about DTO pattern with Java frameworks here

Because DTO is almost take the shape of domain object, there is some cases that this will mismatch with the UI and data will be sent from consumer, so you may need to add additional Request class to get the data from consumer and then map it to your DTO and start processing it as below.

Image description

Respond and Accept only Required Data

We are working with limited resources starting from RAM, CPU, network traffic, bandwidth and so on, in order to transfer data from source to destination, data will be serialized to bits then encapsulated with packets, then transmitted over network to the destination then deserialized again and all that stuff, so we need to decrease the amount of data we send and accept as much as possible, to use our resources in more efficient way and make our application scalable and reliable.

Image description

Add Exception Handling Layer

Dealing with exceptions is essential to deliver robust services, your service should always safely go down in case of any failure, so you should consider add a group of classes that deal with service exceptions, you should also consider create custom exceptions for your domain validations to deal with them in special way in your exception handling layer, most of frameworks now make it easy to deal with exceptions and encapsulate that logic in separate layer instead of mixing it with domain logic. you have also to make a general exception handler for the cases you may encounter in runtime.

Don't Mix Responsibilities

At the start of the application development, you may find some APIs that share same logic and requirements, like Create & Update API for an example, if you decide to make same API to serve both purposes, it will backfire you, because as application requirements grow, each case will grow with each requirements as well, starting from validations to the use case itself, it will start with small conditional statements, but then it will be a big ball of mud that no one can deal with.

Use Thin Service Layer

Most of developers use service layer to put all the domain logic (application requirements logic), neglecting the domain objects (entities) and keep them animc without any logic just attributes, getters and setters. this by time make service bloat with a lot of code and hard to maintain and hard to write test cases, so by enabling domain objects and adding logic to it will reduce complexity of service layer and make it more readable and maintainable.

Image description

Ideally, each service should contains its' repository class/interface only and have the logic that deals directly with its' domain object, and in case of any requirement that need collaboration of group of services, you can apply Facade pattern to achieve this.

Image description

Code with Contracts Not Concrete Implementation

Softwares made for change and match users need at anytime, and to make your services easy to change you should consider using interfaces while you coding the business logic, this will provide you with multiple benefits, first, the interface will only contain the needed operations and will not bother you with any additional helper methods that may your implementation class have, second and more important it will provide you with interchangeability, you can swap the implementation as much as you need without much changes in your code.

Image description

Resources

IF YOU LIKED THE POST, THEN YOU CAN SUPPPORT SUCH CONTENT WITH A CUP OF COFFEE, THANKS IN ADVANCE.

Buy Me A Coffee

Top comments (0)