Introdução: por que um monorepositório em Kotlin?
\nNo desenvolvimento moderno com Kotlin, surge frequentemente um dilema: armazenar vários projetos relacionados (backend, microsserviços, bibliotecas, aplicativos Android) em repositórios separados (polirepositório) ou combiná-los em um único monorepositório. Um monorepositório é uma estratégia de armazenamento de código onde todos os componentes estão em um único sistema de controle de versão. Para projetos Kotlin, isso é especialmente relevante devido ao poderoso sistema de build Gradle e ao suporte nativo a multimódulos.
\nNeste artigo, abordaremos os aspectos práticos da organização de um monorepositório com Kotlin: desde a estrutura de diretórios até a configuração de CI/CD e gerenciamento de dependências. Você aprenderá como evitar erros comuns e obter o máximo benefício de um repositório de código unificado.
\n\n1. Estrutura do monorepositório: da teoria à prática
\nUma estrutura correta é a base de um monorepositório bem-sucedido. Vamos considerar um projeto típico que inclui uma API REST com Ktor, uma biblioteca para trabalhar com banco de dados e um aplicativo Android. Aqui está a hierarquia recomendada:
\nmy-monorepo/\n├── build.gradle.kts # Arquivo de build raiz\n├── settings.gradle.kts # Configuração dos módulos\n├── gradle.properties # Propriedades comuns\n├── gradle/\n│ └── libs.versions.toml # Catálogo de versões (Version Catalog)\n├── libs/ # Bibliotecas compartilhadas\n│ ├── common-utils/ # Utilitários, extensões\n│ ├── common-db/ # Trabalho com BD (Exposed)\n│ └── common-dto/ # Data Transfer Objects\n├── services/ # Aplicações servidoras\n│ ├── user-service/ # Microsserviço de usuários\n│ └── order-service/ # Microsserviço de pedidos\n└── apps/ # Aplicações cliente\n └── android-app/ # Aplicativo Android\n\nO elemento-chave é o settings.gradle.kts. Ele inclui todos os módulos:
rootProject.name = "my-monorepo"\n\ninclude("libs:common-utils")\ninclude("libs:common-db")\ninclude("libs:common-dto")\ninclude("services:user-service")\ninclude("services:order-service")\ninclude("apps:android-app")\n\nEssa estrutura permite reutilizar facilmente o código entre serviços e cliente, além de gerenciar centralizadamente as versões das dependências.
\n\n1.1. Version Catalog: gerenciamento de dependências
\nEm um monorepositório, é fundamental evitar a duplicação de versões de bibliotecas. Use o Version Catalog (arquivo gradle/libs.versions.toml):
[versions]\nkotlin = "2.0.21"\nktor = "3.0.3"\nexposed = "0.57.0"\n\n[libraries]\nktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }\nexposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }\n\n[plugins]\nkotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }\n\nAgora, em qualquer módulo, você pode escrever:
\ndependencies {\n implementation(libs.ktor.server.core)\n implementation(libs.exposed.core)\n}\n\nIsso garante que todos os módulos usem as mesmas versões e simplifica a atualização em massa.
\n\n2. Organização prática dos módulos: Gradle Multi-Project
\nCada módulo do monorepositório é um subprojeto Gradle separado com seu próprio build.gradle.kts. Vamos considerar a configuração da biblioteca common-utils:
// libs/common-utils/build.gradle.kts\nplugins {\n alias(libs.plugins.kotlin.jvm)\n}\n\ngroup = "com.mycompany.common"\nversion = "1.0.0"\n\ndependencies {\n implementation(kotlin("stdlib"))\n implementation(libs.kotlinx.coroutines)\n}\n\nE aqui está um microsserviço típico com Ktor:
\n// services/user-service/build.gradle.kts\nplugins {\n alias(libs.plugins.kotlin.jvm)\n alias(libs.plugins.ktor)\n}\n\ndependencies {\n implementation(project(":libs:common-utils"))\n implementation(project(":libs:common-dto"))\n impl