
Reusing code in microservice architecture - SPRING BOOT as an example
- Transfer
- Tutorial
Hello, Khabrovites! Today we offer you another interesting post on the inexhaustible topic of microservices, this time for luminaries and neophytes of the Java language. Read and vote!
In most microservice architectures, there are a lot of opportunities for sharing code - accordingly, the temptation to do this is great. In this article I will share my own experience: I will tell you when it is appropriate to reuse the code, and when it is better to avoid it. All points will be illustrated by the example of a special project using Spring Boot, which is available on Github .
INTRODUCTION
Before we talk about code sharing and what is behind it, we will determine what tasks are usually solved using microservice architectures. Here are the key benefits of implementing microservices:
Naturally, many of these advantages make it possible not only to build a better system, but also to facilitate the life of the developer and make his work more grateful. Of course, you can argue about them as much as you like, so let's just agree that microservices are useful (which is confirmed by the experience of large companies such as Netflix and Nginx). Like any other architecture, microservices have their own shortcomings and difficulties that need to be overcome. The most important are:
PROBLEM
So, here we come to the question that most of the teams starting to work with microservices face. Considering the purpose of working with microservices and the recommended techniques for their implementation, we encounter a problem: “We need weakly connected services, between which there will be almost no common code and dependencies. Thus, whenever we consume some service, we need to write classes that will handle the response. But what about the principle of "DRY" (Do not repeat)? What to do?". In this case, it is easy to hit two antipatterns:
SOLUTION
If you clearly state the purpose of the architecture and how to explain the problem, the solution seems to be asking for itself. If the service code should be completely autonomous, but we need to consume rather complex responses on clients, then clients should write their own libraries to consume this service.
This approach has the following advantages:
This solution cannot be called completely new - just such an approach is described in the book “ Creating Microservices ” by Sam Newman (I highly recommend it). The embodiment of these ideas is found in many successful microservice architectures. This article is mainly devoted to the reuse of code in the subject area, but similar principles apply to code that provides general connectivity and exchange of information, since this does not contradict the principles set forth here.
Another question is also possible: is it worth worrying about linking domain objects and connectivity with client libraries. As with the answer to our main question, the most important factor in this case is the influence of such details on the overall architecture. If we decide that productivity will improve if we include the connecting code in the client libraries, then we need to ensure that there is no strong linkage between the client services. Given that connectivity in such architectures is usually provided using simple REST calls or using a message queue, I do not recommend putting such code in the client library, since it adds unnecessary dependencies, but is not very profitable. If the code for connectivity has something special or too complex - for example, client certificates for executing SOAP requests, until, it may be advisable to attach an additional library. If you choose this path, then always specify the use of the client library as optional, and not mandatory. Client services do not have to fully own the code (it is impossible to oblige the service provider to certainly update the corresponding client libraries).
EXAMPLE WITH SPRING BOOT

So, I explained the solution, and now I will demonstrate it in code. By the way, here is the opportunity to once again promote my favorite microservice library - Spring Boot . The entire example can be downloaded from the repository on Github , created specifically for this article.
Spring Boot allows you to develop microservices right off the bat - yes, I'm not exaggerating. If Dropwizard seemed fast to you, then you will be very surprised how much more convenient it is to work with Spring Boot. In this example, we are developing a very simple service
SERVICE USER
In
In fact, it couldn't be simpler! Spring Boot is a very categorical framework, so if the defaults suit us, then typing almost nothing is necessary manually. However, one thing still needs to be changed: we are talking about the default port number. Let's see how this is done in the file
Simple and beautiful. If you have ever written a REST service in Java, then you probably know what it takes
So we just let the user fulfill requests to the endpoint
That's the whole service needed to create User JSON. Since this is the first Spring Boot service we are considering, it does not hurt to look into the file
When calling a service whose id is 10, we see the following JSON output:

CUSTOMER LIBRARY
Suppose we have two services that use this API - a notification service and a personal account. In a realistic example, the User object could be much more complex, and we could have more than two clients. The client library is a simple project called
As you can see, this class is simpler - there are no details related to simulating users in it, there is no friends list, which is recognized as undesirable in the original class. We hide these details from customers. In such a lightweight implementation, new fields that this API may return will also be ignored. Of course, in a realistic example, the client library could turn out to be much more complicated, which would save us the time spent on the set of screen code and help to better understand the relationship between the fields.
CUSTOMERS
This example shows the implementation of two separate client services. One is needed to create a “user account”, and the other is for a “notification list”. You can consider them specialized microservices for working with user interface components.
Here is the personal account service controller:
And this is the controller of the personal notification service:
As you can see, both clients are very simple, and the connection between them and the service is also trivial. Of course, in doing so, we must add dependencies to the .pom files for both services
All that remains to be done in this example is to start all three services on ports 9001, 9002 and 9003 to see the output:
My account:

Notifications:

CONCLUSION
I believe that this design approach allows you to solve most problems with code reuse in microservice architecture. It is understandable, avoids most of the shortcomings inherent in other approaches and simplifies the life of the developer. Moreover - this is a solution that has been tested on real projects and has proven itself well.
The Spring Boot example clearly demonstrates how convenient this approach is; in addition, it turns out that microservices are much simpler than they might seem. If you want to learn more about this project - look at my on Githuband try to develop it.
Good luck with the development of microservices!
PS - from the authors of the translation:
→ Here is a book about Spring Boot.
→ Here's a book on microservices in Spring.
Do you want any?
In most microservice architectures, there are a lot of opportunities for sharing code - accordingly, the temptation to do this is great. In this article I will share my own experience: I will tell you when it is appropriate to reuse the code, and when it is better to avoid it. All points will be illustrated by the example of a special project using Spring Boot, which is available on Github .
INTRODUCTION
Before we talk about code sharing and what is behind it, we will determine what tasks are usually solved using microservice architectures. Here are the key benefits of implementing microservices:
- Scaling improves - different parts of the application scale independently
- Effectively removing the strong connection between different parts of the system is always desirable, but is best achieved with microservices.
- The reliability of the system increases - if one service fails, the others remain operational.
- Freedom to choose technology - each service can be implemented using the technology that is best suited for this case.
- Improved reusability of components - services (even those that are already deployed) can be shared in different projects
- There are many other benefits that depend on the specific architecture or problem being addressed.
Naturally, many of these advantages make it possible not only to build a better system, but also to facilitate the life of the developer and make his work more grateful. Of course, you can argue about them as much as you like, so let's just agree that microservices are useful (which is confirmed by the experience of large companies such as Netflix and Nginx). Like any other architecture, microservices have their own shortcomings and difficulties that need to be overcome. The most important are:
- Increased deployment complexity - the deployment process does not consist of one or more stages, but of dozens and even more
- More integration code - often services must exchange information with each other. How to organize it correctly, it would be worth writing a separate article
- Will work in this subject area require active copying of code in a distributed system - or maybe not?
PROBLEM
So, here we come to the question that most of the teams starting to work with microservices face. Considering the purpose of working with microservices and the recommended techniques for their implementation, we encounter a problem: “We need weakly connected services, between which there will be almost no common code and dependencies. Thus, whenever we consume some service, we need to write classes that will handle the response. But what about the principle of "DRY" (Do not repeat)? What to do?". In this case, it is easy to hit two antipatterns:
- Let's make the services dependent on each other! Well, this means that we can forget about weak binding (we definitely won’t achieve it here), and that freedom in choosing technology will also be lost: the logic will be scattered throughout the code, and the subject area will become excessively complicated.
- Let's just copy-paste the code! This is not so bad, because, at a minimum, it allows you to maintain weak binding and prevents the domain from being oversaturated with logic. The client cannot depend on the service code. However, we will be honest; no one wants to copy paste the same classes everywhere and write a ton of stencil code whenever it is planned to consume this vile user service. The Sushi Code principle has turned into a mantra for a reason!
SOLUTION
If you clearly state the purpose of the architecture and how to explain the problem, the solution seems to be asking for itself. If the service code should be completely autonomous, but we need to consume rather complex responses on clients, then clients should write their own libraries to consume this service.
This approach has the following advantages:
- The service is completely separated from the client, and specific services are independent of each other - the library is autonomous and client-specific. It can even be sharpened for a specific technology if we work with several technologies at once.
- The release of the new client version does not depend on the client; in the presence of backward compatibility, clients may not even notice the release, since it is the client that provides the library support
- Now clients are DRY - no redundant code is copied
- Integration with the service is accelerated, but at the same time, we do not lose any advantages of the microservice architecture.
This solution cannot be called completely new - just such an approach is described in the book “ Creating Microservices ” by Sam Newman (I highly recommend it). The embodiment of these ideas is found in many successful microservice architectures. This article is mainly devoted to the reuse of code in the subject area, but similar principles apply to code that provides general connectivity and exchange of information, since this does not contradict the principles set forth here.
Another question is also possible: is it worth worrying about linking domain objects and connectivity with client libraries. As with the answer to our main question, the most important factor in this case is the influence of such details on the overall architecture. If we decide that productivity will improve if we include the connecting code in the client libraries, then we need to ensure that there is no strong linkage between the client services. Given that connectivity in such architectures is usually provided using simple REST calls or using a message queue, I do not recommend putting such code in the client library, since it adds unnecessary dependencies, but is not very profitable. If the code for connectivity has something special or too complex - for example, client certificates for executing SOAP requests, until, it may be advisable to attach an additional library. If you choose this path, then always specify the use of the client library as optional, and not mandatory. Client services do not have to fully own the code (it is impossible to oblige the service provider to certainly update the corresponding client libraries).
EXAMPLE WITH SPRING BOOT

So, I explained the solution, and now I will demonstrate it in code. By the way, here is the opportunity to once again promote my favorite microservice library - Spring Boot . The entire example can be downloaded from the repository on Github , created specifically for this article.
Spring Boot allows you to develop microservices right off the bat - yes, I'm not exaggerating. If Dropwizard seemed fast to you, then you will be very surprised how much more convenient it is to work with Spring Boot. In this example, we are developing a very simple service
User
that will return a simulated objectUser
JSON In the future, this service will be used by the notification service and the table service, in fact, building various representations of the data; however, in both cases, the service needs to understand the object User
. SERVICE USER
In
UserServiceApplication
will be the primary method. Since this is Spring Boot, it also includes an integrated Tomcat server at startup:package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
In fact, it couldn't be simpler! Spring Boot is a very categorical framework, so if the defaults suit us, then typing almost nothing is necessary manually. However, one thing still needs to be changed: we are talking about the default port number. Let's see how this is done in the file
application.properties
:server.port = 9001
Simple and beautiful. If you have ever written a REST service in Java, then you probably know what it takes
Controller
. If you are doing this for the first time - do not worry, writing controllers in Spring Boot is quite simple:package com.example;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping("/user")
public User getUser(@RequestParam(value="id", defaultValue="1") int id) {
return new User(id);
}
}
So we just let the user fulfill requests to the endpoint
/user?id=
, where it id
can match any user that interests us. Given how simple these classes are - in fact, all logic should lie in a particular class User
. This class will generate stub data and will be serialized using Jackson (JSON library for Java):package com.example;
import java.util.ArrayList;
import java.util.List;
public class User {
private final long id;
private final String forename;
private final String surname;
private final String organisation;
private final List notifications;
private final long points;
// Друзья признаны нежелательными и использоваться не будут
private final List friends;
public User(int id) {
String[] forenames = {"Alice", "Manjula", "Bartosz", "Mack"};
String[] surnames = {"Smith", "Salvatore", "Jedrzejewski", "Scott"};
String[] organisations = {"ScottLogic", "UNICEF"};
forename = forenames[id%3];
surname = surnames[id%4];
organisation = organisations[id%2];
notifications= new ArrayList<>();
notifications.add("You have been promoted!");
notifications.add("Sorry, disregard the previous notifaction- wrong user");
points = id * 31 % 1000;
// У вас нет друзей
friends = new ArrayList<>();
this.id = id;
}
// Геттеры и сеттеры на все случаи…
}
That's the whole service needed to create User JSON. Since this is the first Spring Boot service we are considering, it does not hurt to look into the file
.pom
:4.0.0 com.example user-service 0.0.1-SNAPSHOT jar user-service Demo user-service with Spring Boot org.springframework.boot spring-boot-starter-parent 1.3.5.RELEASE UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test com.fasterxml.jackson.core jackson-databind 2.5.0 org.springframework.boot spring-boot-maven-plugin
When calling a service whose id is 10, we see the following JSON output:

CUSTOMER LIBRARY
Suppose we have two services that use this API - a notification service and a personal account. In a realistic example, the User object could be much more complex, and we could have more than two clients. The client library is a simple project called
user-client-libs
, consists of a single class:@JsonIgnoreProperties(ignoreUnknown = true)
public class UserView {
private long id;
private String forename;
private String surname;
private String organisation;
private List notifications;
private long points;
public UserView(){
}
public long getId() {
return id;
}
public String getForename() {
return forename;
}
public String getSurname() {
return surname;
}
public String getOrganisation() {
return organisation;
}
public List getNotifications() {
return notifications;
}
public long getPoints() {
return points;
}
}
As you can see, this class is simpler - there are no details related to simulating users in it, there is no friends list, which is recognized as undesirable in the original class. We hide these details from customers. In such a lightweight implementation, new fields that this API may return will also be ignored. Of course, in a realistic example, the client library could turn out to be much more complicated, which would save us the time spent on the set of screen code and help to better understand the relationship between the fields.
CUSTOMERS
This example shows the implementation of two separate client services. One is needed to create a “user account”, and the other is for a “notification list”. You can consider them specialized microservices for working with user interface components.
Here is the personal account service controller:
import com.example.user.dto.UserView;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class UserDashboardController {
@RequestMapping("/dashboard")
public String getUser(@RequestParam(value="id", defaultValue="1") int id) {
RestTemplate restTemplate = new RestTemplate();
UserView user = restTemplate.getForObject("http://localhost:9001/user?id="+id, UserView.class);
return "USER DASHBOARD
" +
"Welcome " + user.getForename() +" "+user.getSurname()+"
"+
"You have " +user.getPoints() + " points! Good job!
"+
"
"+
"
"+user.getOrganisation();
}
}
And this is the controller of the personal notification service:
import com.example.user.dto.UserView;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class UserNotificationController {
@RequestMapping("/notification")
public String getUser(@RequestParam(value="id", defaultValue="1") int id) {
RestTemplate restTemplate = new RestTemplate();
UserView user = restTemplate.getForObject("http://localhost:9001/user?id="+id, UserView.class);
String response = "NOTIFICATIONS";
int number = 1;
for(String notification : user.getNotifications()){
response += "
Notification number "+(number++)+": "+notification;
}
return response;
}
}
As you can see, both clients are very simple, and the connection between them and the service is also trivial. Of course, in doing so, we must add dependencies to the .pom files for both services
com.example user-client-libs 0.0.1-SNAPSHOT
All that remains to be done in this example is to start all three services on ports 9001, 9002 and 9003 to see the output:
My account:

Notifications:

CONCLUSION
I believe that this design approach allows you to solve most problems with code reuse in microservice architecture. It is understandable, avoids most of the shortcomings inherent in other approaches and simplifies the life of the developer. Moreover - this is a solution that has been tested on real projects and has proven itself well.
The Spring Boot example clearly demonstrates how convenient this approach is; in addition, it turns out that microservices are much simpler than they might seem. If you want to learn more about this project - look at my on Githuband try to develop it.
Good luck with the development of microservices!
PS - from the authors of the translation:
→ Here is a book about Spring Boot.
→ Here's a book on microservices in Spring.
Do you want any?
Only registered users can participate in the survey. Please come in.
Spring and microservices
- 70.1% Please translate Craig Walls' Spring Boot in Action 40
- 82.4% Please translate John Carnell's Spring Microservices in Action 47
- 1.7% Please translate another book (in comments) 1