Recommended ways of (Java) dependency management

Hi everyone!

I was wondering what the current recommendations for managing JVM (Java) dependencies in multi-project builds are.

For context, we’re talking about a Gradle multi-project build for a Java 11 application with ~100 subprojects and 242 unique external dependencies (dependencies between subprojects not counted).

I’ve found multiple approaches but would like to discuss the advantages and disadvantages.

  1. Declare dependencies in a dedicated submodule with the java-platform plugin and include the platform in all subprojects. This is the approach I’m currently using
  2. Declare dependencies in a map in a Gradle script and refer to them in each subproject build.gradle file.
    Example: Kafka
  3. Declare dependency versions in gradle.properties and include them in every subproject.
    api("org.apache.kafka:kafka-clients:${kafka_version}")
    implementation("org.slf4j:slf4j-api:${slf4j_version}")
    // ...
    

Which approach are you using What are the advantages or disadvantages you see?

1 Like

Nobody with an opinion on this? :sweat_smile:

Do you mean all your subprojects share the same dependencies? If I have same configuration in subprojects I usually define it in their parent project like this:

subprojects {
    dependencies {
        ...
    }
}

You could also create a convention plugin if you have more complex logic, see Sharing build logic between subprojects Sample

No, but all subprojects which are using the same dependencies should use them in the same version.

Since tooling support for version catalogs wasn’t good/existent at the time, we went with the platform (BOM) approach.

Platforms and version catalogs are not interchangeable though, they are two different tools that can also be used together.

A version catalog is just a directory of coordinates with versions you can choose from when declaring a dependency, much like just writing the coordinates in the build script itself, or taking it from Gradle properties, or having a holder class in buildSrc, just better, standardized, more powerful, and with type-safe accessors in Kotlin DSL.

A platform influences the resolution result, so can for example be used to upgrade transitive dependencies without adding the dependency manually even though you are not using it directly.