Monday, May 29, 2023

Interpreter Design Pattern with Kotlin

 Interpreter Design Patterns

This design pattern may seem very simple or very hard, all based on how much background you have in computer science. Some books that discuss classical software design patterns even decide to omit it altogether, or put it somewhere at the end, for curious readers only. The reason behind this is that the interpreter design pattern deals with translating certain languages. But why would we need that? Don't we have compilers to do that anyway?

We need to go deeper In this section we discuss that all developers have to speak many languages or sub-languages. Even as regular developers, we use more than one language. Think of tools that build your projects, like Maven or Gradle. You can consider their configuration files, build scripts, as languages with specific grammar. If you put elements out of order, your project won't be built correctly. And that's because such projects have interpreters to analyze configuration files and act upon them. Other examples would be query languages, be it one of the SQL variations or one of the languages specific to NoSQL databases. If you're an Android developer, you may think of XML layouts as such languages too. Even HTML could be considered a language that defines user interfaces. And there are others, of course.

Maybe you've worked with one of the testing frameworks that define a custom language for testing, such as Cucumber: github.com/cucumber. Each of these examples can be called a Domain Specific Language (DSL). A language inside a language. We'll discuss how it works in the next section.

A language of your own In this section, we'll define a simple DSL-for-SQL language. We won't define the format or grammar for it, but only an example of what it should look like: 

val sql = select("name, age", {

              from("users", {

                  where("age > 25")

              }) // Closes from

          }) // Closes select

 println(sql) // "SELECT name, age FROM users WHERE age > 25" The goal of our language is to improve readability and prevent some common SQL mistakes, such as typos (like FORM instead of FROM). We'll get compile time validations and autocompletion along the way. We'll start with the easiest part—select: fun select(columns: String, from: SelectClause.()->Unit): 

    SelectClause {

return SelectClause(columns).apply(from)

We could write this using single expression notation, but we use the more verbose version for clarity of the example. This is a function that has two parameters. The first is a String, which is simple. The second is another function that receives nothing and returns nothing. The most interesting part is that we specify the receiver for our lambda: SelectClause.()->Unit 

This is a very smart trick, so be sure to follow along: 

SelectClause.()->Unit == (SelectClause)->Unit 

Although it may seem that this lambda receives nothing, it actually receives one argument, an object of type SelectClause. The second trick lies in the usage of the apply() function we've seen before. Look at this: SelectClause(columns).apply(from) 

It translates to this: 

val selectClause = SelectClause(columns)

from(selectClause)

return 

selectClause Here are the steps the preceding code will perform: Initialize SelectClause, which is a simple object that receives one argument in its constructor. Call the from() function with an instance of SelectClause as its only argument. Return an instance of SelectClause. That code only makes sense if from() does something useful with SelectClause. Let's look at our DSL example again: 

select("name, age", {

    this@select.from("users", {

        where("age > 25")

    })

}) 

We've made the receiver explicit now, meaning that the from() function will call the from() method on the SelectClause object.

You can start guessing what this method looks like. It clearly receives a String as its first argument, and another lambda as its second:

class SelectClause(private val columns: String) {

    private lateinit var from : FromClause

    fun from(table: String, where: FromClause.()->Unit): FromClause {

        this.from = FromClause(table)

        return this.from.apply(where)

    }

This could again be shortened, but then we'd need to use apply() within apply(), which may seem confusing at this point. That's the first time we've met the lateinit keyword. This keyword is quite dangerous, so use it with some restraint. Remember that the Kotlin compiler is very serious about null safety. If we omit lateinit, it will require us to initialize the variable with a default value. But since we'll know it only at a later time, we ask the compiler to relax a bit. Note that if we don't make good on our promises and forget to initialize it, we'll get UninitializedPropertyAccessException when first accessing it. Back to our code; all we do is: Create an instance of FromClause Store it as a member of SelectClause Pass an instance of FromClause to the where lambda Return an instance of FromClause Hopefully, you're starting to get the gist of it: 

select("name, age", {

    this@select.from("users", {

        this@from.where("age > 25")

    })

}) 

What does it mean? After understanding the from() method, this should be much simpler. The FromClause must have a method called where() that receives one argument, of the String type: 

class FromClause(private val table: String) {

    private lateinit var where: WhereClause

 fun where(conditions: String) = this.apply {

        where = WhereClause(conditions)

    }

Note that we made good on our promise and shortened the method this time. We initialized an instance of WhereClause with the string we received, and returned it. Simple as that:


class WhereClause(private val conditions: String) {

    override fun toString(): String {

        return "WHERE $conditions"

    }

WhereClause only prints the word WHERE and the conditions it received: class FromClause(private val table: String) {

    // More code here... override fun toString(): String {

        return "FROM $table ${this.where}"

    }

}

 FromClause prints the word FROM as well as the table name it received, and everything WhereClause printed: class SelectClause(private val columns: String) {

    // More code here...

    override fun toString(): String {

        return "SELECT $columns ${this.from}"

    }

SelectClause prints the word SELECT, the columns it got, and whatever FromClause printed.

Taking a break 

Kotlin provides beautiful capabilities to create readable and type-safe DSLs. But the interpreter design pattern is one of the hardest in the toolbox. If you didn't get it from the get-go, take some time to debug this code. Understand what this means at each step, as well as when we call a function and when we call a method of an object.

Call suffix 

In order not to confuse you, we left out one last notion of Kotlin DSL until the end of this section. Look at this DSL: 

val sql = select("name, age", {

              from("users", {

                  where("age > 25")

              }) // Closes from

          }) // Closes select It could be rewritten as: val sql = select("name, age") {

              from("users") {

                  where("age > 25")

              } // Closes from

          } // 

Closes select This is common practice in Kotlin. If our function receives another function as its last argument, we can pass it out of parentheses. This results in a much clearer DSL, but may be confusing at first.

Code Example :

package designpatterns

fun main(args: Array<String>) {
val sql = select("name, age") {
from("users") {
where("age > 25")
}
}

println(sql) // "SELECT name, age FROM users WHERE age > 25"
}

fun select(columns: String, from: SelectClause.() -> Unit): SelectClause {
from(SelectClause(columns))
return SelectClause(columns).apply(from)
}


class SelectClause(private val columns: String) {

private lateinit var from: FromClause
fun from(table: String, where: FromClause.() -> Unit): FromClause {
this.from = FromClause(table)

return this.from.apply(where)
}

override fun toString(): String {
return "SELECT $columns ${this.from}"
}
}

class FromClause(private val table: String) {
private lateinit var where: WhereClause

fun where(conditions: String) = this.apply {
where = WhereClause(conditions)
}

override fun toString(): String {
return "FROM $table ${this.where}"
}
}

class WhereClause(private val conditions: String) {
override fun toString(): String {
return "WHERE $conditions"
}
}

Output :
SELECT name, age FROM users WHERE age > 25







Sunday, May 28, 2023

Microservice - Configuration Server , Discovery Server and Gateway in Cloud Native way

 Creating Cloud-Native Microservices 

Creating microservices that are able to work natively in a cloud infrastructure is something that we should look forward to when we design a microservices architecture that follows our microservices principles. we will learn about Cloud-Native microservices, and how Spring Cloud allows us to easily create them using a vendor-independent approach. To understand more about Spring Cloud, we will discuss how it works and how we can use it to create our microservices. Finally, we will cover the most important components that Spring Cloud provides. 

Understanding Spring Cloud 

Configuration server 

Service discovery 

Gateway

Understanding Spring Cloud 

Modern microservices architecture focuses on deploying microservices into a cloud. This will enable a more optimal model for our products since we can grow or shrink the infrastructure as per our needs; however, the landscape of cloud providers has grown drastically, and many platforms need to tide our microservices to a particular vendor. Spring Cloud allows a vendor-agnostic approach that permits us to deploy our services in any cloud and take the full benefits of cloud computing, converting our microservices into Cloud-Native microservices.

What is a Cloud-Native application? 

The cloud has very interesting capabilities. Applications that will be deployed in a cloud need to use cloud capabilities in order to work effectively. For example, in a cloud we should be able to scale our application; we can start three instances of a service if we need to increase the capacity of the application. We can increase the number of instances from three to five, but when we do this, we need to do it in a timely manner so that new instances are ready to work right away, therefore the application needs to be built with the ability to start quickly. In the same way, an application that is on a cloud may consume other services from the platform and should go through the platform capabilities. If we are invoking one service from another, since the instances are created or destroyed dynamically, we can't just have a static set of URLs to access them. Our application should query the cloud to find the services and their instances. Since we don't know when an instance will be created or destroyed, it is not so easy to synchronize data between the ones that are running, neither guarantee that different requests for a client or customer will always hit the same instance. Hence, we can not guarantee affinity in the same way that a traditional architecture can. We highlight this because we believe that a cloud application needs to be aware that it is running on a cloud, since the actions that will be performed need to be understood in the context that is used; the same things that work outside a cloud may not work in a cloud. For this, we need to create cloud-aware microservices. However, we can try to make an application that is not cloud-aware work in a cloud. For our previous example, when instances are created or destroyed, we can map them using a routing system external to the cloud, so our application can still use it, without using the cloud capabilities to discover services. We never recommend making things that are not cloud-aware to run in a cloud. If we are in a cloud, it is to fully utilize its benefits. If not, it's probably better that we choose a more traditional architecture. When we create a cloud-aware microservice that is used natively in the cloud where it's running, we are creating a Cloud-Native microservice. Spring Cloud allows us to easily create Cloud-Native microservices.

Spring Cloud architecture 

Spring Cloud provides a framework to easily create Cloud-Native microservices with the benefit of having a vendor-agnostic approach, while the same components can be used seamlessly in a range of cloud providers. But before using it, we need to understand how the architecture of their components works. There are some key components in this architecture, but before understanding them, we will review the architectural patterns that will be used to build our Cloud-Native microservices:

Configuration server 

Service discovery 

Gateway 

Circuit breaker

Configuration server Configuring our microservices is usually a complex topic, as we learned in Chapter 2, Getting Started with Spring Boot 2.0. We can use Spring configuration to simplify this, but it is still a complicated process. We can't just statically set the microservices configuration in our application code, since it can change when our cloud changes. We need a mechanism that allows our services to request their configuration. A Configuration Server provides the capability to be queried about configurations, so when a microservice starts, it can retrieve all the values that need to be configured, such as URL, database connections, password, and anything that can be configured, from the Configuration Server:



For example, let's imagine that we have a Configuration Server ready and two microservices that use it; the Products Microservice Instances and the Customers Microservice Instances. When new instances are created, they can retrieve their configuration from the config server. This allows us to easily create those service instances since all configuration required by our microservice will be provided by the Configuration Server. This speeds the creation of the new instance since they don't require any more configuration than the one provided by the Configuration Server.

Service discovery 

In a cloud, we may need to connect to other services, but we don't actually know where this service is and how many instances are available. This requirement can be satisfied using a Service Discovery Server. Each instance of any service, when created, will be registered in the Service Discovery server. Thus, an application can ask what instance is available, but the Service Discovery needs to keep an up-to-date view of what is available. When the microservice is shutting down, it needs to be deregistered from the Discovery Service, so it gets removed from the known instances; however, a microservice could go down drastically (for example, in an application error) without informing the discovery service. This could result in a situation where the discovery service may have registered instances that are actually not working or no longer available. To avoid that situation, discovery services could use the Heart-beat mechanism. A Heart-beat is a way for a microservice to tell a Service Discovery that it is available and ready to work. It is like sending a request to the Discovery Server every five minutes to confirm that it is still alive. If the Discovery Server does not receive those Heart-beats from a microservice instance, it will assume the microservice is dead and removes it from the list of known instances of that microservice. Finally, with an updated list of microservice instances, it is possible for a microservice to find, when needed, the instance of any service. Let's visualize this with an example:



In this example, we have a Products Microservice that is registered in a Service Discovery Server, and will periodically notify that it is still up and running via a Heart-beat. When another microservice, such as customers in this example, asks for the Products Microservice, the Service Discovery Server will be able to answer back with the available instances of the Products Microservice.

Load Balancer 

When we have a microservice that needs to send a request to a different microservice, we can use a Service Discovery to get the list of instances available of the services that we want to use. Then we may need to choose one instance among them to perform our request. Eventually, we may need to perform another request, and in that case, we may want to choose a different instance so our request is distributed evenly to the different instances. To achieve this, we can use the Load Balancer pattern. To perform, for example, a round-robin iteration on the instance of a microservice to distribute calls to them. So when we perform the first request, it will choose the first instance, for the next request, the second instance, and so on, until there are no more instances and we return back to the first. On top of that, we may need to add the information from our Service Discovery service, since the instances can be created and destroyed, and then register or unregister from the Service Discovery Server. We may need to check that the list of the instance that we are iterating is up to date and managed correctly. This will make an optimal usage of the instances of our microservices, and split the load among them evenly.

Gateway 

When we need to expose our microservices to be used by any application, we may need to use the Gateway pattern. In a Gateway pattern, we have an entry point to our microservice whose mission is to simplify the access to them; instead, to make the consumer applications use a Discovery Server and a Load Balancer to find our microservice instances, we expose them using a simple route and managing the details for them. Let's draw an example to explore this topic further:



We have two microservices registered on our Service Discovery Server, Customers Microservice, and Products Microservice. When an application (for example, a web application) invokes our Gateway using the /products path, our Gateway can: Find all the instances of the Products Microservice using the Service Discovery Server Rotate among them using a Load Balancer Invoke the selected instance Return the result back to the calling application And if the web application uses the /customers path, we can send that request to the microservice customers instead. The web application does not need to use the Service Discovery directly, neither the Load Balancer, since this is done by the Gateway internally, giving a simple interface to the calling applications. Since our Gateway is an entry point to our microservice, we can use it to add a couple of benefits to our architecture. Our Gateway can be used as the entry point for our security, checking the credentials or privileges to access those microservices, and if those security mechanisms need to be changed, we will change them just in a single place—our access point, our Gateway. Decouple who implements a call, if tomorrow the /products path will be implemented by another microservice, we can change it in our Gateway without affecting the application that uses it. The Gateway pattern is a common pattern for enterprise applications that have existed for a very long time, but in today's cloud era, it makes more sense than ever before.

Circuit breaker 

In our microservice, we may need to perform operations that can eventually fail. If those operations fail, we need to ensure that we are still able to answer our users, and that other parts of the software work as they should. Let's imagine that we have three microservices: The Opportunities microservice will return a list of customers and the offers tied to each of them The Customers microservices will return just customer information The Offers microservice will return offers information The Opportunities microservice will call the Customers microservices to get the customer information, and for each of the customers, it will call the Offers microservice to retrieve the offers tied to them. Let's assume that the Offers microservice is not working, and since Opportunities uses Offers every single time our Opportunities microservice is being called, we return an error back to the users of our microservice. In doing so, we may be overstressing the Offers microservice that we know is not working, but we continue calling it and getting an error. In fact, we may even decrement the performance of the Opportunities microservice; since waiting for a response from the Offers microservice may take a long time, we are leaking a problem from a dependency, Offers, into our Opportunities microservice. To prevent this from happening, we can encapsulate the operation by calling the Offers microservice from the Opportunities microservice in a circuit breaker. A circuit breaker can have an open or closed status, telling us if the operation that encapsulates can be used—think of circuit breakers as fuses in an electrical installation. When the operation fails, we can just close the circuit for a period of time, then if new requests are coming to our Opportunities microservice since the circuit is closed, we just return an error without sending a request to the Offers microservice. After a certain amount of time, we can check whether the Offers microservice is working again, opening the circuit and allowing to return valid responses again, but in the period of time that the circuit was closed, we were given time for the Offers microservice to recover. But we can find other benefits in this technique.A circuit breaker allows us to define a fallback mechanism. If we can't answer with the offers since the circuit is closed, we could just return a default set of offers to whoever invokes our Opportunities microservice. This will allow our application to still show some information. In most cases, this is much better than just an error without any information at all. We always need to ensure that a failure in one part of our application does not make other parts, or even the whole application, fail. Circuit breakers are a perfect mechanism for this, even in the worst scenarios.

Spring Cloud Netflix 

Netflix is one of the big contributors to open source initiatives. They are a company with a tremendous focus on microservices, so within the Netflix OSS (Open Source Software), they originally created many components that implement the cloud architecture pattern, and we can easily use them with Spring Cloud since doing so allows us to choose the implementation of the pattern. Let's review the ones that we are going to use: Eureka: A Service Discovery service that we can easily use to register or find instances of our microservices. Ribbon: A configurable software Load Balancer that can be integrated with Eureka to distribute calls within the microservices instances. Hystrix: A configurable circuit breaker, with a fall-back mechanism, that we can use when creating microservices. Zuul: A Gateway Server that uses Eureka, Ribbon, and Hystrix to implement the Gateway pattern.

Now Lets check our code : It will make more sense.

1. Config_Server :

package com.microservices.configserver


import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.config.server.EnableConfigServer

@SpringBootApplication
@EnableConfigServer
class ConfigServerApplication

fun main(args: Array<String>) {
runApplication<Chapter06ConfigServerApplication>(*args)
}

under resources: config folder:

microservice:
example:
greetings: "hello from config server"
zuul:
ignoredServices: '*'
routes:
greetings:
path: /message/**

serviceId: greetings         

microservice:
example:
greetings: "{cipher}7b310fe9e22a913d6a21dd768a616f9700ba4bde6f1879b4d82a5a09ea8344a4"
microservice:
example:
greetings: "hello from another profile"

application.yml

server:
port: 8888
spring:
profiles:
active: native
cloud:
config:
server:
native:
search-locations: classpath:config/

bootstrap.yml

encrypt.key: "this_is_a_secret"
spring:
cloud:
config:
server:
encrypt:
enabled: false
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.microservices</groupId>
<artifactId>chapter06-config-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>chapter06-config-server</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.M5</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jre8</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>


</project>

2.Discovery_Server :

package com.microservices.discoveryserver

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
class Chapter06DiscoveryServerApplication

fun main(args: Array<String>) {
runApplication<Chapter06DiscoveryServerApplication>(*args)
}

application.yml

server:
port: 8761
spring:
application:
name: "discovery-server"
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.microservices</groupId>
<artifactId>chapter06-discovery-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>chapter06-discovery-server</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.M5</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jre8</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>


</project>

3.Gateway :

package com.microservices.gateway

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.netflix.zuul.EnableZuulProxy

@SpringBootApplication
@EnableZuulProxy
class Chapter06GatewayApplication

fun main(args: Array<String>) {
runApplication<Chapter06GatewayApplication>(*args)
}

application.yml

spring:
application:
name: "gateway"
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.microservices</groupId>
<artifactId>chapter06-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>chapter06-gateway</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.M5</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jre8</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>


</project>


Chain of Responsibility Design Pattern with Kotlin

 Chain Of Responsibility

Chain of responsibility I'm a horrible software architect, and I don't like to speak with people. Hence, while sitting at home, I wrote a small web application. If a developer has a question, he shouldn't approach me directly, oh no. He'll need to send me a proper request through this system, and I shall answer him only if I deem this request worthy. A filter chain is a very common concept in web servers. Usually, when a request reaches to you, it's expected that: Its parameters are already validated The user is already authenticated, if possible User roles and permissions are known, and the user is authorized to perform an action So, the code I initially wrote looked something like this:

fun handleRequest(r: Request) {

    // Validate

    if (r.email.isEmpty() || r.question.isEmpty()) {

return

    }

     // Authenticate

    // Make sure that you know whos is this user

    if (r.email.isKnownEmail()) {

        return

    }

     // Authorize

    // Requests from juniors are automatically ignored by architects

    if (r.email.isJuniorDeveloper()) {

        return

    }

 println("I don't know. Did you check StackOverflow?")

A bit messy, but it works. Then I noticed that some developers decide they can send me two questions at once. Gotta add some more logic to this function. But wait, I'm an architect, after all. Isn't there a better way to delegate this? This time, we won't learn new Kotlin tricks, but use those that we already learned. We could start with implementing an interface such as this one:

interface Handler {

    fun handle(request: Request): Response

We never discussed what my response to one of the developers looked like. That's because I keep my chain of responsibility so long and complex that usually, they tend to solve problems by themselves. I've never had to answer one of them, quite frankly. But at least we know what their requests look like: 

data class Request(val email: String, val question: String)

data class Response(val answer: String) 

Then we could do it the Java way, and start implementing each piece of logic inside its own handler: 

 class BasicValidationHandler(private val next: Handler) : Handler {

    override fun handle(request: Request): Response {

        if (request.email.isEmpty() || request.question.isEmpty()) {

            throw IllegalArgumentException()

        }

 return next.handle(request)

    }

}

Other filters would look very similar to this one. We can compose them in any order we want: 

val req = Request("developer@company.com", "Who broke my build?")

 val chain = AuthenticationHandler(

                BasicValidationHandler(

                    FinalResponseHandler()))

 val res = chain.handle(req)

 println(res)  

But I won't even ask you the rhetoric question this time about better ways. Of course there's a better way, we're in the Kotlin world now. And we've seen the usage of functions in the previous section. So, we'll define a function for that task: 

typealias Handler = (request: Request) -> Response 

Why have a separate class and interface for something that receives a request and returns a response in a nutshell: 

val authentication = fun(next: Handler) =

    fun(request: Request): Response {

        if (!request.email.isKnownEmail()) {

            throw IllegalArgumentException()

        }

return next(request)

    } 

Here, authentication is a function that literally receives a function and returns a function. Again, we can compose those functions: 

val req = Request("developer@company.com", "Why do we need Software Architects?")

 val chain = basicValidation(authentication(finalResponse()))

 val res = chain(req)

 println(res) 

It's up to you which method you choose. Using interfaces is more explicit, and would better suit you if you're creating your own library or framework that others may want to extend. Using functions is more concise, and if you just want to split your code in a more manageable way, it may be the better choice.

Code Example :

package designpatterns.chainOfResponsibility



typealias Handler = (request: Request) -> Response

fun main(args: Array<String>) {

val req = Request("developer@company.com", "Why do we need Software
                                   Architects?")

val chain = basicValidation(authentication(finalResponse()))

val res = chain(req)

println(res)
}


val basicValidation = fun(next: Handler): Handler {
return fun(request: Request): Response {
if (request.email.isEmpty() || request.question.isEmpty()) {
throw IllegalArgumentException()
}
return next(request)
}
}

val authentication = fun(next: Handler) =
fun(request: Request): Response {
if (!request.email.isKnownEmail()) {
throw IllegalArgumentException()
}
return next(request)
}

val finalResponse = fun() = fun(_: Request) = Response("Try git-blame.")


private fun String.isKnownEmail(): Boolean {
return true
}


data class Request(val email: String, val question: String)

data class Response(val answer: String)


Output :
Response(answer=Try git-blame.)


package designpatterns.not


fun handleRequest(r: Request) {
// Validate
if (r.email.isEmpty() || r.question.isEmpty()) {
return
}

// Authenticate
// Make sure that you know whos is this user
if (r.email.isKnownEmail()) {
return
}

// Authorize
// Requests from juniors are automatically ignored by architects
if (r.email.isJuniorDeveloper()) {
return
}

println("I don't know. Did you check StackOverflow?")
}

private fun String.isJuniorDeveloper(): Boolean {
return false
}

private fun String.isKnownEmail(): Boolean {
return true
}


data class Request(val email: String, val question: String)
package designpatterns.chainOfResponsibility.java


fun main(args: Array<String>) {
val req = Request("developer@company.com",
"Why do we need Software Architects?")

val chain = AuthenticationHandler(
BasicValidationHandler(
FinalResponseHandler()))

val res = chain.handle(req)

println(res)
}


class BasicValidationHandler(private val next: Handler) : Handler {
override fun handle(request: Request): Response {
if (request.email.isEmpty() || request.question.isEmpty()) {
throw IllegalArgumentException()
}

return next.handle(request)
}
}

class AuthenticationHandler(private val next: Handler) : Handler {
override fun handle(request: Request): Response {
if (!request.email.isKnownEmail()) {
throw IllegalArgumentException()
}

return next.handle(request)
}
}

class FinalResponseHandler : Handler {
override fun handle(request: Request) = Response("Read documentation.")
}

private fun String.isJuniorDeveloper(): Boolean {
return true
}

private fun String.isKnownEmail(): Boolean {
return true
}

interface Handler {
fun handle(request: Request): Response
}


data class Request(val email: String, val question: String)

data class Response(val answer: String)

Output :

Response(answer=Read documentation.)


Saturday, May 27, 2023

Creating a Reactive Microservice with Kotlin Live Code Example with MongoDb

 Creating A Reactive Microservice with Kotlin

Reactive microservices are the next step in the evolution of microservices. Based on the reactive paradigm, they target delivering more responsive, resilient, and elastic message-driven services that will outperform the more traditional non-reactive architectures. We will learn how easily we can create them using Spring Framework 5.0, and how we can use reactive programming to create them. We learned about the benefits of reactive programming. You can review the section covering reactive programming to understand this topic further. The reactive microservices that we will create based on our previously created RESTful API examples, showing how easily we can adapt to this new model.

In this blog, you will learn about: 

Spring WebFlux 

Router functions

Mono 

Flux 

Introduction to functional programming Reactive error handling

Understanding Spring WebFlux 

Spring WebFlux is a new component introduced in Spring Framework 5.0 that allows the creation of reactive microservices using Netty as the new web/application server. WebFlux extensively uses the Reactor Framework to implement the reactive streams pattern. In this section, we will understand how to create Spring WebFlux applications, and how we can use them to migrate our non-reactive microservices into this new technological stack.

Using Netty 

Netty was originally developed by JBoss with the idea to create a Client-Server Framework that allows us to perform non-blocking IO operations. To archive this capability, we use a message-driven implementation of the reactor pattern. Nowadays, it is supported by an extensive open source community. Netty includes support for major algorithm and protocols such as HTTP, SSL/TSL, or DNS, but adds support for modern protocols such as HTTP/2, WebSocket, or Google Protocol Buffers. This is just a summary of some of Netty's capabilities. You can find more information on Netty on their website at https://netty.io/. Spring Boot 1.x uses Apache Tomcat as the default application/web server, but Tomcat performs only blocking operations. In order to be able to create reactive services, Spring Boot 2.0 has chosen Netty instead, because of its non-blocking IO capabilities.

When using Spring Boot 2.0, if we include the Spring WebFlux component, when our application starts it will create a new Netty server, but if we use the standard Spring Web, a new Tomcat server will be started instead. Understanding Microservices, one of the desired qualities for any reactive system is to be responsive; event-driven software, such as Netty, will fulfill this requirement among the rest of the Reactive Manifesto. We analyze the Reactive Manifesto in Chapter 1, Understanding Microservices, in the Reactive microservices section. With this technology, our microservices can handle a higher workload and will be more effective than ever before. There are other non-blocking IO systems for different technologies such as NodeJS, Nginx, Apache Mina, Vert.X, or Akka. It will probably be a good idea to learn some of them since reactive systems will be one of the most used technologies in coming years.

Blocking is not reactive 

We can see that we can easily convert our more traditional non-reactive microservices to reactive, but this service is not fully reactive yet. Our controller may be, but if the operation performed is a blocking operation, it will do blocking operations as any other non-reactive system would do. So as we have declared, when a client invokes our URL, we will call our service to either get a customer or search for them, and when this operation is completed, we serialize the results as JSON; only then will we return the value to the consumer, as our operation is a blocking operation. We may need to modify our service to become a reactive service.

Understanding subscribers and publishers First

we need to understand a core component in reactive programming, the subscribe and publish mechanism. Reactive programming is based on the event-model mechanism, in which a set of events are triggered and dispatched to whoever needs them. This abstract concept can easily be understood with how we handle actions from users in almost all UI Frameworks. Let's think that we want to react to a UI action, for example, pressing a button and then performing an operation such as closing a window. In a normal UI Framework, user actions such as pressing buttons, selecting menus, or scrolling the contents of a window can be considered events. When we choose to listen to those events, what we are defining is a subscriber, an object whose responsibility is to receive an event when it is produced. On the other hand, we may need to generate events; for example, when we click with our mouse on a button, a new event about the button being clicked needs to be triggered. By doing so we define a publisher, an object whose responsibility is to emit events. Our UI Framework will probably connect things together and when we click the button, the publisher will generate the event, and the subscriber will be notified about it; then, it can react by performing the desired functionality, closing the window. But events can be chained and combined. In a UI Framework, we may already have a publisher that is sending any mouse click event on the screen, and we may also have a subscriber for those events. Then, when we click on the screen, our publisher emits that generic message and the more generic subscribe will get it and find out that the position of the mouse is actually on a certain button. This generic subscribe will trigger a new event about that button being clicked on, however, is subscribe to that event could process that as an action to perform the desired logic.

Let's visualize this with a bit of pseudocode:

Framework Starts

Registering MouseClick as a subscriber for mouse.click events

Registering MouseHandler as a publisher of mouse.click events

Application Starts

This is probably not the most optimal implementation of a UI event system, but it serves to illustrate the concept. In each of these subscriber and publisher objects, we are not blocking the operation waiting for a result; they are in a way listeners that will be invoked when required. Meanwhile, other actions and events in our UI can happen, even simultaneously, such as pressing a button at the same time that we press a key. The publish and subscribe mechanism is not a new concept, and neither is the event-driven system; they have been used for a very long time, from managing our hardware to the more complicated message queue systems. However, with the reactive programming approach, they are more relevant than ever.

Spring uses the Reactor Framework to create reactive microservices. Our controllers can become publishers of results, and Spring will subscribe to produce more events that will send the data back to whoever is using our services. This data will be sent in a reactive stream, as was defined in the reactive streams specification, providing non-blocking back-pressure.

MONO

Publishing a single object Reactor provides a way to define a reactive publisher through a class named Mono, but that publisher can only send one result. What we should understand is that a Mono is not actually the value of the Customer instance that we create, it is a promise of what we are going to get. When we declare something as Mono<Customer>, we are just indicating that this is a publisher that in future will publish a customer. When someone is subscribed to that publisher, the data will be consumed.

FLUX

Publishing multiple objects Reactor provides a class to create a publisher that will return from 0 to an undetermined number of elements; that class is named Flux. Mono can be used when we only need to return one result in our publisher, whereas Flux can be used for 0 to N including 1, but if we are going to return just 1, it is preferable to use Mono instead of Flux since it is more optimal for that usage.

Now lets have a look at the sample code : This is a live Reactive Microservice Code.

package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class ReactiveMicroserviceApplication

fun main(args: Array<String>) {
runApplication<ReactiveMicroserviceApplication>(*args)
}
package com.example.demo

import org.springframework.data.mongodb.core.mapping.Document

@Document(collection = "Customers")
data class Customer(var id: Int = 0, var name: String = "",
                         var telephone: Telephone? = null) {
data class Telephone(var countryCode: String = "",
                    var telephoneNumber: String = "")
}
package com.example.demo

import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.BodyInserters.fromObject
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse.*
import org.springframework.web.reactive.function.server.bodyToMono
import java.net.URI

@Component
class CustomerHandler(val customerService: CustomerService) {
fun get(serverRequest: ServerRequest) =
customerService.getCustomer(serverRequest.pathVariable("id").toInt())
.flatMap { ok().body(fromObject(it)) }
.switchIfEmpty(status(HttpStatus.NOT_FOUND).build())

fun create(serverRequest: ServerRequest) =
customerService.createCustomer(serverRequest.bodyToMono()).flatMap {
created(URI.create("/functional/customer/${it.id}")).build()
}

fun delete(serverRequest: ServerRequest) =
customerService.deleteCustomer(serverRequest.pathVariable("id").toInt())
.flatMap {
if (it) ok().build()
else status(HttpStatus.NOT_FOUND).build()
}

fun search(serverRequest: ServerRequest) =
ok().body(
customerService.searchCustomers(
serverRequest.queryParam("nameFilter")
.orElse("")
), Customer::class.java
)
}
package com.example.demo

import jakarta.annotation.PostConstruct
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.find
import org.springframework.data.mongodb.core.findById
import org.springframework.data.mongodb.core.query.Criteria.where
import org.springframework.data.mongodb.core.query.Query
import org.springframework.data.mongodb.core.query.isEqualTo
import org.springframework.data.mongodb.core.remove
import org.springframework.stereotype.Repository
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono


@Repository
class CustomerRepository(private val template: ReactiveMongoTemplate) {
companion object {
val initialCustomers = listOf(
Customer(1, "Kotlin"),
Customer(2, "Spring"),
Customer(3, "Microservice",Customer.Telephone("+91", "7123456789"))
)
}

@PostConstruct
fun initializeRepository() =
initialCustomers.map(Customer::toMono).map(this::create).map(Mono<Customer>::subscribe)

fun create(customer: Mono<Customer>) = template.save(customer)
fun findById(id: Int) = template.findById<Customer>(id)
fun deleteById(id: Int) = template.remove<Customer>(Query(where("_id").isEqualTo(id)))
fun findCustomer(nameFilter: String) = template.find<Customer>(
Query(where("name").regex(".*$nameFilter.*", "i"))
)
}
package com.example.demo

import org.springframework.context.annotation.Bean
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.router

@Component
class CustomerRouter(private val customerHandler: CustomerHandler) {
@Bean
fun customerRoutes() = router {
"/customer".nest {
GET("/{id}", customerHandler::get)
POST("/", customerHandler::create)
DELETE("/{id}", customerHandler::delete)
}
"/customers".nest {
GET("/", customerHandler::search)
}
}
}
package com.example.demo

import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

interface CustomerService {
fun getCustomer(id: Int): Mono<Customer>
fun createCustomer(customer: Mono<Customer>): Mono<Customer>
fun deleteCustomer(id: Int): Mono<Boolean>
fun searchCustomers(nameFilter: String): Flux<Customer>
}
package com.example.demo

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono

@Service
class CustomerServiceImpl : CustomerService {
@Autowired
lateinit var customerRepository: CustomerRepository

override fun getCustomer(id: Int) = customerRepository.findById(id)
override fun createCustomer(customer: Mono<Customer>) =
                   customerRepository.create(customer)
override fun deleteCustomer(id: Int) =
                   customerRepository.deleteById(id).map { it.deletedCount > 0 }
override fun searchCustomers(nameFilter: String) =
                    customerRepository.findCustomer(nameFilter)
}
spring:
data:
mongodb:
uri: "mongodb://localhost:27700"
database: "microservices"
spring.jackson.default-property-inclusion: NON_NULL
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ReactiveMicroservice</name>
<description>Demo project for Spring Boot</description>
<properties>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<kotlin.version>1.8.21</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor.kotlin</groupId>
<artifactId>reactor-kotlin-extensions</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>

</project>

Microservices Responses :

[

  {

    "id": 1,

    "name": "Kotlin"

  },

  {

    "id": 2,

    "name": "Spring"

  },

  {

    "id": 18,

    "name": "New Customer",

    "telephone": {

      "countryCode": "+44",

      "telephoneNumber": "7123456789"

    }

  },

  {

    "id": 3,

    "name": "Microservice",

    "telephone": {

      "countryCode": "+44",

      "telephoneNumber": "7123456789"

    }

  }

]

Bad Request Example :

{

  "error": "error creating customer",

  "message": "JSON decoding error: Unexpected character ('b' (code 98)): expected a valid value (number, String, array, object, 'true', 'false' or 'null'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('b' (code 98)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')\n at [Source: UNKNOWN; line: 9, column: 2]"

}

I have shared the code . you can copy-paste the code and use it  and start your Reactive Microservice journey with Kotlin.