Introdução: o que é um monorepositório e por que ele é necessário em Java
No mundo do desenvolvimento Java, existem duas abordagens principais para armazenar código: monorepositório (monorepo) e multirrepositório (multirepo). Monorepositório é uma estratégia onde todas as bases de código do projeto (bibliotecas, serviços, utilitários, configurações) são armazenadas em um único repositório. Para desenvolvedores Java que trabalham com grandes sistemas corporativos ou microsserviços, o monorepositório está se tornando uma solução cada vez mais popular.
As vantagens do monorepositório em Java são óbvias:
- Versão única de dependências: Todos os módulos usam uma única versão de bibliotecas, eliminando conflitos (classpath hell).
- Refatoração simplificada: A alteração de uma API em um módulo é imediatamente visível em todos os módulos dependentes.
- Histórico de alterações transversal: Um único commit pode conter alterações em vários módulos, simplificando a depuração.
- Padronização: Regras unificadas de code style, linting e CI/CD para todo o projeto.
No entanto, o monorepositório exige uma organização cuidadosa. Neste artigo, vamos analisar como estruturar corretamente um projeto Java em um monorepositório usando Maven e Gradle.
Parte principal: organização do monorepositório em Java
1. Estrutura de diretórios: abordagem modular
A base do monorepositório é a estrutura modular. Cada módulo representa uma unidade independente (biblioteca, serviço, utilitário). Vamos considerar uma estrutura típica para um projeto Java:
my-monorepo/├── build.gradle # Arquivo de build raiz (Gradle)├── settings.gradle # Lista de módulos├── gradle.properties # Configurações comuns├── gradle/│ └── wrapper/├── common/ # Bibliotecas comuns│ ├── common-utils/ # Utilitários (strings, datas)│ └── common-dto/ # DTOs para troca de dados├── services/ # Microsserviços│ ├── user-service/ # Serviço de usuários│ └── order-service/ # Serviço de pedidos├── libs/ # Bibliotecas internas│ └── logging-lib/ # Biblioteca de logging├── tests/ # Testes de integração/fim a fim│ └── e2e-tests/└── buildSrc/ # Plugins e configurações comunsCada módulo internamente possui a estrutura padrão do Maven/Gradle:
user-service/├── build.gradle├── src/│ ├── main/│ │ ├── java/│ │ │ └── com/example/user/│ │ │ ├── controller/│ │ │ ├── service/│ │ │ └── repository/│ │ └── resources/│ │ └── application.yml│ └── test/│ └── java/└── Dockerfile2. Gerenciamento de dependências: Maven BOM e Gradle Version Catalog
O principal problema do monorepositório é a sincronização das versões das dependências. Em Java, isso é resolvido de duas maneiras.
Opção 1: Maven BOM (Bill of Materials)
Cria-se um POM raiz que declara todas as versões das dependências. Os módulos filhos usam dependencyManagement:
<!-- pom.xml (raiz) --><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.2.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.example</groupId> <artifactId>common-utils</artifactId> <version>1.0.0</version> </dependency> </dependencies></dependencyManagement>Opção 2: Gradle Version Catalog
No arquivo gradle/libs.versi