Monorepositório com Java: organização de código na prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que monorepositório em projetos Java?

Com o crescimento da complexidade de aplicações Java, as equipes frequentemente enfrentam um dilema: armazenar todo o código em um único repositório (monorepositório) ou dividi-lo em vários repositórios de microsserviços (polirepositório). A prática mostra que o monorepositório é especialmente eficaz para projetos com lógica de negócios, bibliotecas e configurações compartilhadas. Em Java, ele permite gerenciar centralizadamente dependências, versões e builds, simplificando refatoração e integração.

Neste artigo, vamos analisar como organizar código Java em um monorepositório na prática: desde a estrutura de pastas até a configuração de CI/CD. Você aprenderá como evitar erros típicos e acelerar o desenvolvimento.



1. Estrutura do monorepositório para Java

O princípio chave do monorepositório é a modularidade. Em Java, isso é implementado através de projetos multimódulo (multi-module project). Cada módulo é responsável por sua área de responsabilidade: bibliotecas compartilhadas, serviços, utilitários.

Exemplo de estrutura típica:

my-java-monorepo/├── build.gradle          # Arquivo de build raiz├── settings.gradle       # Conecta todos os módulos├── gradle.properties     # Versões compartilhadas de dependências├── common-lib/           # Biblioteca compartilhada (DTO, utilitários)│   ├── build.gradle│   └── src/├── service-auth/         # Serviço de autenticação│   ├── build.gradle│   └── src/├── service-payment/      # Serviço de pagamento│   ├── build.gradle│   └── src/└── integration-tests/    # Testes de integração    ├── build.gradle    └── src/

Para gerenciar versões, é conveniente colocar dependências compartilhadas no arquivo raiz build.gradle ou pom.xml (para Maven). No Gradle, isso é feito através do bloco subprojects:

subprojects {    apply plugin: 'java'    group = 'com.pythonlib'    version = '1.0.0'

repositories { mavenCentral() }

dependencies { testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' }}

Essa estrutura permite adicionar facilmente novos serviços sem copiar configurações e garante que todos os módulos usem as mesmas versões de bibliotecas.



2. Gerenciamento de dependências e versões

Um dos principais problemas do monorepositório é o conflito de versões. Em Java, para resolvê-lo, são usados BOM (Bill of Materials) ou descrição centralizada de dependências. Vamos considerar um exemplo no Gradle usando java-platform.

Criaremos um módulo platform que define todas as dependências externas:

// platform/build.gradleplugins {    id 'java-platform'}

javaPlatform { allowDependencies()}

dependencies { constraints { api 'com.fasterxml.jackson.core:jackson-databind:2.15.2' api 'org.springframework.boot:spring-boot-starter-web:3.1.5' api 'org.slf4j:slf4j-api:2.0.9' }}

Agora, em cada serviço, podemos simplesmente referenciar a plataforma:

// service-auth/build.gradledependencies {    implementation platform(project(':platform'))    implementation 'com.fasterxml.jackson.core:jackson-databind'    implementation 'org.springframework.boot:spring-boot-starter-web'}

Isso garante que todos os módulos usem as mesmas versões. Ao atualizar a versão em platform, as alterações são aplicadas automaticamente a todos os serviços.

Dica: para Maven, use <dependencyManagement> no pom.xml raiz. Funciona de forma semelhante.



3. CI/CD para monorepositório

A organização da integração contínua em um monorepositório requer uma abordagem especial. Não é possível compilar todos os módulos a cada commit — isso tornaria o pipeline mais lento. A solução ideal é a compilação incremental com base nas alterações.

Exemplo no GitHub Actions usando paths:

name: Java CIon:  ```                

Blogs

Book Recommendations