Architecture Proposal to Build Servers With Ktor

Jorge R.
The Startup
Published in
4 min readJul 28, 2020
Image by Pixabay

After attending KotlinConf 2018 and inspired by Ryan Harter talk there (check it here), I've started working with Ktor to develop a backend to connect my apps. I have to say I'm really impressed how easy it was to develop a REST API, including JWT authentication, database connection and Docker deployment with Docker Compose.

Before we start, I would like to warn everyone about the aim of this article. There are several publications about how to code using Ktor, and the official documentation and community behind it are amazing, so I won't spend too much time explaining about how to get things done. This article will be focused on the next step, when you have something working but you want a clean and testable code. This is the architecture I'm using in a real life & production environment server to provide a REST API to my app MySecretSanta.

December 2020 update: I will be presenting this article in a new Kotlin London Meetup in January 13th, 2021. If you want to know more or ask some questions, you can join me there ;)

https://www.youtube.com/watch?v=XhijgrEG1tI

Architecture diagram

Suggested Ktor server's architecture

At the time of writing this article, Ktor server can only be run in JVM, so we will start with a Java main function placed in Application.kt file.

This main function will be used to recover HOCON environment configuration, start embedded server with Netty, setup Koin dependency injector with our dependencies and calling the main module function. Let's call this part the project setup.

Inside Module.kt file we will configure our Ktor server instance. This should be an Application extension function to be able to apply all the configurations. I consider database initialisation as part of server configuration, this is why I call databaseProvider.init() method here. As you can see in the following snippet, all Ktor features and interceptors are installed in this file. Status pages and route modules (registrationModule & userModule) are defined in different files.

registrationModule and userModule are the equivalent of ModuleA and ModuleB in our architecture diagram. We are getting into the interesting part.

Route definition

We have left the configuration part and now we need to start defining routes. This is what registrationModule and userModule are doing, they will install routes to our root routing node of our application. As you can see in the following snippet, all routing modules will be a Routing extension function to be able to install new routes. Each of them will have a Koin injected controller instance, that will be responsible of doing the business logic in this module. This means this layer will only be responsible of defining routes, receive arguments and delegate logic to controllers.

Business logic (Controllers)

All business logic should be defined in a Controller. Following this advice you should be able to unit test all your business logic without worrying about any Ktor dependency. Controllers will use Koin injected API layer objects to fetch and store data. Controllers and API's should always implement an interface, in order to allow unit testing and disconnect other layers. Also, Controller's interfaces will help on tracking changes in your exposed API.

You can feel free to change the name of this layer, as there are other architectures that will use the same name in other contexts.

API layer

APIs will provide a clear interface to recover and store data in our server. They should implement an interface and, unlike DAOs, they won't talk directly to database and may need to do some data transformation.

DAO layer

DAO objects will define each table and functions to access them. In my current implementation I'm using Exposed with SQL DSL, so each DAO object will be one table with CRUD functions.

If you like what is written here and you want to know more, you can find the full code example here:

Have fun and happy "clean" coding!

Update: I will update Ktor dependencies and I will also merge extra features into master, so some of the explanations in this article may refer to this state of the code:

https://github.com/mathias21/KtorEasy/tree/simpleCompose

--

--