Spring Boot - Microservice - Spring Data REST and HATEOAS Integration
Spring Boot - Microservice - Spring Data REST and HATEOAS Integration
In this article, we discuss how to write a Spring Boot microservice based on Spring Data REST and HATEOAS API integration.
Join the DZone community and get the full member experience.Join For Free
The following article is a guide on how to write Spring Boot microservice based on Spring Data REST and HATEOAS API integration. As a foundation of our project, we will use a sample written by Greg Turnquist, one of the authors of Spring HATEOAS Reference Documentation and Spring Data REST Reference Guide listed in the Reference section.
If you are already familiar with the sample, feel free to skip the following paragraphs and go directly to the section CustomOrderHateaosController, where we describe our approach to the Spring Data REST and HATEOAS integration, the difference from the implementation provided in the sample, and its benefits. Otherwise, we do encourage you to read the following detailed descriptions of the project and its goals.
Complete Maven Project With Code Examples
The following Github repository has the project with all code that we are going to present next.
Project Code Structure
The project has the following structure.
All listed classes besides the CustomOrderHateoasContoller.java and KievSpringRestDataAndHateoasApplication.java were written by Greg Turnquist.
You may also like: REST API — What Is HATEOAS?
Maven Project Dependencies
The project has the following Maven dependencies.
The following is the relevant snippet from the project POM file.
There is nothing special about this class. It is a Spring Boot Application class, and you have seen similar code many times before.
At this point, you would expect that we are going to talk about the second and most important class CustomOrderHateoasController.java class. After all, it is the place where all integration happens, and that mere fact makes it the focal point of the guide. Do not worry we will, but first, we have to make things clear and get a firm grasp on our project goals. As you remember, we are not writing our project from scratch but building it on the foundation of the already existing sample. This gives us a chance for reuse. The following are excerpts from the original README document intermixed with our remarks.
Defining the Problem
PROBLEM: You wish to implement the concept of orders. These orders have certain status codes which dictate what transitions the system can take, e.g. an order can’t be fulfilled until it’s paid for, and a fulfilled order can’t be cancelled.
SOLUTION: You must encode a set of OrderStatus codes, and enforce them using a custom Spring Web MVC controller. This controller should have routes that appear alongside the ones provided by Spring Data REST.
Let's describe the basics of an ordering system. And that starts with a domain object:
The next step in defining your domain is to define
OrderStatus. Assuming we want a general flow of create an order ⇒ pay for an order ⇒ fulfill an order, with the option to cancel only if you have not yet paid for it, this will do nicely:
At the top are the actual states. At the bottom is a static validation method. This is where the rules of state transitions are defined, and where the rest of the system should look to discern whether or not a transition is valid.
The last step to get off the ground is a Spring Data repository definition:
This repository extends Spring Data Commons' CrudRepository, filling in the domain and key types (Order and Long).
The following DatabaseLoader.java class preloads some testing data.
Remarks: Authors do believe that @Configuration annotation should be used instead of the @Component, but we were trying to reuse the code of our foundation as much as possible and that was the reason why we decided to keep @Component annotation in place.
The following resource file shows how we do modify the root path of the microservice.
It should be stated that right here, you can launch your application. Spring Boot will launch the web container, preload the data, and then bring Spring Data REST online. Spring Data REST with all of its prebuilt, hypermedia-powered routes, will respond to calls to create, replace, update, and delete
But, Spring Data REST will know nothing of valid and invalid state transitions. Its pre-built links will help you navigate from our API to the aggregate root for all orders, to individual entries, and back. But, there will no concept of paying for, fulfilling, or canceling orders — at least, not embedded in the hypermedia. The only hint end users may have are the payloads of the existing orders. And that’s not effective. It’s better to create some extra operations and then serve up their links when appropriate.
Remarks: And that concludes the description of our preliminary steps. At this point, you should know the goals of the project. You do know what has to be done, so let's see how it can be done. Let's talk about Spring Data REST and HATEOAS integration at last. Let's talk about the custom controller CustomOrderHateoasController!
That is it! The big times! So here it is our custom controller where everything happens and all things are done!
Let's dissect the controller.
@BasePathAwareController annotation declares a controller where request mappings is augmented with a base URI in the Spring Data REST configuration. In other words, our custom controller is registered under the base path /api.
The controller has a final private instance variable
OrderPepository that is initialized via constructor injection.
The container has three public methods,
fulfill. These three methods are the primary reason for the controller's existence, as they are responsible for order payment, cancellation, and fulfillment. The implementation of these methods was taken from the CustomOrderController.java written by Greg Turnquist. There is nothing special about these methods, and they are not related to the HATEOAS integration. The following is the code that we discussed so far:
Now, when we know the main purpose of the controller, let's talk how the Integration with HATEOAS happens. But first, let's stop and discuss what we are trying to accomplish. In plain terms, our goal is to add additional links to the existing links created by our Spring Data REST API.
process. Before we break down this code, let's stop again for a minute and talk about the benefits of making the controller responsible for interface implementation versus the creation of a separate processor. The sample project chose to take the latest approach where a separate class OrderProcessor.java was used. The major benefit of making the controller the processor is the consolidation of all related code in one place.
So, if you are thinking about the maintenance of your code after you leave a project, we would advocate for our approach. Otherwise, every time a new developer changes/refactor the controller, he or she would need to remember to go to the separate processor and modify it accordingly. And the truth is that this poor soul might be unaware of the processor's existence. By the way, if you do think about the maintenance of your code in advance, you are a good person. Seriously!
And now, it is time for us to talk about our proposed method
Remember, the main purpose of this method is to create additional links under certain conditions. Let's say we have to create an additional link,
payment (line 16).
As we can see, the method,
process, is called with the argument
EntityModel<Order> model that is the representation to be modified if needed and returned from the method by further processing by the Spring framework (namely serialization). It means, we have to construct the
Link object and add it to the model (line 16).
The addition is trivial; we are using the method,
add, exposed by the
EntityModel class. In order to construct the
Link, we can use the following constructor,
public Link(String href, String rel), where the
href is the desired URI and
rel is Relation. In other words, our
href should be "http://localhost:8080/api/orders/1/pay," and the
payment. So, the question now is how to construct the
href URI easily. The answer lies in the
model object supplied to us as an argument by the Spring framework.
The debugger screenshot clearly indicates that the model (our resource
Order representation) has been already modified by the framework, and its links collection is not empty. Furthermore, the first element is a “self” Link. We are almost at the end of our presentation here. All we have to do is concatenate this
href with the “/pay” (line 15), create the
Link instance, add it to the model, and return the modified representation to the framework for further serialization. And that's all, folks! We can start the Spring Boot application and follow the steps outlined in the README document.
We completed the goal of our project. That concludes our guide on how to write microservice with Spring Boot using the Spring Data REST and HATEOAS API.
It is also worth it to notice that we reused the test case OrderIntegrationTest.java written by Greg Turnquist and for that, we are grateful to him!.
The authors do hope that you find the proposed approach reasonable and most importantly useful in your own development endeavors. So give us a like if you found the reading informative, and let us know your thoughts on the subject in the comment section.
Opinions expressed by DZone contributors are their own.