Skip to content

Using bluetape4k-dependencies: Combining Multiple Libraries

Small robotic workers align module blocks on a conveyor and central dependency BOM station in a 3D workbench illustration
An application does not need to memorize separate versions for projects, exposed, aws, image, text, graph, leader, and javers. Import the BOM first, then choose the modules it needs.

Using one bluetape4k library is simple enough. An application may add bluetape4k-core and stop there. Real services usually do not stay that small.

A more realistic combination looks like this:

  • common Kotlin/JVM helpers from projects
  • database access through exposed
  • AWS integration through aws
  • batch or scheduler ownership through leader
  • search text or blockword handling through text

At that point the developer’s question becomes practical:

What should go in build.gradle.kts, and which versions should the application manage directly?

The short answer is: import the bluetape4k-dependencies BOM first, then omit versions from bluetape4k modules.

Architecture diagram showing an application applying the bluetape4k-dependencies BOM to align feature module versions while documenting temporary overrides with an exit condition
The application uses the central BOM as its normal version boundary. A per-module override remains a documented temporary exception with an explicit exit condition.

With the Kotlin DSL, import the BOM with platform(...).

dependencies {
implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:1.4.0"))
implementation("io.github.bluetape4k:bluetape4k-core")
implementation("io.github.bluetape4k:bluetape4k-coroutines")
implementation("io.github.bluetape4k.exposed:bluetape4k-exposed-jdbc")
implementation("io.github.bluetape4k.aws:bluetape4k-aws-java")
implementation("io.github.bluetape4k.leader:bluetape4k-leader-core")
}

The important line is the first one:

implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:1.4.0"))

After that, declare the modules without versions. Adding versions again makes the BOM less useful.

// Prefer not to write this when the BOM is already imported.
implementation("io.github.bluetape4k.exposed:bluetape4k-exposed-jdbc:1.11.0")
implementation("io.github.bluetape4k.aws:bluetape4k-aws-java:0.4.0")

dependencies 1.4.0 manages a tested combination such as projects 1.12.1, exposed 1.12.1, aws 0.5.0, image 0.4.0, text 0.3.0, graph 0.6.0, leader 0.5.0, and javers 0.3.0. The application only chooses the BOM version.

Import It with dependencyManagement in Maven

Section titled “Import It with dependencyManagement in Maven”

In Maven, import the BOM under dependencyManagement.

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.bluetape4k</groupId>
<artifactId>bluetape4k-dependencies</artifactId>
<version>1.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Then omit versions from the dependencies themselves.

<dependencies>
<dependency>
<groupId>io.github.bluetape4k</groupId>
<artifactId>bluetape4k-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.bluetape4k.exposed</groupId>
<artifactId>bluetape4k-exposed-r2dbc</artifactId>
</dependency>
</dependencies>

Gradle and Maven follow the same rule.

Put the version on the BOM.
Do not put versions on individual bluetape4k modules.

Writing every module version can look clearer at first.

dependencies {
implementation("io.github.bluetape4k:bluetape4k-core:1.11.0")
implementation("io.github.bluetape4k.exposed:bluetape4k-exposed-r2dbc:1.10.0")
implementation("io.github.bluetape4k.aws:bluetape4k-aws-spring-boot:0.4.0")
}

But now the application owns the compatibility matrix. core may be new while exposed is old. The AWS module may expect a different Spring Boot or Ktor line. If the mismatch is obvious, the build breaks. If it is less obvious, the runtime classpath gets the surprise.

The symptoms often look simple:

NoSuchMethodError
ClassNotFoundException
BeanCreationException
dependency resolution failed

The investigation is the expensive part. Is the application code wrong? Is the Exposed line too old? Did the Spring Boot line move? Did a transitive dependency win resolution? At that point the work is no longer feature development; it is cleanup.

The BOM at least keeps the bluetape4k library family on one intended combination, so the application can focus on its own service dependencies.

It is easier to choose dependencies by the boundary the service needs.

JobExample dependency
Common Kotlin/JVM foundationio.github.bluetape4k:bluetape4k-core, bluetape4k-coroutines, bluetape4k-logging
Exposed JDBC/R2DBCio.github.bluetape4k.exposed:bluetape4k-exposed-jdbc, bluetape4k-exposed-r2dbc
Exposed in Spring Bootio.github.bluetape4k.exposed:bluetape4k-exposed-spring-boot-jdbc, bluetape4k-exposed-spring-boot-r2dbc
AWS SDK helpersio.github.bluetape4k.aws:bluetape4k-aws-java, bluetape4k-aws-kotlin
AWS with Ktor or Spring Bootio.github.bluetape4k.aws:bluetape4k-aws-ktor, bluetape4k-aws-spring-boot
Image processingio.github.bluetape4k.image:bluetape4k-images, bluetape4k-images-ocr, bluetape4k-images-vips-api
Korean/Japanese text handlingio.github.bluetape4k.text:tokenizer-korean, tokenizer-japanese, text-search, lingua
Leader electionio.github.bluetape4k.leader:bluetape4k-leader-core, bluetape4k-leader-redis-lettuce, bluetape4k-leader-spring-boot
GraphDB integrationio.github.bluetape4k.graph:bluetape4k-graph-core, bluetape4k-graph-neo4j, bluetape4k-graph-memgraph
Audit and diff storageio.github.bluetape4k.javers:javers-core, javers-exposed, javers-persistence-redis

No version appears in that table because the BOM is already imported.

For example, a Spring Boot service using Exposed, AWS, and leader election can start this way:

dependencies {
implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:1.4.0"))
implementation("io.github.bluetape4k:bluetape4k-spring-boot-core")
implementation("io.github.bluetape4k.exposed:bluetape4k-exposed-spring-boot-jdbc")
implementation("io.github.bluetape4k.aws:bluetape4k-aws-spring-boot")
implementation("io.github.bluetape4k.leader:bluetape4k-leader-spring-boot")
}

A Ktor service that adds AWS and text processing can start with this:

dependencies {
implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:1.4.0"))
implementation("io.github.bluetape4k.aws:bluetape4k-aws-ktor")
implementation("io.github.bluetape4k.text:tokenizer-korean")
implementation("io.github.bluetape4k.text:text-search")
}

If an existing application uses 1.2.0, start by changing only the BOM version.

dependencies {
implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:1.2.0"))
implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:1.4.0"))
implementation("io.github.bluetape4k:bluetape4k-core")
implementation("io.github.bluetape4k.exposed:bluetape4k-exposed-jdbc")
}

Then check the boundaries the service actually uses.

Terminal window
./gradlew compileTestKotlin
./gradlew test
./gradlew dependencyInsight --dependency exposed --configuration runtimeClasspath
./gradlew dependencyInsight --dependency spring-boot --configuration runtimeClasspath

Not every project needs dependencyInsight on every upgrade. It is worth checking when dependencies close to the service boundary move together: Exposed, Spring Boot, Ktor, Jackson, AWS SDK, and similar runtime lines. If something breaks, the resolved dependency graph after the BOM upgrade is already visible.

Sometimes a service needs to pin one module temporarily: a security fix, an upstream regression, or a patch that has not yet landed in the BOM.

That override should stay explicit and temporary.

dependencies {
implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:1.4.0"))
implementation("io.github.bluetape4k:bluetape4k-core")
// Temporary override: remove after the fix is included in the BOM.
implementation("io.github.bluetape4k.text:text-search:0.3.0")
}

An override left without a comment is ambiguous later. Was it intentional? Is it safe to remove? What condition clears it? If an override is necessary, leave both the reason and the removal condition.

Gradle Version Catalogs are useful for maintaining bluetape4k repositories and workshops. They keep aliases aligned and make build scripts look similar across repositories.

Application developers using bluetape4k-dependencies do not need to understand the catalog structure first. They need four rules:

  1. Import the io.github.bluetape4k:bluetape4k-dependencies BOM.
  2. Do not put versions on bluetape4k module dependencies.
  3. The more bluetape4k libraries are used together, the more useful the BOM becomes.
  4. If an individual version is overridden, document why and when to remove it.

That is enough. The catalog is mainly for the side that builds bluetape4k. The application side imports the BOM and chooses the modules it needs.

Maven Central Does Not Have a Rollback Button

Section titled “Maven Central Does Not Have a Rollback Button”

One background detail helps explain the release behavior: a released artifact on Maven Central cannot be overwritten with the same version.

So if a BOM version has a problem, the usual fix is not to re-upload the same version. A problem in 1.3.0 becomes a new patch version such as 1.3.1.

A published BOM version is wrong
-> do not wait for the same version to be overwritten
-> look for the next patch BOM version

Users do not need the whole release procedure to use the BOM. This point answers the common question: “why not just republish 1.3.0?” Maven Central does not have a rollback button.

This is the basic usage path for application developers. The following posts look at what changed in the libraries when moving from 1.2.0 to 1.3.x, and which bugs or technical debt were removed along the way.

Comments

Leave a note or reaction with your GitHub account.