From Dagger to Koin, a step by step migration guide

In my last public talks, I see that many people appreciate the simplicity of Koin and like the way it helps them to design their applications with ease. I’m clearly happy to see good feedback from this first stable version of Koin. But one question often comes up: how can I easily migrate my Dagger app to Koin? 🧐

Sorry, there is no IntelliJ plugin for that. But a small example is always a good occasion to understand things. Then, I propose you to migrate the well known Dagger’s thermosiphon app sample to Koin, in a step by step approach. This will help you understand how you can use Koin with another DI framework, and migrate all your app to Koin in a progressive manner.

All the sources are available on Github 👉

The application ☕️

This big picture of this app is to demo dependency injection with a few components. This app is a “coffee maker” app… it will “make the coffee”. Here is the components:

  • The Heater handles the heating part with on() off() isHeating()functions, with the ElectricHeater implementation. It prints the heating step on console.
  • The Pump handles the water pumping actions with the pump() function. The thermosiphon implementation will only pump hot water. It prints the pumping step on console.
  • The CoffeeMaker class run the coffee making process with brew() function. It needs a Pump and a Heater to make it. This last is lazy, as we will create it when we only need it. It prints the final coffee step on console.

Once the coffee making process is done, we have this ascii-art:

~ ~ ~ heating ~ ~ ~
=> => pumping => =>
[_]P coffee! [_]P

Photo from unsplash — credit @rawpixel

The components 📦

Below are the app details written in pure Kotlin:

  • The CoffeeMaker — constructor with Heater & Pump. Lazy type is from Kotlin. We use a backing property to not have to write heater.value every time we need it
  • The Heater & its ElectricHeater implementation
  • The Pump & Thermosiphon implementation — constructor with Heater

We have a small app with all the ingredients to make it ready to be assembled:

  • components filled by constructor (Kotlin property constructors)
  • components used by their abstractions (interfaces)

Now we need a DI technology to assemble & run it 👍

Assembling it with Dagger

To make it with Dagger, we will need to use modules and a bunch of annotations to declare everything. Check the Dagger version here and below the details:

  • A DripCoffeeModule class to declare our Heater component & link it with PumpModule class
  • The PumpModule class to declare our Pump component. Note that here we have to annotate the Thermosiphon class constructor with @Inject
  • The CoffeeMaker class constructor is annotated with @Inject and we need a CoffeeApp module to build it with the DripCoffeeModule (The Lazy type below is the one from Dagger)
  • To run it, we have to make code generation to make our code compile(DaggerCoffeeApp.Builder().build() ) and run it

Assembling it with Koin

To make it with Dagger, we will need to use only one module file and one KoinComponent class. You can check the Koin version here (we don’t have any impact on the original Kotlin files). Below are the details the Koin version:

  • we need a small class to bootstrap the CoffeeMaker class (just tag it with KoinComponent and unlock the inject() delegate function):
  • we have to start Koin & we can run the CoffeeApp class directly

It’s pretty simple and direct to make it with Koin. DSL is clearly readable even by people who don’t use Koin. And KoinComponent interface allows an easy access to Koin features.

A step by step migration to Koin 🚀

This sample app is small enough to be completely rewritten with Koin in one time (remove everything from Dagger and declare things for Koin). But it’s not the case for every app to go in a big bang style. We have here a good candidate to make a Dagger to Koin migration step by step.

The idea of step by step migration is to allow your app to run with instances from both Dagger factories and Koin container. How to do that?

The key of this migration is the use of KoinComponent interface to mark a class as allowed to pickup dependencies directly from the Koin container. With this, we will be able to request dependencies from Koin inside a Dagger component.

Our work will be to declare components one by one in the Koin module, and then unplug it from Dagger’s world. Let’s go 👍

Migrating the Heater component ✨

We begin we the lowest component of the hierarchy, as it should be one of the easiest components to migrate.

Our first step consists in unplugging everything around Heater & ElectricHeater, and declare it in Koin. The Thermosiphon& CoffeeMaker classes will make the bridge between Dagger & Koin instances. To make it:

  • Remove DripCoffeeModule class
  • Begin the CoffeeAppModule file to write our Koin module and declare our first dependency: the Heater component
  • Tag Thermosiphon & CoffeeMaker classes with KoinComponent interface to lazy inject Heater into a property, instead of getting it from its constructor
  • Update the CoffeeApp module to use PumpModule module
  • Update the main function to start Koin container

Migrating the Pump component ✨

Let’s migrate the Pump & Thermosiphon components into Koin. We have to unplug every thing around it & lazy inject it into CoffeeMaker class. This last will make the bridge between Dagger & Koin.

  • in Thermosiphon class remove constructor annotation, KoinComponent interface and make Heater injected by constructor
  • Declare the Pump component into Koin module
  • Remove PumpModule class
  • Update the CoffeeMaker class to inject Pump component as lazy property
  • Update the CoffeeApp module to not use any other Dagger module

Migrating the CoffeeMaker component ✨

Final round! Let’s unplug everything from Dagger (you can even comment daggers dependencies from your Gradle file).

  • Make CoffeeMakerclass injected by its constructor and use the Kotlin lazy type
  • Update the Koin module to add CoffeeMaker definition
  • Replace CoffeeApp module by a simple class. Tag it by KoinComponent. Just lazy inject CoffeeMaker into maker property
  • Update the main function, to run CoffeeApp class and use its maker property.

That’s it 🌈

The app is now completely managed by Koin. The Github repository has 1 commit per step, allowing to understand each phase of migration.

Any feedback? Need more help? Just go on 👍

Software Engineer — Tech Speaker & Writer — Kotlin Google Dev Expert — Open Source Maker