An introduction to Reactive Programming with RxJava – Part #3: Convert a traditional web application to a reactive one

An introduction to Reactive Programming with RxJava – Part #3: Convert a traditional web application to a reactive one

In my last two blog entries, I’ve given an introduction to reactive programming and RxJava. In this third and final part, I will show how you can convert a traditional web application to a reactive one.

All the code will be available at https://gitlab.com/perlilja/rxAirlineDemo, there will also be the instructions on how to run the application. To get a full insight, I suggest you read the code. The final version is merged into the master branch, and the original version is found on before_rxjava branch.

The application architecture

The application consists of three parts; the third-party API, the backend and the client. The third-party API’s will stay the same, and represent an external API that we don’t have control over. The backend and the client are where we will make the changes to convert a traditional server/client application to a responsive web application. But first, let me explain the traditional web application.

The legacy application architecture

The architecture and the flow of the application are explained in the image below.

 

It works like this; the client calls the backend, which then makes two calls to each third party API. One call for fetching all possible destinations for an airline for the given source, and thereafter it fetches all flights for each destination. All is done synchronously, which means it will first fetch the data from the EasyJetAPI, and not until it has fetched all the data for that API it will continue to the next one. When fetched all the data from the three different APIs, it will return all the flights in one big JSON response that will be processed and rendered by the client.

Converting to a reactive architecture

To change the web application to a reactive application, we first need to convert the response from the API to an Observable instead. This is easily done by creating an Observable from the response we got from the API, like this:

[cc lang=”java” tab_size=”2″ lines=”80″]
Observable result = Observable.
fromArray(restTemplate.getForObject(encodeURI(“http://localhost:10002/easyjet/api/destinations/{from}”, from),DestinationDTO[].class));
[/cc]

The next step is to use the new Observable when fetching the data, for example before we did the following:

[cc lang=”java” tab_size=”2″ lines=”80″]
List possibleEasyJetDestinations = easyJetService.getDestinations(from);
[/cc]

[cc lang=”java” tab_size=”2″ lines=”80″]
for (DestinationDTO dest : possibleEasyJetDestinations) {
List tmpFlights = easyJetService.getFlights(from, dest.getTo());
tmpFlights.stream().forEach(f -> f.setAirline(“EasyJet”));
allFlights.addAll(tmpFlights);
}
[/cc]

Now we can use the power of Observables and first fetch the destinations and then use the flatMap operator to fetch the flights. The subscibeOn method tells the Observable that we want to run this operation in another thread than the main thread. The advantage of doing this is that each of these services will run in parallel (asynchronously), instead of after each other (synchronously).

[cc lang=”java” tab_size=”2″ lines=”80″]
easyJetService.getDestinations(from)
.subscribeOn(Schedulers.io())
.flatMap(dest -> easyJetService.getFlights(from, dest.getTo()))
.map(f -> new FlightDTO(f.getFrom(), f.getDestination(), f.getDeparture(), f.getArrival(), f.getPrice(), “EasyJet”));
[/cc]

And now that each service is returning an Observable, we can use merge to join the three Observables that we got from the three different services (one for each external API).

As the last step, we change the FlightsController from returning the response as one big JSON, to instead take the advantage of Server-sent Events (SSE). SSE is a HTML5 standard, where updates or small chunks of data is pushed to the client. In our case, we are sending each FlightDTO as an update to the client. This means that the client doesn’t need to wait for the whole response before it can start rendering the data. Instead, it can start rendering as soon as it receives the first FlightDTO object. The support for SSE is built in Spring Boot, and is supported by most web browsers.

[cc lang=”java” tab_size=”2″ lines=”80″]
SseEmitter sseEmitter = new SseEmitter();
airlineService.getPossibleDestinations(from).subscribe(a -> {
sseEmitter.send(a);
},
sseEmitter::completeWithError,
sseEmitter::complete
);

return sseEmitter;
[/cc]

I will not explain the client part but do take a look at the code at Gitlab. The client application is based on Angular 4 and uses RxJS.

Conclusion

So, what are the advantages of using RxJava? The big advantage is that we will get a more reactive application, where the data is displayed to the user in a much faster way. We were also able to divide the execution of the code into different threads, and in that way, make the code less synchronously (and almost three times faster). Of course, we could also have done this with the legacy application, but that wouldn’t be as easy.

And furthermore – even though it’s my very personal opinion – the code becomes much more readable.

This blog entry is an updated version of the one published on One Agency’s blog, https://www.oneagency.se/blogg/.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.