DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Kotlin Spring Boot Web-Service Integration With Database
  • Data Management Patterns for Microservices
  • Component Tests for Spring Cloud Microservices
  • A Robust Distributed Payment Network With Enchanted Audit Functionality - Part 2: Spring Boot, Axon, and Implementation

Trending

  • Throughput vs Goodput: The Performance Metric You Are Probably Ignoring in LLM Testing
  • Stop Writing Dialect-Specific SQL: A Unified Query Builder for Node.js
  • S3 Vectors: How to Build a RAG Without a Vector Database
  • Key Takeaways From Integrating a RAG Application With LangSmith
  1. DZone
  2. Data Engineering
  3. Databases
  4. How to Create Microservices Using http4k

How to Create Microservices Using http4k

Learn how to create a simple microservice-based application using http4k, Kotlin, and JDBI.

By 
Keuller Magalhães user avatar
Keuller Magalhães
·
Jan. 15, 21 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
6.4K Views

Join the DZone community and get the full member experience.

Join For Free

In this article, we will build a simple microservice based on Kotlin, the http4k toolkit , and JDBI.

Our Use Case

Let's consider a simple functionality: registering companies and generating a ranking system. Each company has a score based on their foundation date. Companies are classified by their score, so we have a simple solution, as described below:

article image       

The above diagram shows a simple service with it own database. We are assuming that the Ranking Web (UI) Application will interact with Company Service to show the company list. Let's take a look at some design points:

  • Language/Toolkit: We are using the http4k toolkit. This toolkit provides a simple and uniform way to serve, consume, and test HTTP services. It was entirely written in Kotlin and brings with it the concept of "function as service." http4k provides many server runtimes, such as Netty, Apache, Ktor, etc.
  • Persistence: all data will be managed via JDBI. JBDI is a library that provides an idiomatic access to relational data in the Java world. All data will be stored in a PostgreSQL database.
  • Testing: All services need to be tested and require a lot of testing. To create our unit tests, we are using Kotest and JUnit. Kotest has an amazing DSL for testing, making our tests more fluent.

Getting Started

We will start from the repository, ranking-service. All boilerplate code is provided in this initial repository. Let's build the registration operation. First, when we create a company record, we need to set the company's score in our ranking system. The score calculation is based on its foundation date, so we have some rules for that:

  • Companies less than 2-years-old cannot be included in our rankings.
  • Companies between 2- and 3-years-old have a score of "C".
  • Companies between 4- and 5-years-old have a score of "B".
  • Companies that are 6-years-old or more have a score of "A".

Once the company is registered, we can perform an evaluation using the following:

  • Like
  • Dislike

Those values must be cumulative, and only allowed to go up, not down, based on votes. The company table should look like:

Our entity's code looks like the following:

Kotlin
 




x
13


 
1
import java.time.LocalDate
2
import java.time.LocalDateTime
3

          
4
data class Company(
5
    val id: String,
6
    val name: String,
7
    val site: String,
8
    val foundation: LocalDate,
9
    val score: Char,
10
    val likes: Int = 0,
11
    val dislikes: Int = 0,
12
    val createdAt: LocalDateTime = LocalDateTime.now()
13
)


Operations

Our service must provide three operations, as shown in the table below:

Register a New Company

Now we need to implement our company registration functionality. Let's implement our HTTP handler that represents the first operation shown in the table above. First, let's see the code below:

Kotlin
 




x


 
1
// creates application routes
2
fun routes(): RoutingHttpHandler = routes(
3
    "/" bind GET to index(),
4
    "/v1/companies" bind routes(
5
        "/" bind POST to registerHandler(service),
6
        "/" bind GET to rankingHandler(service),
7
        "/vote" bind PATCH to voteHandler(service)
8
    )
9
)
10

          
11
fun registerHandler(service: CompanyService): HttpHandler {
12
    val registerData = Body.auto<RegisterRequest>().toLens()
13
    return { request ->
14
        val data = registerData(request)
15

          
16
        when(val result = service.register(data)) {
17
            is Success -> Response(Status.CREATED).body(result.value)
18
            is Fail -> Response(Status.BAD_REQUEST).body(result.cause)
19
        }
20
    }
21
}


Inside the Router.kt file, we're defining a function, registerHandler, that receives a service as an argument. http4k defines a Handler like a function: (Request) → Response. Now, let's see the register function from the CompanyService class.

Kotlin
 




xxxxxxxxxx
1
21


 
1
fun register(data: RegisterRequest): Result<String> {
2
    val foundationCompanyDate = LocalDate.parse(data.foundation, DateTimeFormatter.ofPattern(DATE_FORMATTER))
3
  
4
    val companyScore = calculateScore(foundationCompanyDate)
5
    if (companyScore == '0') return Fail("This company is too much younger.")
6

          
7
    val currentCompany = repository.fetchBySite(data.site)
8
    if (currentCompany != null) return Fail("There is a company with same site.")
9

          
10
    val entity = Company(
11
        id = UUID.randomUUID().toString(),
12
        name = data.name,
13
        site = data.site,
14
        foundation = foundationCompanyDate,
15
        score = companyScore
16
    )
17

          
18
    return when(val result = repository.save(entity)) {
19
        is Success -> Success("Company has been created successfully.")
20
        is Fail -> Fail(cause = result.cause)
21
    }
22
}


The function above implements the business logic to register a new company. Basically, we validate the foundation date and check if another company with the same site exists. Our service depends on CompanyRepository, which implements a persistence contract with the database. Let's check the code for the repository's implementation:

Kotlin
 




xxxxxxxxxx
1
24


 
1
class CompanyRepositoryImpl : CompanyRepository {
2

          
3
    override fun save(value: Company): Result<Unit> = try {
4
        Database.runCommand {
5
            it.createUpdate(Database.getSql("db.insertCompany"))
6
                .bindBean(value)
7
                .execute()
8
        }
9
        Success(Unit)
10
    } catch(ex: Exception) {
11
        Fail("Cannot insert company", ex)
12
    }
13

          
14
  
15
    override fun fetchBySite(value: String): Company? {
16
        val entity = Database.runCommand {
17
            it.createQuery(Database.getSql("db.fetchBySite"))
18
                .bind("site", value)
19
                .mapTo<Company>()
20
                .findOne()
21
        }
22

          
23
        return if (entity.isPresent) entity.get() else null
24
    }
25
}


The implementation uses a helper class, Database, which abstracts the boilerplate code used by JDBI under the hood. The function getSql() loads external files which contain SQL statements. Our functionality for company registration has been completed with two persistence functions.

Company Ranking

Once our registration operation is complete, we can build the company listing functionality to display our Rankings page.

Kotlin
 




xxxxxxxxxx
1


 
1
fun rankingHandler(service: CompanyService): HttpHandler = {
2
    val rankingResponse = Body.auto<List<RankingResponse>>().toLens()
3
    rankingResponse(service.ranking(), Response(Status.OK))
4
}


As you can see in code below, our handler defines a body lens that will returns a list of based onRankingResponse. The lens concept is a functional paradigm used by http4k that can read or get a value from an object passed by an argument through the lens. The code for the service is really simple, as we can see below:

Kotlin
 




xxxxxxxxxx
1


 
1
fun ranking(): List<RankingResponse> = repository.fetchRankingList().map { company ->
2
    RankingResponse(company.id, company.name, company.site, company.score, company.likes, company.dislikes)}
3
}


The service basically transforms an entity into a Model object, which will be serialized in response. Now, let's see the repository function to fetch companies.

Kotlin
 




xxxxxxxxxx
1


 
1
override fun fetchRankingList(): List<Company> = Database.runCommand {
2
    it.createQuery(Database.getSql("db.fetchCompanies"))
3
        .mapTo(Company::class.java)
4
        .list()
5
}


Voting

Let's finish our simple service by implementing a Voting operation. Our vote handler must look like:

Kotlin
 




x


 
1
fun voteHandler(service: CompanyService): HttpHandler = { request ->
2
    val voteRequestBody = Body.auto<VoteRequest>().toLens()
3
    val voteRequest = voteRequestBody(request)
4
    service.vote(voteRequest)
5
    Response(Status.NO_CONTENT)
6
}


Now let's see the vote function from the CompanyService class. It has a simple logic that identifies which type of vote we are performing and updates the counter.

Kotlin
 




x


 
1
fun vote(data: VoteRequest) {
2
    when(data.type) {
3
        "like" -> repository.like(data.id)
4
        "dislike" -> repository.dislike(data.id)
5
        else -> logger.warn("Invalid vote type ${data.type}")
6
    }
7
}


The repository implementation is very straightforward, as we can see below:

Kotlin
 




xxxxxxxxxx
1
13


 
1
    override fun like(id: String) {
2
        Database.runCommandInTransaction {
3
            it.createUpdate("UPDATE companies SET likes = likes + 1 WHERE id = :id")
4
                .bind("id", id).execute()
5
        }
6
    }
7

          
8

          
9
    override fun dislike(id: String) {
10
        Database.runCommandInTransaction {
11
            it.createUpdate("UPDATE companies SET dislikes = dislikes + 1 WHERE id = :id")
12
                .bind("id", id).execute()
13
        }
14
    }


Wrapping Up

I hope this little tutorial has shown you the power and simplicity that http4k brings to us. Creating services is really simple and enjoyable using http4k and in the future we will explore more features. If you want a deep dive into more of http4k, take a look at the many examples and documentation from the http4k website. The complete code of this tutorial can be found at https://github.com/keuller/tutorial-ranking-service. See you next time!

Database microservice Kotlin (programming language) Web Service Relational database

Opinions expressed by DZone contributors are their own.

Related

  • Kotlin Spring Boot Web-Service Integration With Database
  • Data Management Patterns for Microservices
  • Component Tests for Spring Cloud Microservices
  • A Robust Distributed Payment Network With Enchanted Audit Functionality - Part 2: Spring Boot, Axon, and Implementation

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook