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 dependenciesblock, 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 specific version
        api("com.google.guava:guava:[33.1.0-jre,)") // Enforcing version range
    }
}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.