Refactoring from imperative to reactive implementation

Share on:

Background

As software industry is embracing the new Microservice Architecture paradigm, myriad applications have been built with Spring Boot framework. By the time organizations have got its early versions of microservice applications in production, industry has found out newer and better avenues for further optimizing microservices, so that systems can be more robust, resilient and responsive a.k.a Reactive Systems (as per Reactive Manifesto ). Thanks to Spring Reactor and Spring Webflux which can help us in building reactive systems using Spring framework.

What I intend to demonstrate over here is, how a Spring Boot application can be refactored from imperative to reactive implementation along with compelling justifiable reasons.

Reference Application

Lets consider a hypothetical scenario where in we have a Spring Boot based developed application i.e. Card Service App which exposes REST endpoints using Spring WebMVC. Similar to a conventional Microservice Architecture, this application in turn not only invokes an API (using Spring's RestTemplate) exposed by downstream system (written in Go) but also performs some Database operations (using Spring Data Repository). The sole purpose of adding this infrastructure complexity is to mimic a real world scenario and thereby really justify the refactoring efforts that we will be putting in.

APIs exposed by application

HTTP MethodURIDescription
GET/card/{id}Fetches a card based on cardId
GET/cardsFetches all the cards from the system
POST/cardAdds a card within system
DELETE/cardsDeletes all the cards from the system

Source Code

Imperative Implementation (Before refactoring) Reactive Implementation (After refactoring)

Steps for refactoring

1. Build Assembly

Conventionally speaking build assembly of a typical Spring Boot application (which exposes imperative REST APIs) will have Spring Boot Starter Web. So we will be replacing this by Spring Boot Starter Webflux, which is parallel version of Spring MVC that supports fully non-blocking reactive streams. We will also be moving from Tomcat to Reactor Netty (based on Netty framework) which provides non-blocking HTTP clients and servers. Ideally Spring Boot should automatically configure Reactor Netty as default server if we are using Webflux. Since it was not getting configured automatically due to some dependency issue, I had to explicitly exclude Tomcat and add Reactor Netty as dependency.

Build Assembly Refactorings

2. REST Controllers

Now that we have added Webflux as one of the dependencies, we can use its APIs to refactor controllers. Easiest way to get started is by refactoring GET APIs first and then follow it up with other APIs

2.1 GET API

Basically GET APIs either return a response object or a collection of response objects. We will be using Mono and Flux to refactor our APIs from imperative to reactive implementation by making use of just and _fromIterable _operator

2.2 POST API

Now here comes the complexity as we will need to refactor CardService before refactoring Controller. And we being Software Craftsman love solving complex pieces. Hence this is going to be fun and exciting with multi step process as mentioned below -

2.2.1 Refactoring CardService from REST template to WebClient

We will replace RestTemplate with WebClient which is a non blocking and reactive client to perform HTTP requests

We will also refactor the method that invokes downstream system's API by replacing instantiated RestTemplate with WebClient object

2.2.2 Refactoring enroll method of CardService

Another tricky piece will be to refactor enroll method. So we will be flattening the returned Mono from downstream system, which will allow us to extract card alias, than set in Card object which will be eventually saved in database

And finally we will refactor Controller

2.2.3 Refactoring Controller

Comparison on performance metrics

After performing all the above refactoring steps, lets understand whether these efforts are really going to add any business value or is this refactoring activity going to sound like a buzz word driven development to the business / product team :simple_smile:

I certainly do not want to digress from core agenda of this post, hence I will try to keep this section as much concise as I can.

Throughput

  • For 1500 concurrent users throughput of Webflux based implementation increases by 1.52 times
  • Spring WebMvc based implementation was so slow that not a single Get Card request got executed for 1000 and 1500 concurrent users

Latency

  • For Webflux based implementation, 95 percentile response time is 50% less when compared with Spring WebMvc based implementation

CPU Utilization

  • Based on average readings Spring Webflux based implementation is 8-10% less in CPU utilization

No. of threads

  • For Spring Webflux based implementation, total no. of threads is reduced by 7 times (31 Vs 234)

Conclusion

We clearly saw how to systematically refactor imperative code to reactive implementation. And with above performance metrics it is quiet apparent that even though this refactoring may not enhance any business value of application, but it has significant impact from NFR standpoint, as it mainly aids in making the system Reactive. Needless to say that comparative analysis would have convinced you regarding the benefits it bring from performance and resource utilization standpoint.

Happy REFACTORING!

comments powered by Disqus