skip to content

How do you align a dependency family that publishes no BOM, using a ComponentMetadataRule and belongsTo with a virtual platform?

level: middleimportance: must knowfreq 45%

answer

  1. ComponentMetadataRule.execute
  2. components.all<Rule>()
  3. belongsTo(coord, true) = virtual
  4. version = details.id.version
  5. rules are cacheable, no I/O

basics

~10 s

Write a ComponentMetadataRule, register it with components.all(...), and in execute() call details.belongsTo("group:family-platform:version", true). The true flag makes it a virtual (Gradle-synthesized) platform so all matching modules align.

solid answer

~40 s

When a vendor ships related modules but no BOM, you synthesize a **virtual platform**. You implement `ComponentMetadataRule`, register it under the `dependencies { components { all<Rule>() } }` block, and in `execute(ctx)` inspect `ctx.details.id` and call `belongsTo("group:some-platform:${id.version}", true)` for every module of the family. The second argument `true` means *virtual* — there is no published `group:some-platform` artifact; Gradle invents one purely for alignment bookkeeping. Once two or more modules declare membership in the same virtual platform coordinate, Gradle's resolution aligns them: pick the winning version via normal conflict resolution, then pull every sibling to it. Use `belongsTo(coord, false)` only when the platform is actually published. Keep the rule cheap and side-effect-free; it's evaluated per component and is cacheable, so avoid I/O.

code

kotlin · 15 lines
kotlin
abstract class GroovyAlignmentRule : ComponentMetadataRule {
    override fun execute(context: ComponentMetadataContext) {
        context.details.run {
            if (id.group == "org.codehaus.groovy") {
                belongsTo("org.codehaus.groovy:groovy-platform:$id.version".let {
                    "org.codehaus.groovy:groovy-platform:${id.version}"
                }, true)
            }
        }
    }
}

dependencies {
    components { all<GroovyAlignmentRule>() }
}

go deeper

for a junior

Recognize that a rule plus belongsTo can align a family; exact API not expected.

for a middle

Write the rule, register it, and explain the virtual-vs-real boolean and version mapping.

for a senior

Reason about caching/hygiene, cross-group alignment, and how alignment composes with conflict resolution.

for a principal

Standardize alignment rules as a convention plugin so every project inherits consistent family handling.

## ComponentMetadataRule basics A `ComponentMetadataRule` is a callback Gradle runs while resolving the metadata of each component (each `group:name:version`). It lets you *adjust* metadata that the publisher got wrong or omitted — adding constraints, attributes, capabilities, or platform membership — without touching the published POM. You register rules in the `dependencies` block: ```kotlin dependencies { components { all<GuavaAlignmentRule>() // applies to every component // or withModule("g:n") { ... } for a single module } } ``` ## Declaring virtual-platform membership Inside `execute`, you reach `ComponentMetadataDetails` via `ctx.details` and call: ```kotlin details.belongsTo("com.example:my-family-platform:${details.id.version}", true) ``` - The coordinate is *your* choice — a synthetic group:name you pick. Convention: reuse the family group, e.g. `com.example:family-platform`. - The version is normally `details.id.version`, so each concrete module version maps to the *same-versioned* virtual platform — that is what makes `databind:2.15` and `core:2.13` resolve to a common winner. - The boolean: `true` = **virtual** (Gradle synthesizes the platform; nothing is published). `false` = a **real published** platform you're asserting membership of. ## How alignment then resolves Gradle sees several modules all claiming membership in `com.example:family-platform` at varying versions. It treats the platform as a single node, runs conflict resolution to pick one platform version (highest by default), and forces every member to that version. Direct *and* transitive members are caught. ## Good rule hygiene - Filter precisely: `if (details.id.group == "com.fasterxml.jackson.core") ...` — over-broad matching aligns unrelated modules. - Make the rule a top-level `abstract class` (or one with `@Inject` parameters) so it's cacheable; avoid capturing the project or doing network/file I/O — rules run during resolution and are cached by Gradle. - Prefer a published BOM/platform when one exists; a virtual platform is the fallback. ## Multi-group families Some families span groups (Jackson core vs. datatype modules). One rule can map several groups to *one* virtual platform coordinate, aligning across groups — something a single Maven BOM import wouldn't naturally express.

  • What does the second boolean argument to belongsTo control?
    Whether the platform is virtual (true → Gradle synthesizes it, no published artifact) or real/published (false → you assert membership of an actually-published platform).
  • Why must metadata rules avoid file or network I/O?
    Rules run during resolution and their results are cached; doing I/O breaks reproducibility and cacheability and slows every resolution.
  • How would you align two modules that live in different Maven groups?
    Map both groups to the *same* virtual-platform coordinate inside one rule; Gradle then treats them as one platform and aligns across groups.

saying these in an interview costs you the question

  • Using belongsTo(coord, false) for a platform that was never published — Gradle will try to resolve a real platform and fail.
  • Capturing the Project object or reading files in the rule, breaking caching.
  • Over-broad group matching that drags unrelated modules into the platform.

context