Bluetape4k Projects Part 6: Spring Boot 4 and Ktor

This is the final post in the bluetape4k-projects series. The earlier posts moved from foundation, coroutine/testing,
I/O, data/infra, and utilities toward the application boundary. Now we look at Spring Boot 4 and Ktor 3 modules.
The application modules in projects do not replace the frameworks. Spring Boot remains Spring Boot; Ktor remains
Ktor. The modules organize repeated wiring near service entry points: common JSON, coroutine helpers, Redis serializers,
R2DBC/MongoDB coroutine extensions, observability, resilience, and testing helpers. The goal is not hidden magic. The
goal is less repeated wiring and clearer boundaries.

Spring Boot 4: The Versionless Standard Path
Section titled “Spring Boot 4: The Versionless Standard Path”Current spring-boot/* modules target Spring Boot 4.x. The README states that versionless spring-boot/* artifacts are
published as Spring Boot 4 implementations. It also documents an important build rule: import Spring Boot BOMs with
Gradle implementation(platform(...)), not dependencyManagement { imports }, to avoid Kotlin Gradle Plugin 2.3.x
conflicts.
dependencies { implementation(platform("org.springframework.boot:spring-boot-dependencies:4.x.x")) implementation(platform("io.github.bluetape4k:bluetape4k-bom:<version>")) implementation("io.github.bluetape4k:bluetape4k-spring-boot-core")}bluetape4k-spring-boot-core covers WebFlux + coroutines, RestClient suspend DSL, WebClient extensions, Jackson 2
customizers, Retrofit2 integration, and WebTestClient testing helpers. Spring Boot 4 still uses Jackson 2 internally,
so this module keeps the application boundary aligned with that framework boundary.
val restClient = RestClient.create("https://api.example.com")
val user: User = restClient.suspendGet("/users/1")val created: User = restClient.suspendPost("/users", newUser, MediaType.APPLICATION_JSON)Spring Data support is split into narrower modules. spring-boot/redis provides RedisBinarySerializer,
RedisCompressSerializer, and redisSerializationContext {} DSLs for RedisTemplate and ReactiveRedisTemplate.
spring-boot/r2dbc adds coroutine CRUD helpers for R2dbcEntityOperations. MongoDB and Cassandra modules organize
Spring Data reactive/coroutine boundaries. Hibernate Lettuce brings Hibernate second-level cache and Lettuce Near Cache
auto-configuration into the application layer.
Ktor 3: Explicit Plugin Assembly
Section titled “Ktor 3: Explicit Plugin Assembly”Ktor modules use explicit installation as the default posture. ktor/core provides common JSON defaults,
installBluetape4kKtorCore(), JSON error responses, /healthz, /readyz, and query/path parameter helpers. If the
application needs to own plugin setup directly, config flags can disable individual pieces.
fun Application.module() { installBluetape4kKtorCore()
routing { get("/items/{type}") { val type = call.requiredPathParameter("type") val size = call.intQueryParameter("size", defaultValue = 10, range = 1..100) call.respond(mapOf("type" to type, "size" to size)) } }}ktor/observability adds sanitized correlation IDs, call logging MDC integration, Micrometer metrics, and optional
Prometheus scrape route helpers. It does not install OpenTelemetry tracing by default because exporter policy should
belong to the application.
ktor/resilience4j is route-scoped. Callers pass their own retry, circuit breaker, rate limiter, and time limiter
through KtorResiliencePolicies, then protect suspend blocks with resilientGet, resilientPost, or
withKtorResilience. CancellationException is rethrown and not counted as a circuit breaker failure, so cancellation
does not distort resilience metrics.
Spring Boot or Ktor?
Section titled “Spring Boot or Ktor?”Neither framework is always better. The application boundary decides.
| Need | Spring Boot 4 side | Ktor 3 side |
|---|---|---|
| Spring Data, auto-configuration, enterprise integration | Natural default | More manual wiring |
WebFlux coroutine controllers and WebTestClient | Fits spring-boot/core directly | Use Ktor when the runtime can stay smaller |
| Redis serializers, R2DBC/MongoDB coroutine extensions | Spring Data modules fit well | Compose infra modules directly |
| Small HTTP API, explicit plugins, thin runtime | May be heavier than needed | Fits ktor/core well |
| Route-scoped resilience/observability | Usually tied to Spring ecosystem config | Explicit through ktor/observability and ktor/resilience4j |
| Test style | Spring context and integration tests | Fast testApplication route tests |
Spring Boot is strong when ecosystem integration matters. Ktor is strong when a small HTTP API, explicit plugins, and thin runtime boundaries matter. The useful comparison is not taste; it is configuration ownership, test speed, data access shape, and observability policy.
Wiring Checklist
Section titled “Wiring Checklist”For a new backend service, keep the adoption order small:
- Import
bluetape4k-bomand framework BOMs withplatform(...). - Choose the application boundary first: Spring Boot or Ktor.
- Lock JSON, error, health, and readiness policy at the HTTP boundary.
- Add data, Redis, or messaging modules one external system at a time.
- Define correlation ID, metric, and trace ownership clearly.
- Apply resilience to one path at a time so timeouts, retries, and circuit breakers tell the same story.
- Use the closest example or workshop to verify application shape and test fixtures.
This keeps application modules thin. Skip the order and controllers can end up knowing about serializers, retries, pool sizing, and metrics all at once.
Source Links
Section titled “Source Links”- Spring Boot root:
spring-boot/ - Ktor root:
ktor/ - Examples root:
examples/ - Workshop root: bluetape4k-workshop
- Spring Boot core README:
spring-boot/core/README.md - Ktor core README:
ktor/core/README.md - Ktor observability README:
ktor/observability/README.md
Closing
Section titled “Closing”The bluetape4k-projects series started at the shared foundation and climbed to the application boundary. This
repository is not a framework that tries to solve everything. It is a shared Kotlin/JVM foundation for treating repeated
backend boundaries the same way.
Spring Boot 4 and Ktor 3 modules are the final layer that brings the foundation into service entry points. One favors
ecosystem integration; the other favors explicit, thin runtime assembly. Either way, the same rules hold: choose only
the modules you need, keep boundaries visible, check the example shape, and lock behavior in tests. Then projects
stays focused on repeated backend boundaries, and application code can stay focused on domain behavior.
Comments
Leave a note or reaction with your GitHub account.