Introduction: What is a Monorepository and Why Does a Kotlin Developer Need It
A monorepository (monorepo) is an approach where the code of several projects or modules is stored in a single repository. In the context of Kotlin, this is especially convenient: you can combine a server-side part (Ktor), libraries, CLI tools, and even mobile applications (Kotlin Multiplatform) into a single structure. For beginner developers, a monorepository simplifies navigation, testing, and collaboration.
In this article, we will break down how to organize a monorepository with Kotlin using Gradle Multi-Project Build. You will learn how to properly structure folders, configure dependencies, and avoid common mistakes.
Main Part: Creating a Monorepository with Kotlin
1. Monorepository Structure: Basic Principles
A proper structure is the key to success. In a Kotlin monorepository, each module is a separate Gradle subproject. Here is a typical scheme:
my-monorepo/├── build.gradle.kts # Root build file├── settings.gradle.kts # Module configuration├── gradle.properties # Common properties├── shared/ # Shared library (module)│ ├── build.gradle.kts│ └── src/│ └── main/│ └── kotlin/│ └── com/example/shared/│ └── Utils.kt├── backend/ # Server module (Ktor)│ ├── build.gradle.kts│ └── src/│ └── main/│ └── kotlin/│ └── com/example/backend/│ └── Application.kt└── cli-tool/ # CLI tool ├── build.gradle.kts └── src/ └── main/ └── kotlin/ └── com/example/cli/ └── Main.ktKey rule: one module — one responsibility. Do not mix business logic and infrastructure in one module. This simplifies testing and code reuse.
2. Configuring Gradle for a Monorepository
Let's start with the root file settings.gradle.kts. It defines which modules are included in the project:
// settings.gradle.ktsrootProject.name = "my-monorepo"
include(":shared")include(":backend")include(":cli-tool")
// Optional: you can set module names via include()// include(":libs:utils") — if the module is nested in the libs folderNow the root build.gradle.kts. It usually contains only common settings (plugins, repositories) and has no code of its own:
// build.gradle.kts (root)plugins { kotlin("jvm") version "1.9.22" apply false id("application") apply false}
allprojects { repositories { mavenCentral() }}
subprojects { apply(plugin = "kotlin") // Common settings for all subprojects dependencies { implementation(kotlin("stdlib")) }}Note the apply false — this prevents applying the plugin to the root project. Each module decides which plugins it needs.
3. Module Example: Shared Library (shared)
The shared module will contain reusable functions. Its build.gradle.kts:
// shared/build.gradle.ktsplugins { kotlin("jvm")}
dependencies { // No internal dependencies — this is a base module testImplementation("org.jetbrains.kotlin:kotlin-test")}The code itself in Utils.kt:
// shared/src/main/kotlin/com/example/shared/Utils.ktpackage com.example.shared
fun greet(name: String): String = "Hello, $name!"
fun add(a: Int, b: Int): Int = a + bNow this module can be connected to other modules:
// backend/build.gradle.ktsplugins { kotlin("jvm") id("application")}
dependencies { implementation(project(