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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Kotlin Spring Boot Web-Service Integration With Database
  • Practical Transaction Handling in Microservice Architecture
  • Spring Reactive Microservices: A Showcase
  • Optimize AWS Solution Architecture for Performance Efficiency

Trending

  • AI Speaks for the World... But Whose Humanity Does It Learn From?
  • Implementing Explainable AI in CRM Using Stream Processing
  • AI Agents: A New Era for Integration Professionals
  • Optimizing Serverless Computing with AWS Lambda Layers and CloudFormation
  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.1K 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
  • Practical Transaction Handling in Microservice Architecture
  • Spring Reactive Microservices: A Showcase
  • Optimize AWS Solution Architecture for Performance Efficiency

Partner Resources

×

Comments
Oops! Something Went Wrong

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

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

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 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!