[[version_alignment]]
= Aligning dependency versions

Dependency version alignment allows different modules belonging to the same logical group (a _platform_) to have identical versions in a dependency graph.

== Handling inconsistent module versions

Gradle supports aligning versions of modules which belong to the same "platform".
It is often preferable, for example, that the API and implementation modules of a component are using the same version.
However, because of the game of transitive dependency resolution, it is possible that different modules belonging to the same platform end up using different versions.
For example, your project may depend on the `jackson-databind` and `vert.x` libraries, as illustrated below:

.Declaring dependencies
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/groovy",files="build.gradle[tags=dependencies]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/kotlin",files="build.gradle.kts[tags=dependencies]"]
====

Because `vert.x` depends on `jackson-core`, we would actually resolve the following dependency versions:

- `jackson-core` version `2.9.5` (brought by `vertx-core`)
- `jackson-databind` version `2.9.5` (by conflict resolution)
- `jackson-annotation` version `2.9.0` (dependency of `jackson-databind:2.9.5`)

It's easy to end up with a set of versions which do not work well together.
To fix this, Gradle supports dependency version alignment, which is supported by the concept of platforms.
A platform represents a set of modules which "work well together".
Either because they are actually published as a whole (when one of the members of the platform is published, all other modules are also published with the same version), or because someone tested the modules and indicates that they work well together (typically, the Spring Platform).

== Aligning versions natively with Gradle

Gradle natively supports alignment of modules produced by Gradle.
This is a direct consequence of the transitivity of <<dependency_constraints.adoc#sec:adding-constraints-transitive-deps, dependency constraints>>.
So if you have a multi-project build, and you wish that consumers get the same version of all your modules, Gradle provides a simple way to do this using the <<java_platform_plugin.adoc#,Java Platform Plugin>>.

For example, if you have a project that consists of 3 modules:

- `lib`
- `utils`
- `core`, depending on `lib` and `utils`

And a consumer that declares the following dependencies:

- `core` version 1.0
- `lib` version 1.1

Then by default resolution would select `core:1.0` and `lib:1.1`, because `lib` has no dependency on `core`.
We can fix this by adding a new module in our project, a _platform_, that will add constraints on all the modules of your project:

.The platform module
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignmentWithPlatform/groovy/platform",files="build.gradle[tags=platform]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignmentWithPlatform/kotlin/platform",files="build.gradle.kts[tags=platform]"]
====

Once this is done, we need to make sure that all modules now _depend on the platform_, like this:

.Declaring a dependency on the platform
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignmentWithPlatform/groovy/core",files="build.gradle[tags=dependencies]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignmentWithPlatform/kotlin/core",files="build.gradle.kts[tags=dependencies]"]
====

It is important that the platform contains a constraint on all the components, but also that each component has a dependency on the platform.
By doing this, whenever Gradle will add a dependency to a module of the platform on the graph, it will _also_ include constraints on the other modules of the platform.
This means that if we see another module belonging to the same platform, we will automatically upgrade to the same version.

In our example, it means that we first see `core:1.0`, which brings a platform `1.0` with constraints on `lib:1.0` and `lib:1.0`.
Then we add `lib:1.1` which has a dependency on `platform:1.1`.
By conflict resolution, we select the `1.1` platform, which has a constraint on `core:1.1`.
Then we conflict resolve between `core:1.0` and `core:1.1`, which means that `core` and `lib` are now aligned properly.

NOTE: This behavior is enforced for published components only if you use Gradle Module Metadata.

[[sec:align-versions-unpublished]]
== Aligning versions of modules not published with Gradle

Whenever the publisher doesn't use Gradle, like in our Jackson example, we can explain to Gradle that all Jackson modules "belong to" the same platform and benefit from the same behavior as with native alignment.
There are two options to express that a set of modules belong to a platform:

1. A platform is **published** as a <<platforms.adoc#sub:bom_import,BOM>> and can be used:
   For example, `com.fasterxml.jackson:jackson-bom` can be used as platform.
   The information missing to Gradle in that case is that the platform should be added to the dependencies if one of its members is used.
2. No existing platform can be used. Instead, a **virtual platform** should be created by Gradle:
   In this case, Gradle builds up the platform itself based on all the members that are used.

To provide the missing information to Gradle, you can define <<component_metadata_rules.adoc#,component metadata rules>> as explained in the following.

[[sec:align_bom_platform]]
=== Align versions of modules using a published BOM

.A dependency version alignment rule
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/groovy",files="build.gradle[tags=bom-alignment-rule]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/kotlin",files="build.gradle.kts[tags=bom-alignment-rule]"]
====

By using the `belongsTo` with `false` (**not** virtual), we declare that all modules belong to the same _published platform_.
In this case, the platform is `com.fasterxml.jackson:jackson-bom` and Gradle will look for it, as for any other module, in the declared repositories.

.Making use of a dependency version alignment rule
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/groovy",files="build.gradle[tags=use_bom_rule]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/kotlin",files="build.gradle.kts[tags=use_bom_rule]"]
====

Using the rule, the versions in the example above align to whatever the selected version of `com.fasterxml.jackson:jackson-bom` defines.
In this case, `com.fasterxml.jackson:jackson-bom:2.9.5` will be selected as `2.9.5` is the highest version of a module selected.
In that BOM, the following versions are defined and will be used:
`jackson-core:2.9.5`,
`jackson-databind:2.9.5` and
`jackson-annotation:2.9.0`.
The lower versions of `jackson-annotation` here might be the desired result as it is what the BOM recommends.

NOTE: This behavior is working reliable since Gradle 6.1. Effectively, it is similar to a <<component_metadata_rules.adoc#,component metadata rule>> that adds a platform dependency to all members of the platform using `withDependencies`.

[[sec:virtual_platform]]
=== Align versions of modules without a published platform

.A dependency version alignment rule
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/groovy",files="build.gradle[tags=alignment-rule]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/kotlin",files="build.gradle.kts[tags=alignment-rule]"]
====

By using the `belongsTo` keyword without further parameter (platform **is** virtual), we declare that all modules belong to the same _virtual platform_, which is treated specially by the engine.
A virtual platform will not be retrieved from a repository.
The identifier, in this case `com.fasterxml.jackson:jackson-virtual-platform`, is something you as the build author define yourself.
The "content" of the platform is then created by Gradle on the fly by collecting all `belongsTo` statements pointing at the same virtual platform.

.Making use of a dependency version alignment rule
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/groovy",files="build.gradle[tags=use_rule]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/kotlin",files="build.gradle.kts[tags=use_rule]"]
====

Using the rule, all versions in the example above would align to `2.9.5`.
In this case, also `jackson-annotation:2.9.5` will be taken, as that is how we defined our local virtual platform.

For both published and virtual platforms, Gradle lets you override the version choice of the platform itself by specifying an _enforced_ dependency on the platform:

.Forceful platform downgrade
====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/groovy",files="build.gradle[tags=enforced_platform]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-dependencyAlignment/kotlin",files="build.gradle.kts[tags=enforced_platform]"]
====

