Monorepositório com Java: organização de código na prática
\nNo desenvolvimento Java moderno, a questão da escolha entre um monorepositório (monorepo) e vários repositórios separados surge cada vez mais. Monorepositório é uma abordagem onde todo o código do projeto (vários serviços, bibliotecas, configurações) é armazenado em um único repositório Git. Neste artigo, vamos analisar como organizar um monorepositório com Java na prática: desde a estrutura de pastas até a configuração de build e CI/CD. Você obterá exemplos de código prontos e recomendações que ajudarão a evitar erros típicos.
\n\nPor que um monorepositório para Java?
\nO monorepositório é especialmente útil para projetos Java onde muitos módulos dependem uns dos outros. Principais vantagens:
\n- \n
- Versão única de dependências — todos os módulos usam a mesma versão das bibliotecas, eliminando conflitos. \n
- Refatoração simplificada — a alteração de uma API em um módulo é imediatamente visível em todos os dependentes. \n
- Configuração de build compartilhada — Gradle ou Maven são configurados uma vez para todos os módulos. \n
- Integridade do CI/CD — um único pipeline para todos os serviços, facilitando o rastreamento de alterações. \n
No entanto, o monorepositório exige disciplina: sem uma estrutura clara, ele se transforma em caos. Vamos considerar a organização correta.
\n\nEstrutura do monorepositório para um projeto Java
\nEstrutura recomendada para um monorepositório com Java (exemplo com Gradle):
\nmy-monorepo/\n├── build.gradle.kts # arquivo de build raiz\n├── settings.gradle.kts # inclui todos os módulos\n├── gradle.properties # propriedades comuns\n├── libs/ # bibliotecas comuns\n│ ├── common-utils/\n│ │ ├── build.gradle.kts\n│ │ └── src/\n│ └── logging/\n│ ├── build.gradle.kts\n│ └── src/\n├── services/ # microsserviços\n│ ├── user-service/\n│ │ ├── build.gradle.kts\n│ │ └── src/\n│ └── payment-service/\n│ ├── build.gradle.kts\n│ └── src/\n├── config/ # configurações (ex.: checkstyle, spotbugs)\n│ └── checkstyle.xml\n└── docker/ # Dockerfile para cada serviço\n ├── user-service.Dockerfile\n └── payment-service.Dockerfile\n\nPrincípios-chave:
\n- \n
- Os módulos são divididos em
libs(bibliotecas) eservices(aplicações executáveis). \n - O
build.gradle.ktsraiz define os plugins e versões comuns. \n - O arquivo
settings.gradle.ktsinclui todos os subprojetos. \n
Exemplo de settings.gradle.kts:
rootProject.name = "my-monorepo"\n\ninclude(\n "libs:common-utils",\n "libs:logging",\n "services:user-service",\n "services:payment-service"\n)\n\n\nConfiguração de dependências entre módulos
\nA principal tarefa de um monorepositório é gerenciar dependências internas. Vamos considerar um exemplo onde user-service depende de common-utils.
Passo 1. No gradle.properties raiz, declaramos as versões:
# gradle.properties\nkotlinVersion=1.9.0\nspringBootVersion=3.2.0\ncommonUtilsVersion=1.0.0\n\nPasso 2. No build.gradle.kts raiz, conectamos os plugins e definimos as configurações comuns:
plugins {\n id("java-library") apply false\n id("org.springframework.boot") version "3.2.0" apply false\n id("io.spring.dependency-management") version "1.1.4" apply false\n}\n\nallprojects {\n group = "com.example"\n version = "1.0.0"\n\n repositories {\n mavenCentral()\n }\n}\n\nsubprojects {\n apply(plugin = "java-library")\n\n java {\n sourceCompatibility = JavaVersion.VERSION_17\n targetCompatibility = JavaVersion.VERSION_17\n }\n\n dependencies {\n implementation("org.slf4j:slf4j-api:2.0.9")\n testImplementation("org.j