Using Gradle version catalogs and Maven BOMs

Learn how to use Gradle Version Catalogs and Maven BOM definitions to help simplify dependency management

Using Gradle version catalogs and Maven BOMs

Whether starting a new project or working on an existing one, you should use Gradle Version Catalogs and Maven BOM definitions to help simplify dependency management. Let's understand why and how you can bring this magic to your projects.

When working with web frameworks such as Micronaut or Spring Boot, you will undoubtedly face the inevitable challenge of dealing with dependencies. Not properly maintaining a consistent list of dependencies and their versions will eventually cause problems across modules within a Gradle project.

Using Gradle version catalogs and Maven BOMs (Bill of Materials) can improve your projects in the following ways.

  • Type-Safe Dependency References: Maven BOMs and Gradle version catalogs in Kotlin Gradle scripts allow type-safe access to the dependencies and their versions throughout your Gradle scripts. This means you can keep references to plugins and dependencies in multiple modules in sync.
  • Consistent Dependency Versions: A BOM specifies a set of dependencies with their versions. This declaration helps avoid version conflicts and ensures that different parts of your project and modules use compatible versions of the libraries you depend on.
  • Simplified Dependency Management: Using a BOM lets you manage or override versions in a single place. You don’t need to specify versions for each dependency individually. Instead, you reference the BOM in your Gradle build script, and Gradle handles the versioning based on the BOM's definitions.
  • Ease of Upgrades: You can update the BOM version when you need to update your dependencies. This streamlines the process of keeping dependencies up-to-date and helps maintain a stable and secure build environment.
  • Version Conflict Resolution: Gradle version catalogs can help resolve version conflicts by providing a single source of truth for dependency versions. This can simplify the process of ensuring compatibility and avoiding issues where different parts of your project depend on incompatible versions of the same library.

Let’s look at an example of using these in a project using the Micronaut framework. Micronaut has a Gradle plugin that automatically applies its BOM to your project. You can then set the version in your Gradle version catalog. Let's start with a blank project from the Micronaut Launch site.

The Micronaut Launch Site

We will choose the latest stable version of the Micronaut framework, Kotlin as our language, Gradle Kotlin as our build system, and JUnit as our test framework.

Version Catalogs

In our new project we will modify the setup to use version catalogs. We will create a new file at gradle/libs.versions.toml and then move the declarations of micronautVersion and kotlinVersion from gradle.properties to the version catalog.

#file: gradle/libs.versions.toml

[versions]
# This version declaration was moved from gradle.properties
# Note that you must use 'micronaut' - the framework requires it
micronaut = "4.6.1"
kotlin = "1.9.25"

[libraries]

[plugins]

We now have our versions defined in the version catalog, and they can be referenced in our project. We can safely remove the hardcoded version references.

//file: build.gradle.kts

// Modify our plugin versions
plugins {
  id("org.jetbrains.kotlin.jvm") version libs.versions.kotlin
  id("org.jetbrains.kotlin.plugin.allopen") version libs.versions.kotlin
}

// Remove this reference
val kotlinVersion = project.properties.get("kotlinVersion")

// Remove the version references from the Kotlin dependencies
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

When we want to update dependency versions, we can now do so in one place. Since the project uses these references, they will all stay in sync, which is hugely beneficial as the project grows.

Maven BOMs

Another thing we could improve is the dependency references. These are all “stringly” typed and when the project has multiple modules it can be difficult to know if another module already relies on a dependency you may want to add. Let's improve this a bit. It would be great if we could centralize our list of dependencies and get strongly typed references to them via the Maven BOMs.

Micronaut has a built-in plugin that can add this for us. We can then reference all available Micronaut dependencies through this catalog. Let's add this plugin to our settings.gradle.kts file.

//file: settings.gradle.kts

plugins {
  id("io.micronaut.platform.catalog") version "4.4.2"
}

We can now update our dependency references in build.gradle.kts file and use the version catalog.

//file: build.gradle.kts

dependencies {

  ksp(mn.micronaut.http.validation)

  // REMOVE ksp("io.micronaut:micronaut-http-validation")
		
  ksp(mn.micronaut.serde.processor)

  // REMOVE ksp("io.micronaut.serde:micronaut-serde-processor")
    
  implementation(mn.micronaut.kotlin.runtime)
    
  //REMOVE implementation("io.micronaut.kotlin:micronaut-kotlin-runtime")
    
}

We can add any additional dependencies within our version catalog. This allows us to centralize all dependency declarations and even create groups of related dependencies.

Let's bring in an external dependency into libs.versions.toml and add it to our project.

#file: libs.versions.toml

[versions]
commons = "2.16.1"

[libraries]
apache-commons-io = { group = "commons-io", name = "commons-io", version.ref = "commons" }

We can then reference this library in our project in a type-safe manner.

//file: buidld.gradle.kts

dependencies {
  implementation(libs.apache.commons.io)
}

We can also import other BOMs available on Maven Central by adding them to our settings.gradle.kts file under version catalogs. This allows us to reference other version catalogs defined by other projects.

//file: settings.gradle.kts

dependencyResolutionManagement {
    repositories {
        mavenCentral()
    }
    versionCatalogs {
        create("mnopenapi") {
            from("io.micronaut.openapi:micronaut-openapi-bom:6.11.1")
        }
    }
}

With this in place, we can now reference this catalog's dependencies.

//file: build.gradle.kts
dependencies {
  compileOnly(mnopenapi.micronaut.openapi.annotations)
}

Using version catalogs and Maven BOMs can significantly improve dependency management in Gradle projects. Please let me know if you have any questions, thoughts, or improvements.

Subscribe to zsiegel.com

Don’t miss out on the latest articles. Sign up now to get exclusive members only content and early access to articles.
jamie@example.com
Subscribe