CodeGym /Cursos /Java Server /Version Catalog: libs.versions.toml

Version Catalog: libs.versions.toml

Java Server
Nivel 5, Lección 3
Disponible

1. ¿Por qué hace falta un catálogo de versiones?

Imagina que tienes un proyecto con diez dependencias. Las versiones de Spring Boot, Lombok, MapStruct, Testcontainers y otras bibliotecas están esparcidas por el archivo build.gradle.kts. Al principio todo parece bien: «son solo líneas de texto, las encuentro con un Ctrl+F». Pero cuando el proyecto crece, empieza la pesadilla: la misma versión se repite en varios módulos, hay que acordarse de actualizar todos los lugares a la vez, y encontrar dónde exactamente se declaró «aquella» versión puede costar minutos.

La solución clásica era el bloque ext en build.gradle.kts:

ext {
    set("springBootVersion", "3.4.5")
    set("lombokVersion", "1.18.36")
}

Funciona, pero tiene sus inconvenientes: es solo código Kotlin/Groovy, sin estructura formal. El IDE no siempre lo entiende, no hay autocompletado para los alias, y si el proyecto es multi-módulo, el bloque ext hay que repetir o compartir mediante un script aparte.

A partir de Gradle 7 apareció el Version Catalog — una forma estandarizada de describir dependencias y sus versiones en un único archivo TOML. Es la solución oficial al problema de la dispersión de versiones.

2. Estructura del archivo libs.versions.toml

El Version Catalog vive en el archivo gradle/libs.versions.toml. La extensión .toml no es casual: TOML (Tom's Obvious, Minimal Language) es un formato de configuración legible por humanos, diseñado para ser más simple que YAML y más estricto que JSON.

El archivo tiene cuatro secciones principales:

[versions]
# Aquí declaramos las versiones como variables con nombre
spring-boot = "3.4.5"
lombok      = "1.18.36"

[libraries]
# Aquí describimos las dependencias: grupo, artefacto y referencia a la versión
spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot" }
lombok                  = { module = "org.projectlombok:lombok", version.ref = "lombok" }

[plugins]
# Aquí describimos los plugins de Gradle
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }

[bundles]
# Aquí agrupamos dependencias relacionadas bajo un solo alias (opcional)
web-starter = ["spring-boot-starter-web", "spring-boot-starter-validation"]

La idea principal: las versiones se declaran una sola vez en [versions], y en el resto de las secciones se hace referencia a ellas mediante version.ref. Si necesitas actualizar Spring Boot, cambias un solo lugar.

3. Alias y libs.*

La parte más práctica del Version Catalog es cómo se usa en build.gradle.kts. Gradle genera un objeto de acceso de tipo seguro llamado libs, y a través de él puedes referenciar las dependencias:

dependencies {
    // En lugar de escribir la cadena completa con la versión
    implementation("org.springframework.boot:spring-boot-starter-web:3.4.5")

    // Usamos el alias del catálogo — sin versión explícita, el IDE hace autocompletado
    implementation(libs.spring.boot.starter.web)
    compileOnly(libs.lombok)
    annotationProcessor(libs.lombok)
}

Nota que los guiones en los nombres del catálogo se convierten en puntos en el código Kotlin: spring-boot-starter-web → libs.spring.boot.starter.web. Esto es una transformación automática de Gradle.

Las ventajas son evidentes:

  • El IDE conoce todos los alias y los sugiere al escribir.
  • Si el alias no existe, hay un error en tiempo de compilación (no en runtime).
  • La versión está en un solo lugar — en el archivo TOML.

4. Version Catalog en el proyecto ReadLater Starter

En nuestro proyecto educativo readlater-starter el archivo gradle/libs.versions.toml ya está preparado. Así luce su versión simplificada:

[versions]
spring-boot         = "3.4.5"
spring-dependency   = "1.1.7"
lombok              = "1.18.36"
mapstruct           = "1.6.3"
testcontainers      = "1.20.4"

[libraries]
# Spring Boot starters
spring-boot-starter-web        = { module = "org.springframework.boot:spring-boot-starter-web",        version.ref = "spring-boot" }
spring-boot-starter-data-jpa   = { module = "org.springframework.boot:spring-boot-starter-data-jpa",   version.ref = "spring-boot" }
spring-boot-starter-validation = { module = "org.springframework.boot:spring-boot-starter-validation", version.ref = "spring-boot" }
spring-boot-starter-test       = { module = "org.springframework.boot:spring-boot-starter-test",       version.ref = "spring-boot" }

# Utilidades
lombok              = { module = "org.projectlombok:lombok",             version.ref = "lombok" }
mapstruct           = { module = "org.mapstruct:mapstruct",              version.ref = "mapstruct" }
mapstruct-processor = { module = "org.mapstruct:mapstruct-processor",    version.ref = "mapstruct" }

# Testing
testcontainers-junit   = { module = "org.testcontainers:junit-jupiter",   version.ref = "testcontainers" }
testcontainers-pg      = { module = "org.testcontainers:postgresql",       version.ref = "testcontainers" }

[plugins]
spring-boot          = { id = "org.springframework.boot",             version.ref = "spring-boot" }
spring-dependency    = { id = "io.spring.dependency-management",      version.ref = "spring-dependency" }

Presta atención a que mapstruct y mapstruct-processor tienen la misma version.ref = "mapstruct". Cuando llegue el momento de actualizar MapStruct, cambiará exactamente un número en la sección [versions].

5. Cómo conectar el catálogo en build.gradle.kts

Una vez creado el archivo TOML en gradle/libs.versions.toml, Gradle lo recoge automáticamente — no hace falta ninguna configuración adicional. Solo tienes que usar los alias en build.gradle.kts:

plugins {
    // Usamos los alias del catálogo también para los plugins
    alias(libs.plugins.spring.boot)
    alias(libs.plugins.spring.dependency)
    kotlin("jvm") version "2.1.0"
}

dependencies {
    implementation(libs.spring.boot.starter.web)
    implementation(libs.spring.boot.starter.data.jpa)
    implementation(libs.spring.boot.starter.validation)

    compileOnly(libs.lombok)
    annotationProcessor(libs.lombok)
    annotationProcessor(libs.mapstruct.processor)
    implementation(libs.mapstruct)

    testImplementation(libs.spring.boot.starter.test)
    testImplementation(libs.testcontainers.junit)
    testImplementation(libs.testcontainers.pg)
}

El resultado es un archivo de build limpio y sin cadenas mágicas de versiones. Cualquier persona que abra el proyecto verá inmediatamente cuáles son las dependencias y podrá actualizar las versiones en un solo lugar.

6. Errores típicos al trabajar con el Version Catalog

Error n.º 1: alias incorrecto.
Si escribes libs.springBoot.starterWeb en lugar de libs.spring.boot.starter.web, Gradle indicará un error en la compilación. Recuerda: los guiones del TOML se transforman en puntos en el código.

Error n.º 2: olvidar añadir la dependencia al catálogo.
Si declaras una nueva dependencia directamente en build.gradle.kts con una cadena de versión hardcodeada, rompes la idea del catálogo. La disciplina aquí es sencilla: toda nueva dependencia primero va al TOML, luego se referencia desde el build script.

Error n.º 3: duplicación de versiones.
Si la misma versión se declara en varios lugares del archivo TOML (por ejemplo, una vez en [versions] y otra vez dentro de la biblioteca como cadena literal), los archivos pierden consistencia. Utiliza siempre version.ref en lugar de version = "..." para las dependencias.

Error n.º 4: confundir libs.plugins y libs para dependencias.
Los plugins se conectan mediante alias(libs.plugins.XXX), y las dependencias de las bibliotecas mediante implementation(libs.XXX). Son bloques diferentes en el TOML: [plugins] y [libraries].

1
Tarea
Java Server, nivel 5, lección 3
Bloqueada
Un alias para `slf4j-api`
Un alias para `slf4j-api`
1
Tarea
Java Server, nivel 5, lección 3
Bloqueada
Versión común de Jackson en `libs.versions.toml`
Versión común de Jackson en `libs.versions.toml`
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION