Setup Java Spring Boot Modules

This documentation was generated with the assistance of AI. Please report any inaccuracies.

Turns a reactor of empty modules into a Spring Boot service skeleton that compiles, starts, and demonstrates the hexagonal pattern well enough that the first real feature has an obvious shape to follow.

Purpose

The language and build tool come from springboot-stack.yml: project.language (java or kotlin, absent means java) and project.buildTool (maven or gradle, absent means maven; gradle only appears with Kotlin). The Java path is unchanged. With Kotlin the same layering, ports, configuration files and migrations are written, but into src/main/kotlin and src/test/kotlin, with Kotlin idioms: data classes and @JvmInline value class for value objects, a regular class for the aggregate root, hand-written extension-function mappers instead of MapStruct (no Lombok, no kapt/KSP), constructor-bound @ConfigurationProperties data classes with @RefreshScope @Validated, use cases wired as @Bean`s in `boot/config so application keeps no Spring dependency, a runApplication entry point, suspend fun/Flow for reactive stacks, KDoc, and JUnit 5 with MockK. When iru-kotlin-springboot-code-one-task is installed, its reference/ files are treated as the authoritative rules for the generated Kotlin.

iru-setup-java-springboot-modules writes one worked example per adapter kind, not a set of empty files: a single aggregate, its repository port, one adapter implementing that port against the chosen database, one use case, one endpoint. The reasoning is explicit in the skill — a developer’s first task is to rename or delete the example, and both are easier than inventing the layering from an empty tree.

The module it treats as load-bearing is domain. Because domain is the one module every other module may depend on, every capability an outer module provides is declared there as an interface (a port): repository ports, an event publisher, one client port per downstream service, a metrics publisher, a configuration provider, and a small domain-exception hierarchy that the API modules map to HTTP or gRPC status codes. domain carries no Spring annotations at all — the enforcer rule from iru-setup-java-springboot-pom makes that a build failure.

It also writes the runtime configuration: application.yml plus local/dev/prod profiles, with structured JSON logging for deployed profiles, actuator probes and Prometheus exposure on a separate management port, graceful shutdown, and virtual threads enabled when the stack is blocking on Java 21+. Every deployed profile uses placeholders only — no credential, host, or token is written into a committed file, and no default that would accidentally work against something real.

Finally it writes an ArchUnit test in `boot’s test sources. The enforcer rules catch violations at module granularity; ArchUnit catches them at package granularity, which is where they actually creep in.

The skill announces whether the run is scaffolding from scratch or updating an existing source tree (listing the modules that already have sources). Hand-written .java code is never overwritten, whatever the options say. For each generated artifact it owns that already exists (the application*.yml profile files, the ArchUnit test, each engine’s migration directory, the worked example) it warns and asks Update (Recommended) / Keep as is / Replace unless on-existing pre-answers. Replace is only offered for wholly generated files (profile YAML, the ArchUnit test), never for a class containing hand-written logic.

Plugin

Shipped by the iru-springboot plugin of the ai-catalog marketplace (install with /plugin install iru-springboot@ai-catalog; see Plugins).

Invocation

/iru-setup-java-springboot-modules

Inputs

Argument Required Description Default

stack-file

No

args key: value line giving the manifest path. Read in full: the base package, the concurrency model (which decides every method signature — plain values, or Mono/Flux), and which modules exist.

springboot-stack.yml at the repository root

mode

No

args key: value line, new or existing. Informational only: it never means "update silently"; pre-existing artifacts are still asked about.

None

on-existing

No

args key: value line, ask, keep, update or replace, pre-answering the per-artifact Update / Keep as is / Replace question for scripted runs.

ask

An existing build

Yes

Maven: a root pom.xml plus a pom.xml in every module the manifest names. Gradle (Kotlin): settings.gradle.kts including every module, a build.gradle.kts per module, build-logic/ and gradle/libs.versions.toml. If they’re missing the skill stops and points the user at /iru-setup-java-springboot-pom — it writes sources into modules, it doesn’t create modules.

None — a precondition, not a default

Existing hand-written sources

No

Any .java or .kt file already containing code the skill didn’t generate is skipped, never overwritten (even with on-existing: replace), and reported.

Skip and report

Outputs

  • The package layout under src/main/java (or src/main/kotlin for Kotlin), src/main/resources, and the matching test source root for every module.

  • domain — the aggregate, value objects, every port interface with Javadoc describing its contract, and the domain-exception hierarchy.

  • application — one use case class per use case, with a Mockito unit test that mocks every port.

  • infrastructure/database/<engine> — persistence entity, Spring Data repository, MapStruct mapper (Kotlin: extension-function mappers), the port adapter, the migration/changelog directory for that engine’s tooling (Flamingock change class, Liquibase changelog including a pgvector extension changeset where relevant, or a Flyway script), and an integration test.

  • infrastructure/configuration — validated @ConfigurationProperties beans annotated @RefreshScope, the configuration port adapter, and the wiring for the dynamic-configuration mechanism the manifest names.

  • infrastructure/clients/<name> — a port adapter over the generated client, with MapStruct mapping, transport failures translated to domain exceptions, explicit connect/read timeouts, and its base URL bound from clients.<name>.base-url with no default in any deployed profile. That binding is what lets the integration harness redirect the client to the compose stack’s API mock; an adapter that hardcodes a host cannot be tested against anything and will reach a real downstream service from CI.

  • infrastructure/producers and api/consumers — the Kafka publisher adapter and consumer beans, AVRO mappers, bindings, consumer group, and a dead-letter topic with a bounded retry policy. Every destination and group chosen here is reported so it can be reconciled with the AsyncAPI specification Setup Java Spring Boot APIs writes in apis/messaging/: a binding whose topic appears as no AsyncAPI channel is a topic nobody outside the repository can discover.

  • infrastructure/metrics — the metrics port implemented over Micrometer, with a constants class (Kotlin: an object of `const val`s) for every metric name and tag key, and bounded tag cardinality documented in the Javadoc.

  • api/graphql-server — @Controller classes implementing the generated resolver interfaces, mappers, a DataFetcherExceptionResolver (GraphQL answers 200 OK with an errors array, so exception mapping is entirely the application’s job), and query depth/complexity limits — without which one deeply nested query can exhaust the service. The worked field resolver is written batched (@BatchMapping/DataLoader) rather than N+1, because the scaffolded shape is the one that gets copied.

  • api/rest-server / api/grpc-server — controllers or services implementing the generated interfaces, mappers, the exception-to-status advice (RFC 9457 ProblemDetail for REST), and the security filter chain if enabled.

  • boot — the @SpringBootApplication class (Kotlin: plus UseCaseConfiguration declaring the use cases as @Bean`s), `application.yml and the three profile files, composition-root @Configuration, a context smoke test, and the ArchUnit architecture test.

  • A report stating the language and build tool used, whether the tree was scaffolded from scratch or updated, the action taken per pre-existing artifact (created / updated / replaced / kept as is) with any gaps left in kept files, plus the configuration-property list, the metrics list, and the exception-to-status mapping formatted for pasting into the documentation tables.

Execution flow

flowchart TD A["Start /iru-setup-java-springboot-modules"] --> B["Step 0: Read the manifest and args;\nresolve language and build tool;\nverify the Maven or Gradle build exists"] B -- no build --> STOP["Stop — run /iru-setup-java-springboot-pom first"] B --> K0{"language kotlin?"} K0 -- yes --> K1["Apply the Kotlin substitutions\nto Steps 1-11"] K0 -- no --> B2 K1 --> B2["Announce from scratch vs update;\nper existing generated artifact ask\nUpdate / Keep as is / Replace\n(or apply on-existing)"] B2 --> C["Step 1: Create the package layout\nfor every module"] C --> D["Step 2: domain — model, ports, exceptions\n(written first; no Spring annotations)"] D --> E["Step 3: application — use cases\nplus a mocked unit test"] E --> F["Steps 4-9: one adapter per infrastructure/api module\n(database + migrations, configuration, clients,\nKafka producers/consumers, metrics, REST/gRPC/GraphQL)"] F --> G["Step 10: boot — application class,\napplication.yml and local/dev/prod profiles,\ncomposition-root config, smoke test"] G --> H["Step 11: ArchUnit test enforcing\nthe package-level dependency direction"] H --> I["Step 12: mvn clean test or ./gradlew clean test\nvia iru-gate-runner"] I -- ArchUnit fails --> J["Fix the code, not the rule\n(unless the rule is wrong for this project)"] J --> I I --> K["Step 13: Report actions per artifact, ports,\nconfig properties, metrics, status mapping,\nand warnings"]

mvn verify (or ./gradlew integrationTest) is deliberately not run here: the *IT tests need the Testcontainers harness that Setup Java Spring Boot Testcontainers sets up in the next step, so integration tests failing at this point is expected rather than a defect.

Dependencies

Invokes

None. It reads springboot-stack.yml and the existing poms or Gradle build files, and writes sources and configuration. For Kotlin it reads the reference/ files of iru-kotlin-springboot-code-one-task as style rules when that skill is installed, but does not invoke it.

Invoked by

Also runs standalone (/iru-setup-java-springboot-modules) against an existing reactor.

  • iru-gate-runner — spawned for Step 12’s mvn clean test or ./gradlew clean test, so a multi-module compile-and-test log is summarized rather than dumped into the caller’s context.

Source

SKILL.md on GitHub — the file this page was generated from.