How to create a Kotlin Multi-Module Project
Tags: springboot, kotlin, architecture
Published: 2023-11-17 12:18:27

**Spring Boot and Kotlin are a powerful combination** for creating modern web applications. For Kotlin, we usually use Gradle with the Kotlin DSL developed for it. How can we turn such a project into a multi-module project?

Multi-module projects allow a monolith to be broken down into distinct modules without the need for the complex infrastructure required for microservices. The modules are **not only separate folders similar to packages, but also form a dependency graph** - thus circular connections are not possible. In the end, each module is compiled into a jar, and all jar's together form the executable Spring Boot fat jar.

The correct partitioning of the modules is crucial for an architecture that supports maintainability of the app and collaboration within the team in the long run. A few tips on this can be found in [best practices for multi-module projects](https://bootify.io/multi-module/best-practices-for-spring-boot-multi-module.html).

## Splitting the classes and resources into the modules

We can create a simple Kotlin project in the current Spring Boot version `3.1.5` directly in the Bootify Builder. With [open project](https://bootify.io/) we launch a project and select Kotlin as language. Gradle is already preset, and the database we can set to None for now. With this we can already explore and download the source code.

![](https://s3-eu-central-1.amazonaws.com/bootify-prod/ext/img/multiModule/kotlinProjectGradle.png "Initial version of the build.gradle.kts")
_&nbsp; Initial version of the build.gradle.kts_

Before we customize the build file, we first prepare the two modules `kotlin-web` and `kotlin-base` and **create two folders with exactly these names**. For the web module we move only the `HomeController` to the new path `./kotlin-web/src/main/kotlin/io/bootify/kotlin/controller`. All other classes and resources are relevant for the whole application, so we move the whole `src` folder to `./kotlin-base`.

```gradle
include 'kotlin-base', 'kotlin-web'
```
_&nbsp; Adding the new settings.gradle file_

We also need a new file that provides a list of all our modules to Gradle. With this, the only thing missing at last is the customization of the `build.gradle.kts`.

## Customizing the build file

First we prepare the plugins. The version of Kotlin should always be derived from the Spring Boot version, **as this ensures the highest possible compatibility** - the newest Kotlin version is not tested with Spring Boot yet. The `allprojects` part specifies a few global settings.

```gradle
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.springframework.boot.gradle.dsl.SpringBootExtension
import org.springframework.boot.gradle.tasks.run.BootRun

buildscript {
    repositories {
        mavenCentral()
    }
}

plugins {
    id("org.springframework.boot") version "3.1.5" apply false
    id("io.spring.dependency-management") version "1.1.3" apply false

    kotlin("jvm") version "1.8.22" apply false
    kotlin("plugin.spring") version "1.8.22" apply false
}

allprojects {
    tasks.withType<JavaCompile> {
        sourceCompatibility = "17"
        targetCompatibility = "17"
    }

    tasks.withType<KotlinCompile> {
        kotlinOptions {
            freeCompilerArgs = listOf("-Xjsr305=strict")
            jvmTarget = "17"
        }
    }
}
```
_&nbsp; First part of our extended build file_

In the following, **the prepared plugins are applied to all submodules**. Some settings like the location of the Spring Boot BOM and the main class are specified as well.  

```gradle
subprojects {
    apply {
        plugin("java-library")
        plugin("io.spring.dependency-management")
        plugin("org.jetbrains.kotlin.jvm")
        plugin("org.jetbrains.kotlin.plugin.spring")
        plugin("org.springframework.boot")
    }

    tasks.withType<Test> {
        useJUnitPlatform()
    }

    repositories {
        mavenCentral()
    }

    the<DependencyManagementExtension>().apply {
        imports {
            mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
        }
    }

    extra.apply {
        set("projectName", "${extra.get("rootProjectName")}-$project.name")
    }

    configure<SpringBootExtension> {
        mainClass.set("io.bootify.kotlin.base.KotlinApplicationKt")
    }

    tasks.getByName<BootRun>("bootRun") {
        enabled = false
    }
}
```
_&nbsp; Second part of our build file_

In the last part **we configure our actual modules**. Here `kotlin-base` is referenced by `kotlin-web`. By integrating further depedencies as `api` in `kotlin-base` they are visible in `kotlin-web` as well. With `implementation` a depedency would be "invisible" within `kotlin-web` during development, even if eventually all dependencies end up together in the fat jar.

```gradle
project(":kotlin-web") {
    apply {
        plugin("application")
    }

    tasks.getByName<BootRun>("bootRun") {
        environment.put("SPRING_PROFILES_ACTIVE", environment.get("SPRING_PROFILES_ACTIVE") ?: "local")
        workingDir = rootProject.projectDir
        enabled = true
    }

    dependencies {
        val api by configurations

        api(project(":kotlin-base"))
    }
}

project(":kotlin-base") {
    dependencies {
        val api by configurations

        api("org.springframework.boot:spring-boot-starter-web")
        api("org.springframework.boot:spring-boot-starter-validation")
        api("org.jetbrains.kotlin:kotlin-reflect")
        api("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    }
}
```
_&nbsp; Last part of our build file_

The `bootRun` task is only enabled for our `kotlin-web` module and uses the root module as a working directory. This way all required files like `docker-compose.yml` can be picked up from there. 

With this **we have already transformed our project into a multi-module project!** With `gradlew clean build` we can build our application as usual - which will compile and test each modules one after the other according to their dependency path.

Bootify offers an option in the Professional plan to create a multi-module project. **This creates the structure described here**, matching the other settings selected. In addition, integration tests are available and custom modules can be added, which are then already integrated into the build process.

[**&raquo; See Features and Pricing**](https://bootify.io/pricing.html)