How to build a full-fledged backend architecture in Java

How to build a full-fledged backend architecture in Java

The art of developing high-quality software has been in constant evolution. New frameworks, libraries, tools, IDEs and even programming languages have come to existence, and old languages have become very robust and have expanded its reach to satisfy the needs of large corporations. This is the case of Java, which is nowadays the 5th most used programming language in the world. 

Such advancements in technology have made the process of building software more complex; but to some extent, easier. The wide variety of available frameworks and libraries out there has greatly facilitated the job of developers. Nonetheless, devs still need to know what library works best, which pattern design to use in every situation, how to write efficient and clean code and -more importantly- what architecture standards to keep.

Therein lies the importance of architecture as one of the ruling factors of efficiency and cleanliness of the code. The selection of the appropriate architecture might lead to fewer bugs and less work of maintenance in the future.

In this article, you’ll learn how to build a full-fledged backend architecture that supports corporate systems.

Requirements

To build for this project you’ll need:

ü  General knowledge about SOA
ü  General knowledge about unit testing in Java
ü  Familiar with annotations in Java
ü  Knowledge about design patterns
ü  Advanced knowledge of Spring, including CDI
ü  Advanced knowledge of Spring MVC
ü  Advanced knowledge of Maven

Also, the following tools:

ü  IDE of your choice (Intellij IDEA or Eclipse)
ü  Java 8

The task

As the focus of this article is to establish a proper architecture for our software, we’ll rather vote for a simple task. Let’s say:

“Build an API to validate the existence of a user based on its username. The username will be an alphanumeric string and the API will be called via REST from frontend. The information of the users is not stored in our databases, but in the database of one of our clients, who exposes an API to get users, based on its username. The API Blueprint for the external API (client’s API) and our API are shown in the table below.”

NOTE: (1) If you are not familiarized with API Blueprint, I’d suggest you take a look here. (2) We’ll disregard the authentication to our API for now.

Our API
## Validate the existence of a user [/users/exists/{username}]

### validateUserExistence [GET]

+ Parameters

    + username (string)

+ Request (application/json;charset=UTF-8)


+ Response 404 – NOT FOUND
+ Response 302 FOUND
External API
## Get users’ info [<CLIENT_URL>/user/get/{username}]

### getUser [GET]

+ Parameters

    + username (string)

+ Request (application/json;charset=UTF-8)


+ Response 200
+ Attributes (UserInfo)


Also, UserInfo contains the following fields:

ü  Id (String)
ü  Name (String)
ü  Surname (String)
ü  DateOfBirth (Date)
ü  Username (String)

Analysis

Before proceeding, I’ll strongly suggest you to try solving this exercise by yourself and see if you came up with a similar solution. Remember to consider the different service layers that should be involved in the solution.

Now let’s get on with the analysis. First, we need to receive REST requests from the frontend. Therefore, we need a controller to handle these requests. Second, we’ll need a service bean with its respective interface to handle the business logic related to Users. We also need to consume the API exposed by our client, so we need another layer that will serve as client for external services.

Our architecture will look as follows:

Figure 1. Software architecture for this example

Code

EDIT: You can now download the code from GitHub!
Let’s start by creating the Web Service Layer. For this, we’ll need a Controller that accepts REST requests, a function to handle such requests and the appropriate mapping to our function.

1.      The Rest Controller is the easiest part. We’ll use the Spring annotation @RestController to capture REST requests.
2.      According to the API Blueprint, our function should return either 404 or 302 Http Status. To facilitate this, we’ll use the object ResponseEntity, from Spring. Our function will call a service in the service layer and return the HTTP Status accordingly.
3.      The mapping for the function is given by the API Blueprint. It should be: /users/exists/{username}

From step 2 we see that there’s a dependency on the service layer. We need to create an interface that allows us to access the service. Our interface will contain a single method which will accept a String and return a boolean to identify if the user exists. It will look as follows:

UsersService.java
public interface UsersService {
   
boolean userExists(String username);
}

And our controller:

UsersController.java
@RestController
@RequestMapping
(value = "/users", consumes = APPLICATION_JSON_UTF8_VALUE, produces = APPLICATION_JSON_UTF8_VALUE)
@Validated
public class UsersController {

   
private final UsersService usersService;

   
@Autowired
   
public UsersController(UsersService usersService) {
       
this.usersService = usersService;
    }

   
@GetMapping("/exists/{username}")
   
public ResponseEntity validateUserExistence(@NotNull @PathVariable(value = "username") String username) {
       
return (usersService.userExists(username)) ? new ResponseEntity(HttpStatus.NOT_FOUND) :
               
new ResponseEntity(HttpStatus.FOUND);
    }
}


We have the first part of the architecture. Now, we need to implement the business logic behind the interface and call the client that gets the user from the external services. An architectural question arises: How should this client look like? What library should we use to call these external services?

Let’s keep it simple. We’ll use Feign, which “is a declarative web service client”. Feign allows us to create web service clients by creating an interface, mapping it accordingly, and forming the body of the request and response by adding the respective entities.

We know-how the response of the Feign client will look like. It is described by UserInfo in the task’s statement. Therefore, we can easily proceed with its creation. As it is the response of Feign, let’s name it: UserFeignClientResponse

UserFeignClientResponse.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserFeignClientResponse {
    String
id;
    String
name;
    String
surname;
    Date
dateOfBirth;
    String
username;
}

Note that we are using Lombok to facilitate our work.

The Feign client will look as follows:

UsersFeignClient.java
@FeignClient(name = "usersFeignClient", url = "${usersFeignClient.address}")
@RequestMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public interface UsersFeignClient {
   
@GetMapping("/user/get/{username}")
   
@Nullable  UserFeignClientResponse getUser(@NotNull @PathVariable(value = "username") String username);
}

From the API Blueprint, we can see that the Client’s API endpoint is not specified. It says: <CLIENT_URL>. Thus, we need to make this URL configurable. We add the entry usersFeignClient.address to our application.properties file. For the scope of this tutorial, we’ll set this property to localhost with the port 8000.

Application.properties
usersFeignClient.address = http://localhost:8000

Last, let’s call the feign client from the implementation of UsersService:

UsersServiceImpl.java
@Service
public class UsersServiceImpl implements UsersService {
    UsersFeignClient
usersFeignClient;

   
@Autowired
   
public UsersServiceImpl(UsersFeignClient usersFeignClient) {
       
this.usersFeignClient = usersFeignClient;
    }

   
public boolean userExists(String username) {
       
return (usersFeignClient.getUser(username) == null) ? true : false;
    }
}


Checklist

It seems that we have considered all the requirements of the task as well as the aspects of the architecture. Let’s do a quick review:

ü  Web Service Layer: We provided an endpoint for frontend to communicate with us through the use of a Rest Controller.
ü  Service Layer – Internal Services: The service layer is decoupled from the business logic through the use of the interface UsersService.
ü  Business Logic Layer – Implemented by UsersServiceImpl. Calls the external client.
ü  Service Layer – External services: Represented by the FeignClient.

Also:

ü  The API we just built as well as the call of the external service is supported by REST, in accordance with the API Blueprint.

Now you’ve learned how to build a full-fledged backend architecture for your APIs. Now, what’s left? Exactly! We need to test if the API and the underlying architecture works.

EDIT: The tutorial on how to test the architecture is now out!!

Comments