ReBooting your application with Micronaut! 

Spring vs micronaut 

As developers we face constant change and evolution. Whenever we reach a certain level of awareness and confidence in particular technology, another solution appears on the horizon... It happened when we were learning java, life changed with spring and its vast array of libraries. In the meantime, the spring itself has undergone quite an evolution in the form of spring-boot.  

We notice a certain pattern - something new, different and promising is emerging, and with that novelties, there comes some uncertainty in ourselves. Can the transition to another framework be painless? 

I will try to answer this question and, through several simple examples, prove that change can be not only easy but also pleasant. 

To begin with, I have to say that I have not encountered a problem for which spring did not have a proposal for a solution, so I rate spring's maturity highly - after all, it started over 20 years ago! 

So let's go back a few years. I was lucky enough to work on a project where a significant problem arose when using cloud architecture.  

But first things first... We had several nodes of an application based on the Spring Framework. 

The application was small and launched in only several seconds. One of the client's requirements was mechanisms for promotions initiated on a specific day, which resulted in great interest and consequently generated a lot of traffic on the website. We were prepared for this. Autoscaling was supposed to come into play when such a situation occurred. Unfortunately, even a relatively quick start could not cope with it, and autoscaling simply could not keep up with the supply of new instances in relation to those that stopped. 

Today, with a range of technologies such as spring-boot, micronaut, quarkus and the ability to generate native images, the above problem should be able to be effectively mitigated. 

 For the purposes of this article, we will focus on micronaut from the perspective of a developer who knows spring-boot. 

We have always associated spring with being a framework that is widely known and much preferred. 

How does it work? Spring creates proxy classes which it then uses through reflection mechanisms - I suspect that a large number of readers are aware that the very term “reflection” in java is not associated with performance. Additionally, component setup happens at application launches which significantly affects the starting speed of a new instance. 

We have refreshed the basics of spring - so how does micronaut do it?  

Micronaut uses annotations at compilation time so that all Beans (and any problems with them) are resolved at compilation time rather than on the launched application. In addition, the fact that micronaut does not use reflection allows it to significantly reduce memory consumption. 

99% of java developers currently know the spring framework like the back of their hand. Will micronaut require learning from scratch? 

To answer this question, I decided to write the same application twice. 

A simple REST service - controller, service, repository, plus object mapping in the service to separate the application layers. The whole thing connects to a non-relational MongoDB database. 

 When I have already had an idea for the application, I started with a spring-boot. 

I used the well-known https://start.spring.io/. I have chosen the appropriate dependencies needed to achieve the goal. 

With the skeleton ready, I created logic and after a short while I could test my API with swagger-ui. 

 I could then start writing the same application, but with Micronaut. 

The first pleasant surprise was that, similarly to spring, Micronaut also provides an online tool to facilitate the setup of the application https://micronaut.io/launch/. 

Using the same buzzwords as for spring, after a while I had the same ready skeleton as a moment before with spring. 

At the start, I wanted the application to connect correctly to the local mongo. Using the micronaut documentation and bearing in mind how it works in spring-boot, I quickly finished with the intended result. 

mongodb: 

uri: mongodb://XXX:XXX@localhost:27017/ 

It’s time to rewrite the logic. Using the documentation, I managed to rewrite the logic within a short period of time, which works exactly as its prototype that was written before. 

What are the differences?

The controllers:

Controllers

// Spring: 

@RestController 

@RequestMapping("/api/users") 

public class UserController { 

    ... 

} 
// Micronaut: 

@Controller("/api/users")
 
public
 
class
 
UserController
 
{ 
    ... 
} 

Handling of requests within the controllers is similar:

Post

// Spring:

@PostMapping

public ResponseEntity<UserDto> upsertUser(UserDto userDto, UriComponentsBuilder builder) {

   UserDto createdUser = userService.upsertUser(userDto);

   UriComponents uriComponents = builder.path("/api/users/{id}"). buildAndExpand(createdUser.getId());

   return ResponseEntity. created(uriComponents.toUri()).body(createdUser);

}
// Micronaut:

@Post

public HttpResponse<UserDto> upsertUser(UserDto userDto) {

   return HttpResponse.created(userService.upsertUser(userDto));

}

Get

// Spring: 

@GetMapping

public ResponseEntity<Collection<UserDto>> findAll() {

   return ResponseEntity.ok(userService.findAll());

}
Micronaut: 

@Get

public HttpResponse<Collection<UserDto>> findAll() {

   return HttpResponse.ok(userService.findAll());

}

Delete

// Spring: 

@DeleteMapping("/{id}")

public ResponseEntity<Void> deleteById(@PathVariable String id) {

   userService.deleteById(id);

   return ResponseEntity.noContent().build();

}
// Micronaut: 

@Delete("/{id}")

public HttpResponse<Void> deleteById(@PathVariable String id) {

   userService.deleteById(id);

   return HttpResponse.noContent();

}

Defining the services

// Spring: 

@Service

public class UserService {

    ...

}
Micronaut: 

@Singleton

public class UserService {

    ...

}

Defining the repositories

// Spring: 

public interface UserRepository extends MongoRepository<User, String> {

}
// Micronaut:
@MongoRepository(databaseName = "user")

public interface UserRepository extends CrudRepository<User, String> {

}

Launch times / native images

Time to play. After all, both frameworks are “cloud ready” and were supposed to support us with their fast launch times. Let's check this out...

spring-boot vanilla:

  • launch time: 1633 ms

  • RAM consumption: 32,1 MB

micronaut vanilla:

  • launch time: 449 ms

  • RAM consumption: 41,2 MB

spring-boot native-image:

  • launch time 116 ms

  • RAM consumption: 52,8 MB

micronaut native-image:

  • launch time: 38 ms

  • RAM consumption: 11,2 MB

The objective can be achieved in several other ways, but the examples given above prove the hypothesis that the transition from one framework to another can be quick and fairly easy. 

When analysing the above results, it is important to remember that the application is very lightweight. With large projects, times and memory consumption will certainly be higher. On the other hand, one can see a trend that native images start much faster than applications run traditionally. Going further, we can see that micronaut wins on both counts with spring-boot as the launch time is about three times faster. 

Conclusion 

Spring-boot is by far more mature and developed framework. It has great documentation and, thanks to its huge user base, solutions to even complex, very complicated problems are often easy to find. 

Micronaut, on the other hand, has also already had quite good documentation making standard actions easy to address. 

The situation gets worse when we encounter "non-standard" problems. Unfortunately, the community around Micronaut is not that far developed and some problems require more thinking and analysis. However, bearing in mind that spring was launched in 2002 and Micronaut in 2018, this should not surprise anyone. The community will also grow stronger and bigger from month to month. 

Let's not be afraid of change! 

.... we can always do a rollback. (or try quarkus ;)) 


You can find the source codes of the examples used in this article in our GitHub:

https://github.com/hycomsa/spring-vs-micronaut