Introduction to Monorepo with Kotlin
Modern development of complex applications often faces the problem of code management. The choice between monorepo and multi-repo is one of the key architectural decisions. In this article, we will explore how to organize a monorepo with Kotlin using best practices and modern tools.
A monorepo is an approach where all projects, libraries, and services are stored in a single repository. For Kotlin developers, this is especially relevant due to the powerful Gradle build system and the ability to share common code between different modules.
Advantages of Monorepo for Kotlin Projects
Before moving on to organization, let's consider the key advantages of a monorepo for Kotlin:
- Single dependency version — all modules use the same version of Kotlin, libraries, and plugins, eliminating version conflicts.
- Simplified refactoring — changes in a shared library are automatically checked by all dependent modules.
- Centralized configuration — common Gradle settings, linters, and CI/CD are configured once.
- Atomic commits — related changes in different modules can be made in a single commit.
- Code reuse — common utilities, DTOs, and business logic are easily accessible to all projects.
Monorepo Structure with Kotlin and Gradle
Let's look at the optimal directory structure for a Kotlin monorepo using Gradle Multi-Module:
my-monorepo/├── build.gradle.kts # Root build file├── settings.gradle.kts # Module configuration├── gradle.properties # Common properties├── gradle/│ └── libs.versions.toml # Version Catalog├── modules/│ ├── common/ # Common utilities and models│ ├── domain/ # Business logic│ ├── data/ # Data layer (API, DB)│ ├── service-a/ # Microservice A│ ├── service-b/ # Microservice B│ └── shared-test/ # Shared test utilities├── apps/│ ├── backend/ # Main application│ └── cli-tool/ # CLI tool└── config/ ├── checkstyle/ # Linter configuration └── docker/ # Dockerfile for servicesConfiguring settings.gradle.kts
The settings.gradle.kts file defines all project modules:
rootProject.name = "my-monorepo"
// Include all modulesinclude( ":modules:common", ":modules:domain", ":modules:data", ":modules:service-a", ":modules:service-b", ":apps:backend", ":apps:cli-tool")
// Plugin management for version controlpluginManagement { repositories { gradlePluginPortal() mavenCentral() }}Using Version Catalog (libs.versions.toml)
For centralized dependency management, we use gradle/libs.versions.toml:
[versions]kotlin = "1.9.22"coroutines = "1.7.3"ktor = "2.3.7"exposed = "0.44.1"
[libraries]kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
[plugins]kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }Example build.gradle.kts for the common module
plugins { alias(libs.plugins.kotlin.jvm)}
depend