Монорепозиторий с Kotlin: организация кода на практике

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

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

Начать курс

Монорепозиторий с Kotlin: организация кода на практике

В современной разработке программного обеспечения всё чаще возникает выбор между монорепозиторием (monorepo) и мультирепозиторием (multirepo). Для проектов на Kotlin, особенно тех, что используют Kotlin Multiplatform или микросервисную архитектуру, монорепозиторий становится мощным инструментом. В этой статье мы разберём, как правильно организовать код в монорепозитории на Kotlin, рассмотрим практические примеры и лучшие практики, которые помогут вам избежать типичных ошибок.

Что такое монорепозиторий и зачем он нужен в Kotlin?

Монорепозиторий — это подход, при котором весь код проекта (или нескольких связанных проектов) хранится в одном репозитории. В контексте Kotlin это может включать:

  • Общие библиотеки — утилиты, модели данных, DTO.
  • Модули приложений — backend (Ktor, Spring), frontend (Kotlin/JS), мобильные приложения (Kotlin Multiplatform).
  • Скрипты и конфигурации — CI/CD, Dockerfile, Gradle-скрипты.

Основные преимущества монорепозитория для Kotlin-проектов:

  • Единая версия зависимостей — все модули используют одну версию Kotlin и библиотек, что снижает риск конфликтов.
  • Упрощённый рефакторинг — изменение API в одном модуле сразу видно во всех зависимых.
  • Легче настраивать CI/CD — один пайплайн для всех компонентов.
  • Прозрачность — вся кодовая база доступна в одном месте.

Однако есть и недостатки: размер репозитория может расти, а время клонирования увеличивается. Но для средних и крупных проектов на Kotlin монорепозиторий часто оказывается оптимальным решением.

Структура монорепозитория: практический шаблон

Правильная организация папок — основа успеха. Рассмотрим типовую структуру для Kotlin-проекта с использованием Gradle:

my-kotlin-monorepo/
├── build.gradle.kts          # Корневой скрипт сборки
├── settings.gradle.kts       # Настройка модулей
├── gradle.properties         # Общие параметры Gradle
├── gradle/
│   └── libs.versions.toml    # Version Catalog (рекомендуется)
├── modules/
│   ├── common/               # Общие утилиты и модели
│   │   ├── build.gradle.kts
│   │   └── src/
│   ├── backend/              # Backend-сервис на Ktor
│   │   ├── build.gradle.kts
│   │   └── src/
│   ├── android-app/          # Android-приложение
│   │   ├── build.gradle.kts
│   │   └── src/
│   └── web-client/           # Kotlin/JS клиент
│       ├── build.gradle.kts
│       └── src/
├── scripts/                  # Вспомогательные скрипты
│   └── deploy.sh
└── docs/                     # Документация
    └── architecture.md

Файл settings.gradle.kts подключает модули:

rootProject.name = "my-kotlin-monorepo"

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

В корневом build.gradle.kts можно задать общие плагины и зависимости:

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
}

Управление зависимостями: Version Catalog и Convention Plugins

В монорепозитории критически важно избегать дублирования версий зависимостей. Для этого в Kotlin используют Version Catalog (файл libs.versions.toml).

Пример 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.ktor:ktor-server-core", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }

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

Теперь в модуле backend можно использовать:

dependencies {
    implementation(libs.ktor.server.core)
    implementation(libs.ktor.server.netty)
    implementation(libs.kotlinx.coroutines.core)
}

Для сложных проектов полезно создать Convention Plugins — Gradle-плагины, которые настраивают общие конфигурации (например, компиляцию, тестирование, линтинг). Это позволяет избежать дублирования кода в каждом модуле.

Пример convention-плагина для JVM-модуля:

// buildSrc/src/main/kotlin/jvm-common.gradle.kts
plugins {
    kotlin("jvm")
    kotlin("plugin.serialization")
}

kotlin {
    jvmToolchain(17)
}

dependencies {
    implementation(libs.kotlin.stdlib)
    testImplementation("org.jetbrains.kotlin:kotlin-test")
}

Тогда в backend/build.gradle.kts достаточно написать:

plugins {
    id("jvm-common")
}

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

Работа с Gradle и CI/CD в монорепозитории

Одна из главных задач — настроить сборку так, чтобы изменения в одном модуле не пересобирали весь проект. Для этого используют Gradle Build Cache и инкрементальную компиляцию.

Пример оптимизированного CI-пайплайна (GitHub Actions):

name: Build Monorepo
on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions                

Блоги

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