08 February 2022

Tags: gradle micronaut version catalog

Introduction

With the release of Gradle 7.4, Micronaut users now have an interesting option to manage their dependencies: using Gradle’s version catalogs. Indeed, for a few releases already, Micronaut has shipped its own version catalog alongside its BOM.

Let’s explore how to use it and what’s the benefit.

What is a version catalog?

In a nutshell, a version catalog allows centralizing dependency versions in a single place. Instead a build script, a typical dependency declaration looks like this:

dependencies {
    implementation("org.apache.slf4j:slf4j-api:1.7.25")
}

With a version catalog, the declaration looks like this:

dependencies {
    implementation(libs.slf4j)
}

And the dependency coordinates are defined in the gradle/libs.versions.toml file:

[versions]
slf4j = "1.7.25"

[libraries]
slf4j = { module = "org.apache.slf4j", version.ref = "slf4j" }

There are a couple of advantages in doing so:

  • dependency versions are centralized in this TOML file

  • the catalogs create "type safe accessors" which are completed by the IDE (although to my knowledge completion is only supported by IntelliJ IDEA with the Kotlin DSL)

You can read a more complete description about version catalogs in this blog post I wrote a few months ago.

The Micronaut version catalog

In addition, frameworks like Micronaut can publish version catalogs, which are then usable in your projects. You can then think of the Micronaut version catalog as a list of dependencies to pick up from: you don’t have to think about a version to choose, you can simply use the "recommendation" from Micronaut, but you don’t have to remember the dependency coordinates either.

Importing the Micronaut version catalog

Let’s start with a project that you can generate using the Micronaut CLI:

mn create-app catalog

(alternatively, download the project using Micronaut Launch)

Open the generated project and update the Gradle version by changing the gradle/gradle-wrapper.properties file:

distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip

Now, in order to import the Micronaut version catalog, add this to your settings.gradle file:

settings.gradle
dependencyResolutionManagement {
    repositories {
        mavenCentral()
    }
    versionCatalogs {
        create("mn") {
            from("io.micronaut:micronaut-bom:${micronautVersion}")
        }
    }
}

Here, we’re creating a new version catalog called mn. Internally, Gradle will automatically download the catalog which is published at the same GAV coordinates as its BOM as a TOML file and expose it to your build scripts.

Let’s open our build.gradle file. By default it defines the following dependencies:

dependencies {
    annotationProcessor("io.micronaut:micronaut-http-validation")
    implementation("io.micronaut:micronaut-http-client")
    implementation("io.micronaut:micronaut-jackson-databind")
    implementation("io.micronaut:micronaut-runtime")
    implementation("jakarta.annotation:jakarta.annotation-api")
    runtimeOnly("ch.qos.logback:logback-classic")
    implementation("io.micronaut:micronaut-validation")
}

Now, we can replace this with the following:

dependencies {
    annotationProcessor(mn.micronaut.http.validation)
    implementation(mn.micronaut.http.client)
    implementation(mn.micronaut.jackson.databind)
    implementation(mn.micronaut.runtime)
    implementation(mn.jakarta.annotation.api)
    runtimeOnly(mn.logback)
    implementation(mn.micronaut.validation)
}

What happened here? Basically, we replaced hardcoded dependency coordinates with references to the mn version catalog. It’s particularly interesting if you are using the Kotlin DSL as I mentioned earlier, because in this case, the dependency notations are type-safe: you can’t make a typo in dependency coordinates, and you get completion:

catalog completion

Nice!

Future work

Version catalogs will probably be enabled by default in future releases of Micronaut, which means that projects created via Micronaut Launch or the CLI tool would automatically use the catalog, so you don’t have to do the conversion described in this blog post. Stay tuned!