Using Catalogs with Platforms
Both platforms and version catalogs help manage dependency versions in a project, but they serve different purposes and have different effects on dependency resolution:
Version Catalogs
-
Purpose: A version catalog centralizes and standardizes dependency coordinates (group, name, version) and provides type-safe accessors in the build script, making dependencies easier to manage.
-
Effect on Dependency Graph: Version catalogs do not directly affect dependency resolution. The versions defined in the catalog must be explicitly referenced in a
dependencies
block, and once referenced, they behave the same as any locally declared dependency. Additionally, the catalog’s contents are transparent to downstream consumers, meaning that consumers cannot identify whether a dependency was declared locally or sourced from a catalog.
[libraries]
mylib = { group = "com.example", name = "mylib", version = "1.0.0" }
Platforms
-
Purpose: A platform is a module in the dependency graph that enforces or aligns versions of dependencies (including transitive dependencies). It influences dependency resolution and ensures version consistency across different modules.
-
Effect on Dependency Graph: Platforms apply or enforce versions to dependencies that are declarated locally without versions. These versions in a platform are propagated through the dependency graph, affecting transitive dependencies and downstream consumers. They are a formal part of the dependency graph and can dictate the version chosen during resolution.
plugins {
`java-platform`
}
dependencies {
constraints {
api("com.example:mylib:2.0.0")
}
}
Using a catalog with a platform
Even if a version catalog defines a version for a dependency, Gradle might pick a different version during resolution if another component (e.g., a platform or a transitive dependency) suggests a different version (unless enforcedPlatform
is used).
For example, a version catalog may define mylib
as version 1.0.0
, but if a platform enforces 2.0.0
, Gradle will select version 2.0.0
.
To ensure consistent version alignment, a good approach is to use a version catalog to define dependency versions alongside a platform to enforce them.
Version Catalog:
[versions]
junit-jupiter = "5.10.3"
[libraries]
guava = { module = "com.google.guava:guava"}
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
junit-jupiter-launcher = { module = "org.junit.platform:junit-platform-launcher" }
Platform:
plugins {
`java-platform`
}
javaPlatform {
allowDependencies()
}
dependencies {
constraints {
api("org.junit.jupiter:junit-jupiter:5.11.1") // Enforcing version range
api("com.google.guava:guava:[33.1.0-jre,)") // Enforcing specific version
}
}
plugins {
id 'java-platform'
}
javaPlatform {
allowDependencies()
}
dependencies {
constraints {
api 'org.junit.jupiter:junit-jupiter:5.11.1' // Enforcing specific version
api 'com.google.guava:guava:[33.1.0-jre,)' // Enforcing version range
}
}
Consumer:
dependencies {
// Platform
implementation(platform(project(":platform")))
// Catalog
testImplementation(libs.junit.jupiter)
testRuntimeOnly(libs.junit.jupiter.launcher)
implementation(libs.guava)
}
dependencies {
// Platform
implementation platform(project(":platform"))
// Catalog
testImplementation libs.junit.jupiter
testRuntimeOnly libs.junit.jupiter.launcher
implementation libs.guava
}
Best Practices for using both a catalog and a platform:
-
Use version catalogs for defining and sharing dependency coordinates across projects. They make dependency declarations consistent and easier to manage but do not guarantee version alignment.
-
Use platforms when you need to influence or enforce version alignment across modules. Platforms ensure that dependencies resolve to the desired version, particularly in large or multi-module projects.