Micronaut (2 of 6): Demo Application

Lydtech
Micronaut (2 of 6): Spring Demo

Introduction

The Micronaut framework is well suited to building efficient, performant, and scalable cloud-native applications. In this article a companion application provides an example of a simple Micronaut application that serves to highlight some of the framework’s features, testing best practices, and steps to build for a cloud-native environment using GraalVM. The application is provided both in Kotlin and Java, and the source code for each is available here: [Kotlin version | Java version].

Code snippets provided in this and following articles will typically be taken from the Kotlin version unless the differences between the two languages are explicitly called out. Links to the source code for both will always be provided.

This is the second article in a six part series on the Micronaut framework.

  1. Framework Features
  2. Demo Application (this article)
  3. Testing
  4. Native Builds (coming soon)
  5. Postgres Integration (coming soon)
  6. Kafka Integration (coming soon)

Demo Application

The Micronaut application provides a REST API enabling a client to perform create, read, update and delete (CRUD) operations on a notional item entity. In this application items are created in memory, specifically a HashMap, there is no persistence layer.

Figure 1: Micronaut demo application

Figure 1: Micronaut demo application

In an upcoming article (part 5) a database is introduced to provide a persistence layer, and the issues around JPA frameworks that are compatible with cloud-native builds are discussed.

Application

The entrypoint for the Micronaut application is defined in the DemoApplication class [Kotlin | Java]. In Kotlin, the @JvmStatic annotation is used to indicate that this function should be compiled to a static method in Java. This is because static methods are not a natural language feature of Kotlin.

object DemoApplication {


   @JvmStatic
   fun main(args: Array) {
       Micronaut.run(DemoApplication::class.java)
   }
}

The Micronaut.run() call initialises the Micronaut framework and uses the DemoApplication class as the application context. This enables dependency injection, application configuration, and starting the embedded server to handle incoming requests when it is a web application.

Application Properties

The properties for the application are defined in the application.yml in the src/main/resources/ directory [Kotlin | Java]. For this application just the name and the port that the web server should listen on are defined:

micronaut:
   application:
       name: demo
   server:
       port: 9001

By defining these in a properties file rather than in the code itself it means that the different properties can be swapped in when running the application in different environments, and when running tests. No code has to be changed to enable this, which is an important best practice in the heralded Twelve-Factor App methodology.

As more features and functionality are added to the application in the next articles in this series, their associated configuration properties are added to this file.

Controller

A controller class defines the REST endpoint methods for the application. This is annotated with the Micronaut framework’s @Controller annotation. The CRUD endpoints are annotated with @Post, @Get, @Put and @Delete respectively. The following snippet from ItemService [Kotlin | Java] shows the class definition and the endpoint to create the item.

@Controller("/v1/items")
class ItemController @Inject constructor(private val itemService: ItemService) {

   private val log = LoggerFactory.getLogger(ItemController::class.java)

   @Post
   fun createItem(@Body request: @Valid CreateItemRequest): HttpResponse    {
       log.info("Received request to create item with name: {}", request.name)
       return try {
           val itemId = itemService.createItem(request)
           HttpResponse.created(URI.create(itemId.toString()))
       } catch (e: Exception) {
           log.error(e.message)
           HttpResponse.serverError()
       }
   }

Service

The controller delegates to the ItemService [Kotlin | Java] that is responsible for operations on the item entity itself (such as the create). The service is injected into the controller by Micronaut. This split of responsibilities, with the controller responsible for defining the REST API, and the service responsible for the business logic, is an example of the single responsibility principle. It makes it easy to write unit tests, as each test is only concerned with testing the functionality in that layer of the application.

The following code snippet from the service class shows the function for creating an item, with it being stored in a HashMap:

@Singleton
class ItemService {
   companion object {
       private val log = LoggerFactory.getLogger(this::class.java)
   }
   private val itemStore = HashMap()

   fun createItem(request: CreateItemRequest): UUID {
       val item = Item(UUID.randomUUID(), request.name)
       itemStore.put(item.id, item)
       log.info("Item created with id: {}", item.id)
       return item.id
   }

Build & Execution

The companion projects use gradle as the build tool, and the standard Kotlin and Java applications are built and tested using:

./gradlew clean test

In an upcoming article in this series (part 4) building a native application will be covered.

The applications are then run via:

./gradlew run

Then use curl to send requests to the application’s REST API to create an item (which returns a location header), to retrieve the item, update it, and finally delete it:

curl -i -X POST localhost:9001/v1/items -H "Content-Type: application/json" -d '{"name": "test-item"}'
curl -i -X GET localhost:9001/v1/items/653d06f08faa89580090466e
curl -i -X PUT localhost:9001/v1/items/653d06f08faa89580090466e -H "Content-Type: application/json" -d '{"name": "test-item-update"}'
curl -i -X DELETE localhost:9001/v1/items/653d06f08faa89580090466e

Summary

The companion applications, built in Kotlin and Java, demonstrate how the Micronaut framework and its features are used to construct the applications. In upcoming articles in this series, testing best practices and steps to build for a cloud-native environment using GraalVM are covered. The applications are enhanced to utilise a Postgres database for persistence, and the Kafka messaging broker for consuming and producing messages.

Source Code

There are two flavours of the Micronaut application, one in Kotlin and one in Java, and the source code is available here:

Kotlin version: https://github.com/lydtechconsulting/micronaut-rest-kotlin/tree/v1.0.0
Java version: https://github.com/lydtechconsulting/micronaut-rest-java/tree/v1.0.0

Next... Testing

In the third article in the series, unit, integration and component tests are added demonstrating how to comprehensively test the Micronaut application.


View this article on our Medium Publication.