Monorepositorio con Java: organización de código en la práctica
\nEn el desarrollo moderno de Java, cada vez surge más la cuestión de elegir entre un monorepositorio (monorepo) y múltiples repositorios separados. Un monorepositorio es un enfoque en el que todo el código del proyecto (varios servicios, bibliotecas, configuraciones) se almacena en un único repositorio de Git. En este artículo, analizaremos cómo organizar un monorepositorio con Java en la práctica: desde la estructura de carpetas hasta la configuración de la compilación y CI/CD. Obtendrá ejemplos de código listos y recomendaciones que le ayudarán a evitar errores típicos.
\n\n¿Por qué un monorepositorio para Java?
\nUn monorepositorio es especialmente útil para proyectos de Java donde muchos módulos dependen unos de otros. Las principales ventajas son:
\n- \n
- Versión única de dependencias — todos los módulos usan una misma versión de las bibliotecas, lo que elimina conflictos. \n
- Refactorización simplificada — un cambio de API en un módulo es visible de inmediato en todos los dependientes. \n
- Configuración de compilación compartida — Gradle o Maven se configuran una vez para todos los módulos. \n
- Integridad de CI/CD — un único pipeline para todos los servicios, más fácil de rastrear cambios. \n
Sin embargo, un monorepositorio requiere disciplina: sin una estructura clara, se convierte en un caos. Veamos la organización correcta.
\n\nEstructura de un monorepositorio para un proyecto Java
\nEstructura recomendada para un monorepositorio con Java (usando Gradle como ejemplo):
\nmy-monorepo/\n├── build.gradle.kts # archivo de compilación raíz\n├── settings.gradle.kts # incluye todos los módulos\n├── gradle.properties # propiedades comunes\n├── libs/ # bibliotecas comunes\n│ ├── common-utils/\n│ │ ├── build.gradle.kts\n│ │ └── src/\n│ └── logging/\n│ ├── build.gradle.kts\n│ └── src/\n├── services/ # microservicios\n│ ├── user-service/\n│ │ ├── build.gradle.kts\n│ │ └── src/\n│ └── payment-service/\n│ ├── build.gradle.kts\n│ └── src/\n├── config/ # configuraciones (ej. checkstyle, spotbugs)\n│ └── checkstyle.xml\n└── docker/ # Dockerfile para cada servicio\n ├── user-service.Dockerfile\n └── payment-service.Dockerfile\n\nPrincipios clave:
\n- \n
- Los módulos se dividen en
libs(bibliotecas) yservices(aplicaciones ejecutables). \n - El
build.gradle.ktsraíz define los plugins y versiones comunes. \n - El archivo
settings.gradle.ktsincluye todos los subproyectos. \n
Ejemplo 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\nConfiguración de dependencias entre módulos
\nLa tarea principal de un monorepositorio es gestionar las dependencias internas. Veamos un ejemplo donde user-service depende de common-utils.
Paso 1. En el gradle.properties raíz, declaramos las versiones:
# gradle.properties\nkotlinVersion=1.9.0\nspringBootVersion=3.2.0\ncommonUtilsVersion=1.0.0\n\nPaso 2. En el build.gradle.kts raíz, conectamos los plugins y establecemos configuraciones comunes:
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