Monorepo with Kotlin: Code Organization in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Monorepo with Kotlin: Code Organization in Practice



In modern software development, the choice between a monorepo and a multirepo is increasingly common. For Kotlin projects, especially those using Kotlin Multiplatform or a microservice architecture, a monorepo becomes a powerful tool. In this article, we will break down how to properly organize code in a Kotlin monorepo, look at practical examples and best practices that will help you avoid typical mistakes.



What is a monorepo and why is it needed in Kotlin?



A monorepo is an approach where all the code of a project (or several related projects) is stored in a single repository. In the context of Kotlin, this can include:

  • Shared libraries — utilities, data models, DTOs.
  • Application modules — backend (Ktor, Spring), frontend (Kotlin/JS), mobile applications (Kotlin Multiplatform).
  • Scripts and configurations — CI/CD, Dockerfile, Gradle scripts.


The main advantages of a monorepo for Kotlin projects:

  • Single version of dependencies — all modules use the same version of Kotlin and libraries, reducing the risk of conflicts.
  • Simplified refactoring — changing an API in one module is immediately visible in all dependent modules.
  • Easier CI/CD setup — one pipeline for all components.
  • Transparency — the entire codebase is available in one place.


However, there are also disadvantages: the repository size can grow, and cloning time increases. But for medium and large Kotlin projects, a monorepo is often the optimal solution.



Monorepo structure: a practical template



Proper folder organization is the foundation of success. Let's look at a typical structure for a Kotlin project using Gradle:



my-kotlin-monorepo/├── build.gradle.kts          # Root build script├── settings.gradle.kts       # Module configuration├── gradle.properties         # Common Gradle parameters├── gradle/│   └── libs.versions.toml    # Version Catalog (recommended)├── modules/│   ├── common/               # Shared utilities and models│   │   ├── build.gradle.kts│   │   └── src/│   ├── backend/              # Backend service on Ktor│   │   ├── build.gradle.kts│   │   └── src/│   ├── android-app/          # Android application│   │   ├── build.gradle.kts│   │   └── src/│   └── web-client/           # Kotlin/JS client│       ├── build.gradle.kts│       └── src/├── scripts/                  # Helper scripts│   └── deploy.sh└── docs/                     # Documentation    └── architecture.md


The settings.gradle.kts file includes the modules:



rootProject.name = "my-kotlin-monorepo"

include(":modules:common")include(":modules:backend")include(":modules:android-app")include(":modules:web-client")


In the root build.gradle.kts, you can define common plugins and dependencies:



plugins {    kotlin("jvm") version "1.9.22" apply false    kotlin("multiplatform") version "1.9.22" apply false    id("com.android.application") version "8.2.0" apply false}


Dependency management: Version Catalog and Convention Plugins



In a monorepo, it is critically important to avoid duplicating dependency versions. For this, Kotlin uses the Version Catalog (the libs.versions.toml file).



Example of gradle/libs.versions.toml:



[versions]kotlin = "1.9.22"ktor = "2.3.7"coroutines = "1.8.0"

[libraries]kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }ktor-server-core = { module = "io.k

Blogs

Book Recommendations