JVM Core
Shared model for the jvm cluster. The framework, persistence, testing, security, and
verification spokes all depend on these decisions — keep them consistent here so no spoke
contradicts another.
1. The decision this cluster turns on: language × framework
Every JVM service starts with two choices, and they cascade into which spokes apply:
Language ──┐
├──> Framework ──> Patterns + TDD + Security + Verification spokes
Persistence┘
Language — Kotlin (null-safe, coroutines, DSLs → kotlin-patterns) or Java
(immutable-by-default, streams, records → java-coding-standards). Pick one per module.
Framework — the heart of the decision:
| Framework |
Pick it when |
Spokes |
| Spring Boot |
Mature ecosystem, broad library support, team familiarity, JVM-mode services |
springboot-patterns, springboot-tdd, springboot-security, springboot-verification |
| Quarkus 3.x LTS |
Native images, fast startup/low memory, event-driven (Camel), Kubernetes-first |
quarkus-patterns, quarkus-tdd, quarkus-security, quarkus-verification |
| Ktor |
Lightweight Kotlin-native server, coroutine-first, minimal magic |
kotlin-ktor-patterns |
| none / library / KMP |
Pure Kotlin/Java lib or shared UI |
kotlin-patterns, compose-multiplatform-patterns |
Persistence — JPA/Hibernate for Java/Spring Boot (jpa-patterns) or JetBrains
Exposed for Kotlin (kotlin-exposed-patterns). Don't run both in one module.
Rule: one language + one framework + one persistence layer per service. Crossing
streams (Spring patterns inside a Quarkus module, JPA next to Exposed) is the main failure mode.
2. Shared conventions
- Kotlin — prefer immutability (
val), express absence with nullable types not null
hacks, model states with sealed classes/enums, build readable APIs with DSLs. → kotlin-patterns
- Java — final fields,
Optional for absence (not as a field), streams for transforms,
checked vs unchecked exceptions deliberate, constructor injection. → java-coding-standards
- Concurrency — Kotlin uses structured concurrency + Flow (
kotlin-coroutines-flows);
Java/Spring uses @Async/reactive, Quarkus uses Mutiny/reactive — keep async in the framework's idiom.
- Validation — validate every external input at the boundary (request DTOs, command args);
never trust caller data. This is shared across all four
*-security and *-patterns spokes.
- Layering — controller/resource → service → repository; keep persistence types out of the
web layer. →
springboot-patterns, quarkus-patterns.
3. Version / tooling matrix
| Concern |
Kotlin stack |
Spring Boot stack |
Quarkus stack |
| Language |
Kotlin (latest stable) |
Java 17+ (LTS) |
Java 17+ (LTS) |
| Framework |
Ktor / KMP |
Spring Boot 3.x |
Quarkus 3.x LTS |
| DI |
Koin |
Spring context |
CDI (Arc) |
| Persistence |
Exposed + HikariCP + Flyway |
JPA/Hibernate |
Panache (JPA/Hibernate) |
| Test runner |
Kotest + MockK |
JUnit 5 + Mockito + MockMvc + Testcontainers |
JUnit 5 + Mockito + REST Assured |
| Coverage |
Kover |
JaCoCo |
JaCoCo |
| Messaging |
— |
Spring events / async |
Apache Camel |
| Build |
Gradle (Kotlin DSL) |
Gradle / Maven |
Gradle / Maven (+ native) |
Serialization: kotlinx.serialization (Kotlin/Ktor); Jackson (Spring Boot / Quarkus JSON).
4. The lifecycle every stack shares
patterns ──> TDD ──> security ──> verification
- Patterns — architecture + conventions for the chosen framework.
- TDD — write tests first (
*-tdd / kotlin-testing); aim for meaningful coverage, not a number.
- Security — authn/authz, input validation, CSRF, secrets, headers, dependency scanning (
*-security).
- Verification — build → static analysis → tests+coverage → security scan → (Quarkus: native compile) → diff review before PR/release (
*-verification).
5. Shared guardrails
- One stack per module: don't mix Spring Boot and Quarkus, or JPA and Exposed, in one service.
- Tests first: features/bugfixes go through the
*-tdd / kotlin-testing loop before merge.
- Security is not optional: run the matching
*-security spoke; validate all external input.
- Gate releases: nothing ships until the matching
*-verification loop is green.
- Kotlin null-safe, Java immutable-by-default: lean on the language's safety guarantees.
- Spring Boot vs Quarkus: choose Spring Boot for ecosystem breadth and team familiarity;
choose Quarkus for native images, fast startup, low memory, and event-driven/Kubernetes-first
services. Choose Ktor when you want a lightweight, coroutine-first Kotlin server.
1---2name: jvm-core3description: Shared reference for the JVM cluster: the language × framework decision (Kotlin vs Java; Spring Boot vs Quarkus vs Ktor), the build/test/coverage toolchain, the persistence choice (JPA/Hibernate vs Exposed), and the patterns → TDD → security → verification lifecycle. USE WHEN choosing a JVM stack, wiring persistence, or planning the test/security/release loop every JVM spoke shares.4---56# JVM Core78Shared model for the `jvm` cluster. The framework, persistence, testing, security, and9verification spokes all depend on these decisions — keep them consistent here so no spoke10contradicts another.1112## 1. The decision this cluster turns on: language × framework1314Every JVM service starts with two choices, and they cascade into which spokes apply:1516```17Language ──┐18 ├──> Framework ──> Patterns + TDD + Security + Verification spokes19Persistence┘20```2122- **Language** — **Kotlin** (null-safe, coroutines, DSLs → `kotlin-patterns`) or **Java**23 (immutable-by-default, streams, records → `java-coding-standards`). Pick one per module.24- **Framework** — the heart of the decision:2526 | Framework | Pick it when | Spokes |27 |---|---|---|28 | **Spring Boot** | Mature ecosystem, broad library support, team familiarity, JVM-mode services | `springboot-patterns`, `springboot-tdd`, `springboot-security`, `springboot-verification` |29 | **Quarkus 3.x LTS** | Native images, fast startup/low memory, event-driven (Camel), Kubernetes-first | `quarkus-patterns`, `quarkus-tdd`, `quarkus-security`, `quarkus-verification` |30 | **Ktor** | Lightweight Kotlin-native server, coroutine-first, minimal magic | `kotlin-ktor-patterns` |31 | **none / library / KMP** | Pure Kotlin/Java lib or shared UI | `kotlin-patterns`, `compose-multiplatform-patterns` |3233- **Persistence** — **JPA/Hibernate** for Java/Spring Boot (`jpa-patterns`) or **JetBrains34 Exposed** for Kotlin (`kotlin-exposed-patterns`). Don't run both in one module.3536**Rule:** one language + one framework + one persistence layer **per service**. Crossing37streams (Spring patterns inside a Quarkus module, JPA next to Exposed) is the main failure mode.3839## 2. Shared conventions4041- **Kotlin** — prefer immutability (`val`), express absence with nullable types not `null`42 hacks, model states with sealed classes/enums, build readable APIs with DSLs. → `kotlin-patterns`43- **Java** — final fields, `Optional` for absence (not as a field), streams for transforms,44 checked vs unchecked exceptions deliberate, constructor injection. → `java-coding-standards`45- **Concurrency** — Kotlin uses structured concurrency + Flow (`kotlin-coroutines-flows`);46 Java/Spring uses `@Async`/reactive, Quarkus uses Mutiny/reactive — keep async in the framework's idiom.47- **Validation** — validate every external input at the boundary (request DTOs, command args);48 never trust caller data. This is shared across all four `*-security` and `*-patterns` spokes.49- **Layering** — controller/resource → service → repository; keep persistence types out of the50 web layer. → `springboot-patterns`, `quarkus-patterns`.5152## 3. Version / tooling matrix5354| Concern | Kotlin stack | Spring Boot stack | Quarkus stack |55|---|---|---|---|56| Language | Kotlin (latest stable) | Java 17+ (LTS) | Java 17+ (LTS) |57| Framework | Ktor / KMP | Spring Boot 3.x | Quarkus 3.x LTS |58| DI | Koin | Spring context | CDI (Arc) |59| Persistence | Exposed + HikariCP + Flyway | JPA/Hibernate | Panache (JPA/Hibernate) |60| Test runner | Kotest + MockK | JUnit 5 + Mockito + MockMvc + Testcontainers | JUnit 5 + Mockito + REST Assured |61| Coverage | Kover | JaCoCo | JaCoCo |62| Messaging | — | Spring events / async | Apache Camel |63| Build | Gradle (Kotlin DSL) | Gradle / Maven | Gradle / Maven (+ native) |6465Serialization: kotlinx.serialization (Kotlin/Ktor); Jackson (Spring Boot / Quarkus JSON).6667## 4. The lifecycle every stack shares6869```70patterns ──> TDD ──> security ──> verification71```72731. **Patterns** — architecture + conventions for the chosen framework.742. **TDD** — write tests first (`*-tdd` / `kotlin-testing`); aim for meaningful coverage, not a number.753. **Security** — authn/authz, input validation, CSRF, secrets, headers, dependency scanning (`*-security`).764. **Verification** — build → static analysis → tests+coverage → security scan → (Quarkus: native compile) → diff review before PR/release (`*-verification`).7778## 5. Shared guardrails7980- **One stack per module**: don't mix Spring Boot and Quarkus, or JPA and Exposed, in one service.81- **Tests first**: features/bugfixes go through the `*-tdd` / `kotlin-testing` loop before merge.82- **Security is not optional**: run the matching `*-security` spoke; validate all external input.83- **Gate releases**: nothing ships until the matching `*-verification` loop is green.84- **Kotlin null-safe, Java immutable-by-default**: lean on the language's safety guarantees.85- **Spring Boot vs Quarkus**: choose Spring Boot for ecosystem breadth and team familiarity;86 choose Quarkus for native images, fast startup, low memory, and event-driven/Kubernetes-first87 services. Choose Ktor when you want a lightweight, coroutine-first Kotlin server.