Spring Boot facility
Support status
Stable
The Spring Boot facility generates the technical layers for a Spring Boot service: API types and controllers, persistence types and repositories, mappings, service providers, handler skeletons and Flyway schema evolution.
Start a runnable project
The stable Spring Boot Maven Archetype creates the model, generated-backend and manual application modules together with an H2 or PostgreSQL setup.
1. Maven artifact and platform
Add the facility to the model module:
<dependency>
<groupId>org.joinedworkz.facilities</groupId>
<artifactId>spring-boot</artifactId>
<version>${joinedworkz.version}</version>
<scope>provided</scope>
</dependency>The artifact contributes:
- profile package
org.joinedworkz.facilities.profiles.springboot; - platform
SpringBoot; - CMN package
org.joinedworkz.facilities.springboot.apiwith the SpringBoot CRUD method types; and - the SpringBoot cartridge and generator implementations.
SpringBoot specializes Java, which specializes Base. The Maven dependency brings both parent facilities transitively, so their active OpenAPI, diagram and Java DTO generation is inherited.
2. Supported modeling and generation
The SpringBoot platform adds:
- JPA entities and Spring Data repositories for modeled persistent entities;
- advanced JPQL and native SQL repository statements, including reusable statement composition;
- DTOs and MapStruct mappings;
- API interfaces, Spring MVC controllers and handler integration;
- data-access and service-provider code;
- assigned, generated-data-access and JPA-provider ID strategies for entity keys;
- explicit JPA attribute converters for supported persistent
enum<integer>fields; - CRUD method types
createEntity,readEntity,updateEntity,queryEntitiesanddeleteEntity; - first-cut handler implementations that become manual project source after their initial creation;
- Flyway migrations and schema snapshots for modeled persistence components.
The stable documented SpringBoot generation path uses:
PersistenceCartridge;SchemaMigrationCartridge;ServiceProviderCartridge.
The SpringBoot profile also declares IntegrationTestCartridge and the generatedIntegrationTestSource outlet. The cartridge is disabled by default. Enable it explicitly when the project supplies the required test shell, dependencies and Maven test-source configuration:
cartridge.IntegrationTestCartridge.enabled=trueWhen enabled, JoinedWorkz generates a Create-to-Read test only for an eligible flat resource path. The path must provide both crud.create and crud.read; the create operation must consume one complex object and both operations must produce one keyed complex object. Collections, parent-resource path parameters and create bodies with required entity references are outside this generated flow.
An eligible generated test sends the create request, checks its configured success status and returned identifier, then reads that identifier and checks the read status, response body and identifier. Request Content-Type is derived from consumes; Accept is derived from produces.
Unsupported cases are omitted instead of becoming empty green tests. Maven reports a warning with one of these stable codes, and no @Test method is generated for the skipped item:
JW_SPRING_BOOT_IT_UNSUPPORTED_FLOW— the Create-to-Read pair is incomplete or does not meet the supported shape;JW_SPRING_BOOT_IT_UNSUPPORTED_INTENT— an intent such ascrud.update,crud.delete,crud.query,smokeorcontracthas no generated flow.
Keep manually maintained tests for update, delete, query, parent-resource and required-reference behavior.
2.1 Consumer build contract for generated tests
Route the replaceable output into the application module that runs the tests. For example, when joinedworkz.properties belongs to a sibling model module:
outlet.generatedIntegrationTestSource.directory=../application/src/generated/test
cartridge.IntegrationTestCartridge.enabled=trueThe receiving module must register that exact directory as test source and run only the generated class suffix with Failsafe. The following fragments show the relevant parts; keep plugin versions under the receiving project's normal version management:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>add-generated-integration-test-source</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>${project.basedir}/src/generated/test</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.2</version>
<executions>
<execution>
<id>run-generated-joinedworkz-integration-tests</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<failIfNoTests>true</failIfNoTests>
<includes>
<include>**/*GeneratedIT.java</include>
</includes>
<reportsDirectory>
${project.build.directory}/generated-failsafe-reports
</reportsDirectory>
<summaryFile>
${project.build.directory}/generated-failsafe-reports/failsafe-summary.xml
</summaryFile>
</configuration>
</execution>
</executions>
</plugin>Each generated class extends ITBase in its own generated package. Add that base as manual test source in the identical package, for example src/test/java/com/example/customer/it/ITBase.java:
package com.example.customer.it;
public abstract class ITBase {
}Run mvn clean verify in the reactor that contains generation and the receiving application module. A generated-test run writes its XML/text reports and failsafe-summary.xml below target/generated-failsafe-reports; this separate directory distinguishes it from ordinary Surefire output. failIfNoTests also prevents an explicitly configured generated-test execution from succeeding without any selected generated test class.
Disabling the cartridge or changing its outlet does not remove previously generated replaceable files from the former directory. Before interpreting compilation or report results, regenerate from a clean state and, when needed, clean exactly the former effective generatedIntegrationTestSource directory according to the output ownership rules. Do not clean manual source, sibling directories or any other outlet as part of that operation.
3. Public and advanced configuration
platform.springboot.flavor— defaults tolegacy; accepts the case-sensitive valueslegacyandmodernand selects the SpringBoot naming and mapping strategy set.schemaMigration.mode— defaults toSTRICT; acceptsSTRICTorDEVELOPMENT, case-insensitively.java.dto.naming— inherited advanced/stable Java configuration; defaults todecoratedand acceptsdecoratedorplain, case-insensitively.cartridge.IntegrationTestCartridge.enabled— defaults tofalse; set it totrueto opt in to eligible generated Create-to-Read tests.
SpringBoot also inherits applicable Base and Java properties. The complete definitions, diagnostics and precedence are in the joinedworkz.properties reference. For java.dto.naming, SpringBoot's decorated output adds both .dto and the Dto suffix; plain output omits both. See DTO naming and external.
4. Outputs and ownership
- Replaceable Java:
generatedJavaSource,generatedHandlerSourceandgeneratedMappingSource. - Replaceable test source:
generatedIntegrationTestSource; it is populated only when the opt-in integration-test cartridge finds an eligible flow. - Replaceable resources:
generatedConfiguration. - First-cut, then manual:
firstCutJavaSourceandfirstCutHandlerSource. - Stateful schema evolution:
generatedSchemaMigrationandgeneratedSchemaSnapshot.
Most replaceable Java outlets default to src/generated/java; generated resources default to src/generated/resources. First-cut output defaults to src/main/java and does not overwrite an existing file.
For Java output with effective layer api, configure this layer-specific route:
outlet.generatedJavaSource.api.directory=../web/src/generated/javaSchema migration ownership depends on schemaMigration.mode:
STRICTcreates persistent versioned SQL and matching snapshots;DEVELOPMENTcreates replaceable working migration and snapshot files.
Route persistent migration history outside any parent output tree that is cleaned as replaceable. Read Flyway schema migrations before changing these outlets.
5. Minimal model fragment
package com.example.customer
import org.joinedworkz.facilities.profiles.springboot
import org.joinedworkz.facilities.springboot.api
platform SpringBoot
type<entity> Customer {
id**: Id
name*: String
}
resource /customers as Customer[] by id {
createEntity()
readEntity()
queryEntities()
updateEntity()
deleteEntity()
}A component must provide the resource before SpringBoot generates its controller boundary. Add a persists block only when JoinedWorkz should also derive Flyway migration history for a selected set of persistence objects. The Spring Boot CRUD example shows the complete domain, API and component split without Flyway generation.
For several resource trees, nested controller boundaries, component target namespaces, and operation-specific handler delegation, see SpringBoot controller and handler composition.
6. Build and runtime
Register every effective generated Java and resource outlet in the Maven module that consumes it, then run the complete reactor:
mvn clean verifyGeneration alone does not create an entire runnable project. A deployable service also needs:
- a manually owned Spring Boot application shell;
- the Spring Boot, persistence, mapping and database runtime dependencies;
- application and datasource configuration; and
- a compatible database or test environment.
The generated integer-enum converter and data-access service refer to small, replaceable glue contracts. The canonical example uses org.iworkz:genesis-spring:1.0.77 as a convenient reference implementation; it brings genesis-core transitively. This dependency is intended for examples, demos, and prototypes. It is not a mandatory JoinedWorkz application runtime. Production projects can provide compatible implementations and replace both glue package prefixes as described in Facilities and platforms.
When those are present, use the application's normal Spring Boot command, for example:
mvn spring-boot:runThe command belongs in the application module, not necessarily the model module.
7. Boundaries
- Generated Java and resources are replaceable unless their outlet is explicitly first-cut or stateful.
- First-cut files are never a merge target. Once created, they are manual source and later model changes must be integrated deliberately.
- Flyway history must not be deleted by a generic generated-output cleanup.
- The platform flavor changes generated conventions; do not mix generated output from different flavors without a clean regeneration.
- A model using only types and resources is not yet a complete persistence topology. Components determine provided endpoints and persisted packages.
- The documented runtime baseline uses Spring Boot 3 and Jakarta Persistence; this page makes no Spring Boot 2 or
javax.persistencecompatibility claim. - Explicit integer-code persistence is supported for ordinary scalar entity fields. Collections, dictionaries, keys, versions, references, relations, and calculated fields using
enum<integer>are rejected during validation; see Integer-coded enumeration persistence. - Omitting
generationkeeps an entity key assigned.CUSTOMgeneration is part of the generated data-accesscreate(dto)path; direct repository or data-accesssave(...)calls bypass that hook. JPA strategies and their database requirements remain provider-managed. See ID generation strategies. - The schema-migration generator is currently PostgreSQL-oriented.
- Generated integration-test output covers only eligible flat Create-to-Read flows. Use manually maintained runtime tests for the complete CRUD behavior and every skipped case; see Generated output and ownership.
- Generated persistence and mapping code requires the compatible Spring, MapStruct and helper-library dependencies in the application.
- Custom JPQL and native SQL bodies are application statements. CMN preserves their modeling structure, but the selected persistence provider and database remain responsible for accepting and executing the composed query. See Repository statements and composition.
