SECRET OF CSS

First Impressions When Switching From Spring Boot to Ktor | by Matthias Schenk | Jul, 2022


1*dX5WByhliJbHx hl9pgnHQ

When I switched from Java to Kotlin I continued using my familiar tech stack consisting of several parts out of the SpringBoot ecosystem. With some small differences, which are mostly handled by the Kotlin Spring plugin, I could use it the same way I was used to doing in the Java world.

As my interest in Kotlin-related stuff increases, I searched for a Kotlin-native alternative that offers me a similar feature scope as SpringBoot. A lot of people in the Kotlin community recommended Ktor to me.

In this article, I will share my first impression when using Ktor in comparison to Spring Boot.

With IntelliJ, it’s easy to create a new Ktor project using the “New Project” menu which provides a wizard similar to the creation of a Spring Boot project using the SpringBoot starter

1*w T 4JPfLpBHriLqjnWl0Q
1*cAuLmg7FUVaMcQWkKF8K A

To be able to compare the way functionality is implemented with Ktor with Spring Boot, I will use my previously created Rest API application using Kotlin + Spring Boot (incl. JPA). For sure my solution is not the idiomatic way to work with Ktor + Koin + Exposed but a try to see how things are done with the same requirements.

So my first step will be the writing of the acceptance test to guide my implementation. This is the first version of the test (the setup of the test will be updated step by step as functionality is added to the application).

The implementation is very straightforward. Instead of @MockMvc a testApplication block is used. This function provides a configured instance of a test application, which is running locally. If nothing different is configured the settings of the productive applications are used (see below).

fun Application.module() {
...
configureRouting()
...
}

More details can be found in the official documentation of Ktor.

The next step will be the configuration of the datasource. Because there is no autoconfiguration available, I need to manually implement it and register it in the startup of the application.

I set up an Hikari datasource and create the necessary tables (if not already exist).

For setup of datasource SpringBoot provides a more easy implementation because it is only necessary to specify the configuration in application.properties, which also can easily be overridden in tests, the rest of the work is done by autoconfiguration.

The next difference in implementation is the table and repository configuration. Compared to JPA there are no annotations available. Instead Exposed provides base classes for different table types. In my case, I can use the IdLongTable classes.

The creation of the address is very simple because no relations are necessary.

The customer table, together with the account table,is a bit more complicated because of the one-to-one relation to addressing (without cascade update/delete) and the one-to-many relation to accounting (with cascade update/delete).

I use the official documentation in GitHub repository for getting the necessary help.

The DAO API of Exposed which should provide a similar way of persisting data comparing to ORM frameworks like Hibernate (according to the documentation). But the configuration of my entities is not working the same way I’m used to do it with Spring Data JPA.

I wrapped all the DAO calls in transaction blocks so the consumer is not forced to do this. Exposed expects a transaction for all the calls on the database (read and write).

Exposed DAO provides base classes for wrapping the access to tables in an entity. The entity base class provides base access methods to database (very basic comparing to CrudRepositories of Spring Data JPA).

The Exposed DAO is also limited in modeling relations. The one-to-many relation between customer and account cannot be handled by the customer (like JPA supports) but needs to be done from the account.

With this limitation I have to create and persist the entities in the same order they need to be added to the database to be compliant to the constraints. Adding a new customer will look like the sample below. This is not very comfortable compared to the Spring Data JPA version.

To encapsulate the data persistence layer of Exposed to be exchangeable I will add an additional layer of abstraction. But now comes the question: How to make Exposed exchangeable without changing the implementation?

In SpringBoot the dependency injection container is already included and it is easy to have multiple versions of one component switchable e.g. by different profiles.

So my next step will be to add a dependency injection framework to the Ktor application. For this, I use Koin which is a lightweight injection library for Kotlin.

In the documentation of Koin, there is a separate chapter about the integration of Koin in a Ktor application.

The configuration which is necessary is very small. Just add the dependencies to build.gradle.kts, setup your dependency map and install the Koin module. The definition of the components which should be managed by Koin needs to be configured manually. There is no annotation that is doing the job for me.

The repository classes just wrap the Exposed functionality and map the entity objects to domain objects.

Testing components is also very straightforward. Koin provides an interface that can be used in order to inject dependencies in test class.

The implementation of the application service layer I will omitt in this article because it works the same way as in SpringBoot and therefore is not interesting for comparing the 2 applications.

The missing part to fulfill the requirements is the addition of a handler, which is providing the functionality to handle the post request. In SpringBoot this part is done by the RestController.

Ktor has a different concept. The definition of endpoints that are handled by the application is defined in a routing configuration. To keep the definition of routes separated from the handling of the HTTP request, I create a controller component that is doing this work.

When running the test I get an exception because the application is not able to deserialize the JSON payload. So I need to add an additional plugin and dependencies to the build.gradle.kts which enables the serialization/deserialization of JSON.

With this step, I’ve reached my goal and the acceptance test is passing successfully.

During the addition of functionality, I need to update my acceptance test setup in order to correctly set up the test application.

The final version looks like the below:

1*jrcNZd71y eLPydbVdZ0jw

My first impression of using Ktor is over. And I need to spend a lot more time to reach the same goal comparing to SpringBoot, but this is mainly related to my experience with SpringBoot.

There are still open topics I need to evaluate:

  • Transaction handling
  • Using annotations for simplifying the handling of components (https://insert-koin.io/docs/reference/koin-annotations/annotations/)
  • Adding request filter functionality (e.g. for request/response logging)
  • More advanced features which SpringBoot provides (e.g. security)
  • Integrations SpringBoot provides (e.g. JMS, Keycloak, Kafka)
  • With Ktor a lot of steps, which SpringBoot is doing under the hood by auto-configuration or component scan need to be done manually.
  • Ktor and the frameworks I used for my application are using less “magic” than Spring Boot so it is more clear what is happening.
  • The application is more lightweight compared to the SpringBoot application.
  • For writing REST applications Ktor can be an option.

For the sample application which is used for this article, check out the GitHub repository:



News Credit

%d bloggers like this: