Монорепозиторий с Kotlin: организация кода для начинающих

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: что такое монорепозиторий и зачем он нужен Kotlin-разработчику

Монорепозиторий (monorepo) — это подход, при котором код нескольких проектов или модулей хранится в одном репозитории. В контексте Kotlin это особенно удобно: вы можете объединить серверную часть (Ktor), библиотеки, CLI-инструменты и даже мобильные приложения (Kotlin Multiplatform) в единую структуру. Для начинающих разработчиков монорепозиторий упрощает навигацию, тестирование и совместную работу.

В этой статье мы разберем, как организовать монорепозиторий с Kotlin, используя Gradle Multi-Project Build. Вы узнаете, как правильно структурировать папки, настраивать зависимости и избегать типичных ошибок.

Основная часть: создание монорепозитория с Kotlin

1. Структура монорепозитория: базовые принципы

Правильная структура — залог успеха. В монорепозитории Kotlin каждый модуль — это отдельный подпроект Gradle. Вот типичная схема:

my-monorepo/
├── build.gradle.kts          # Корневой файл сборки
├── settings.gradle.kts       # Настройка модулей
├── gradle.properties         # Общие свойства
├── shared/                   # Общая библиотека (модуль)
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── kotlin/
│               └── com/example/shared/
│                   └── Utils.kt
├── backend/                  # Серверный модуль (Ktor)
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── kotlin/
│               └── com/example/backend/
│                   └── Application.kt
└── cli-tool/                 # CLI-инструмент
    ├── build.gradle.kts
    └── src/
        └── main/
            └── kotlin/
                └── com/example/cli/
                    └── Main.kt

Ключевое правило: один модуль — одна ответственность. Не смешивайте бизнес-логику и инфраструктуру в одном модуле. Это упрощает тестирование и переиспользование кода.

2. Настройка Gradle для монорепозитория

Начнем с корневого файла settings.gradle.kts. Он определяет, какие модули входят в проект:

// settings.gradle.kts
rootProject.name = "my-monorepo"

include(":shared")
include(":backend")
include(":cli-tool")

// Опционально: можно задать имена модулей через include()
// include(":libs:utils") — если модуль вложен в папку libs

Теперь корневой build.gradle.kts. Обычно он содержит только общие настройки (плагины, репозитории) и не имеет собственного кода:

// build.gradle.kts (корневой)
plugins {
    kotlin("jvm") version "1.9.22" apply false
    id("application") apply false
}

allprojects {
    repositories {
        mavenCentral()
    }
}

subprojects {
    apply(plugin = "kotlin")
    // Общие настройки для всех подпроектов
    dependencies {
        implementation(kotlin("stdlib"))
    }
}

Обратите внимание на apply false — это предотвращает применение плагина к корневому проекту. Каждый модуль сам решает, какие плагины ему нужны.

3. Пример модуля: общая библиотека (shared)

Модуль shared будет содержать переиспользуемые функции. Его build.gradle.kts:

// shared/build.gradle.kts
plugins {
    kotlin("jvm")
}

dependencies {
    // Внутренних зависимостей нет — это базовый модуль
    testImplementation("org.jetbrains.kotlin:kotlin-test")
}

Сам код в Utils.kt:

// shared/src/main/kotlin/com/example/shared/Utils.kt
package com.example.shared

fun greet(name: String): String = "Hello, $name!"

fun add(a: Int, b: Int): Int = a + b

Теперь этот модуль можно подключить к другим модулям:

// backend/build.gradle.kts
plugins {
    kotlin("jvm")
    id("application")
}

dependencies {
    implementation(project(":shared"))  // Зависимость от модуля shared
    implementation("io.ktor:ktor-server-netty:2.3.7")
}

application {
    mainClass.set("com.example.backend.ApplicationKt")
}

В коде сервера вы сможете использовать com.example.shared.greet() — это и есть сила монорепозитория.

4. Зависимости между модулями: версионирование и common-конфигурация

Часто возникает необходимость вынести общие версии библиотек (например, Ktor, Coroutines) в одно место. Для этого используйте gradle.properties или файл libs.versions.toml (Version Catalog). Рассмотрим простой вариант через gradle.properties:

# gradle.properties
kotlin.version=1.9.22
ktor.version=2.3.7
coroutines.version=1.7.3

Теперь в build.gradle.kts любого модуля можно обращаться к этим версиям:

// backend/build.gradle.kts
dependencies {
    implementation("io.ktor:ktor-server-netty:${property("ktor.version")}")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${property("coroutines.version")}")
}

Более продвинутый способ — использовать Gradle Version Catalog. Создайте файл gradle/libs.versions.toml:

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

[libraries]
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

Тогда в build.gradle.kts можно писать:

dependencies {
    implementation(libs.ktor.server.netty)
}

Это упрощает обновление версий и делает код чище.

5. Советы по организации кода для начинающих

  • Используйте единый стиль именования: папки модулей в нижнем регистре (kebab-case), пакеты — в нижнем (com

Блоги

Рекомендаций по книгам