Tidy up Dropwizard and JDBI with Kotlin

Near the end of 2017, some developers at iZettle got together and started discussing the possibility of enterprise development with Kotlin. My team was starting up a new project and we all thought it would be a good opportunity to try our hands at Kotlin.

Our systems make healthy use of JDBI and Dropwizard, and we didn’t feel like deviating from this pattern too much. You stick with what you know, especially when you’re starting with a new language. We proceeded cautiously.

A Dropwizard app at iZettle basically consists of three things: an application class, a configuration class, and everything else. The application class quickly gets bloated with line after line of instance creations, intertwined with new instances of managers and resources. Each instance is carefully declared after another, creating a precise cascade of instances, one being inserted into another. Among these instances were our JDBI objects, our DAOs (Data Access Object). And they were many.

You may already recognize a problem and even have a solution ready. Sure, we could have introduced Guice, Google’s lightweight dependency injection framework, along with its hodgepodge of adapters for Dropwizard, and tried to make all that work with Kotlin. That didn’t appeal to us. Instead, we decided to keep things simple and embrace the Kotlin way.

Kotlin introduces several new concepts and associated keywords that take some time to wrap your head around. When we discovered inline and reified, we were close to a solution.

The inline keyword tells the Kotlin compiler to literally copy your code into the place where it’s being called from. This allows you to optimize your own code before the compiler does, slightly bending it to your will. Hand-in-hand with inline, we discovered reified, which allows us to access the actual type of a generic type parameter. For something to be reified, it must also be inline.

We created a new class to proxy all interactions with JDBI. We called it DataOperations. At start up, the class scans for any DAOs (defined by being in a specific package and inheriting the Dao interface). We used the Reflections library to do this, but since it doesn’t explicitly support Kotlin yet, we store the Java Class objects it produces.

companion object {
val daoTypes: List<Class<out Dao>> =
Reflections("com.izettle.db", MyApp::class.java.classLoader)
.getSubTypesOf(Dao::class.java).toList()
}

Now that we had a list of the discovered DAO classes, we could use JDBI to instantiate them whenever an instance of DataOperations was created, passing a Jdbi object. This is only done once, from the main Dropwizard application class.

typealias DaoMap = Map<KClass<out Dao>, Dao>
val daos: DaoMap = 
daoTypes.map { it.kotlin to jdbi.onDemand(it) }.toMap()

Then, using inline and reified, we created a way for other classes to access the DAO of their choice. We called it use().

inline fun <reified T : Dao> use() = daos[T::class] as T

This allowed us to use a single instance of DataOperations, declared once in the application class and shared with all the other resources, managers, etc, that needed database access. Thanks to Kotlin and reified, these other classes can access DAOs either by specifying the type desired directly, or by allowing Kotlin to infer the type.

// specify the desired Dao type
val orderDao
= dataOperations.use<OrderDao>()
// infer the Dao type
fun publish(orderDao: OrderDao) = doSomething(orderDao)
publish(
dataOperations.use()
)

We continued to use this pattern to add transactional support to DataOperations. For testing, we added a MockingDataOperations class that creates a mock of each DAO and contains utilities for resetting and programming the mocks.

The switch to using DataOperations has reduced boilerplate code and simplified database access logic throughout the application, pushing knowledge of JDBI down into a single class.

This has just been a first stop on the journey we began when we decided to embrace Kotlin. We are already looking forward to the next stop — the next aha moment.