Reader small image

You're reading from  Kotlin Design Patterns and Best Practices - Second Edition

Product typeBook
Published inJan 2022
Reading LevelBeginner
PublisherPackt
ISBN-139781801815727
Edition2nd Edition
Languages
Right arrow
Author (1)
Alexey Soshin
Alexey Soshin
author image
Alexey Soshin

Alexey Soshin is a software architect with 15 years of experience in the industry. He started exploring Kotlin when Kotlin was still in beta, and since then has been a big enthusiast of the language. He's a conference speaker, published writer, and the author of a video course titled Pragmatic System Design.
Read more about Alexey Soshin

Right arrow

Chapter 11: Reactive Microservices with Vert.x

In the previous chapter, we familiarized ourselves with the Ktor framework. We created a web service that could store cats in its database.

In this chapter, we'll continue working on the example from the previous chapter, but this time using the Vert.x framework and Kotlin. Vert.x is a Reactive framework that is built on top of Reactive principles, which we discussed in Chapter 7, Controlling the Data Flow. We'll list some of the other benefits of the Vert.x framework in this chapter. You can always read more about Vert.x by going to the official website: https://vertx.io.

The microservice we'll develop in this chapter will provide an endpoint for health checks – the same as the one we created in Ktor – and will be able to delete and update the cats in our database.

In this chapter, we will cover the following topics:

  • Getting started with Vert.x
  • Routing in Vert.x
  • Verticles
  • Handling...

Technical requirements

For this chapter, you'll need the following:

  • JDK 11 or later
  • IntelliJ IDEA
  • Gradle 6.8 or later
  • PostgreSQL 14 or later

Like the previous chapter, this chapter will also assume that you have PostgreSQL already installed and that you have basic knowledge of working with it. We'll also use the same table structure we created with Ktor.

You can find the full source code for this chapter here: https://github.com/PacktPublishing/Kotlin-Design-Patterns-and-Best-Practices/tree/main/Chapter11.

Getting started with Vert.x

Vert.x is a Reactive framework that is asynchronous and non-blocking. Let's understand what this means by looking at a concrete example.

We'll start by creating a new Kotlin Gradle project or by using start.vertx.io:

  1. From your IntelliJ IDEA application, select File | New | Project and choose Kotlin from the New Project wizard.
  2. Then, specify a name for your project – CatsShelterVertx, in my case – and choose Gradle Kotlin as your Build System.
  3. Then, select the Project JDK version that you have installed from the dropdown. The output should look as follows:

Figure 11.1 – Creating a Kotlin application

Next, add the following dependencies to your build.gradle.kts file:

val vertxVersion = "4.1.5"
dependencies {
    implementation("io.vertx:vertx-core:$vertxVersion")
    implementation("io.vertx:vertx-web:$vertxVersion...

Routing in Vert.x

Notice that no matter which URL we specify, we always get the same result. Of course, that's not what we want to achieve. Let's start by adding the most basic endpoint, which will only tell us that the service is up and running.

For that, we'll use Router:

val vertx = Vertx.vertx()  
val router = Router.router(vertx) 
...

Router lets you specify handlers for different HTTP methods and URLs.

Now, let's add a /status endpoint that will return an HTTP status code of 200 and a message stating OK to our user:

router.get("/status").handler { ctx ->
    ctx.response()
        .setStatusCode(200)
        .end("OK")
} 
vertx.createHttpServer()
    .requestHandler(router)
    .listen(8081)

Now, instead of specifying the request handler as a block, we will pass this...

Verticles

Our current code resides in the server.kt file, which is getting bigger and bigger. We need to find a way to split it. In Vert.x, code is split into classes called verticles.

You can think of a verticle as a lightweight actor. We discussed Actors in Chapter 5, Introducing Functional Programming.

Let's see how we can create a new verticle that will encapsulate our server:

class ServerVerticle : CoroutineVerticle() {
    override suspend fun start() {
        val router = router()
        vertx.createHttpServer()
            .requestHandler(router)
            .listen(8081)
        println("open http://localhost:8081")
    }
 
    private fun router(): Router...

Handling requests

As we discussed earlier in this chapter, all requests in Vert.x are handled by the Router class. We covered the concept of routing in the previous chapter, so now, let's just discuss the differences between the Ktor and Vert.x approaches to routing requests.

We'll declare two endpoints to delete cats from the database and update information about a particular cat. We'll use the delete and put verbs, respectively, for this:

router.delete("/cats/:id").handler { ctx ->
    // Code for deleting a cat
}
router.put("/cats/:id").handler { ctx ->
    // Code for updating a cat
}

Both endpoints receive a URL parameter. In Vert.x, we use a colon notation for this.

To be able to parse JSON requests and responses, Vert.x has a BodyHandler class. Now, let's declare it as well. This should come just after the instantiation of our router:

val router = Router.router(vertx)
router...

Testing Vert.x applications

To test our Vert.x application, we'll use the JUnit 5 framework, which we discussed in the previous chapter.

You'll need the following two dependencies in your build.gradle.kts file:

dependencies {
    ...
    testImplementation("org.junit.jupiter:junit-jupiter-
        api:5.6.0")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-
        engine:5.6.0")
}

Our first test will be located in the /src/test/kotlin/ServerTest.kt file.

The basic structure of all the integration tests looks something like this:

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ServerTest {
    private val vertx: Vertx = Vertx.vertx()
 
    @BeforeAll
    fun setup() {
        runBlocking {...

Working with databases

To be able to progress further with our tests, we need the ability to create entities in the database. For that, we'll need to connect to the database.

First, let's add the following two lines to our build.gradle.kts dependencies section:

implementation("org.postgresql:postgresql:42.3.0")
implementation("io.vertx:vertx-pg-client:$vertxVersion")

The first line of code fetches the PostgreSQL driver. The second one adds the Vert.x JDBC client, which allows Vert.x, which has the driver, to connect to any database that supports JDBC.

Managing configuration

Now, we want to hold the database configuration somewhere. For local development, it may be fine to have those configurations hardcoded. We'll execute the following steps to do this:

  1. When we connect to the database, we need to specify the following parameters at the very least:
    • Username
    • Password
    • Host
    • Database name

    We'll store the preceding parameters in a...

Understanding Event Loop

The goal of the Event Loop design pattern is to continuously check for new events in a queue, and each time a new event comes in, to quickly dispatch it to someone who knows how to handle it. This way, a single thread or a very limited number of threads can handle a huge number of events.

In the case of web frameworks such as Vert.x, events may be requests to our server.

To understand the concept of the Event Loop better, let's go back to our server code and attempt to implement an endpoint for deleting a cat:

val db = Db.connect(vertx)
router.delete("/:id").handler { ctx ->
    val id = ctx.request().getParam("id").toInt()
    db.preparedQuery("DELETE FROM cats WHERE ID = $1")        .execute(Tuple.of(id)).await()
    ctx.end()
}

This code is very similar to what we've written in our tests in the previous...

Communicating with Event Bus

Event Bus is an implementation of the Observable design pattern, which we discussed in Chapter 4, Getting Familiar with Behavioral Patterns.

We've already mentioned that Vert.x is based on the concept of verticles, which are isolated actors. We've already seen the other types of actors in Chapter 6, Threads and Coroutines. Kotlin's coroutines library provides the actor() and producer() coroutine generators, which create a coroutine bound to a channel.

Similarly, all the verticles in the Vert.x framework are bound by Event Bus and can pass messages to one another using it. Now, let's extract the code from our ServerVerticle class into a new class, which we'll call CatVerticle.

Any verticle can send a message over Event Bus by choosing between the following methods:

  • request() will send a message to only one subscriber and wait for a response.
  • send() will send a message to only one subscriber, without waiting...

Summary

This chapter concludes our journey into the design patterns in Kotlin. Vert.x uses actors, called verticles, to organize the logic of the application. Actors communicate between themselves using Event Bus, which is an implementation of the Observable design pattern.

We also discussed the Event Loop pattern, how it allows Vert.x to process lots of events concurrently, and why it's important not to block its execution.

Now, you should be able to write microservices in Kotlin using two different frameworks, and you can choose what approach works best for you.

Vert.x provides a lower-level API than Ktor, which means that we may think more about how we structure our code, but the resulting application may be more performant as well. Since this is the end of this book, all that's left is for me to wish you the best of luck in learning about Kotlin and its ecosystem. You can always get some help from me and other Kotlin enthusiasts by going to https://stackoverflow...

Questions

  1. What's a verticle in Vert.x?
  2. What's the goal of the Event Bus?
  3. Why shouldn't we block the Event Loop?
lock icon
The rest of the chapter is locked
You have been reading a chapter from
Kotlin Design Patterns and Best Practices - Second Edition
Published in: Jan 2022Publisher: PacktISBN-13: 9781801815727
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Alexey Soshin

Alexey Soshin is a software architect with 15 years of experience in the industry. He started exploring Kotlin when Kotlin was still in beta, and since then has been a big enthusiast of the language. He's a conference speaker, published writer, and the author of a video course titled Pragmatic System Design.
Read more about Alexey Soshin