Requirements
Create a Maven archetype named java-service-archetype that generates a
structured multi-module Java service project. In addition to the standard Maven
coordinates (groupId, artifactId, version), prompt the user for the
properties below, then produce the module structure described in this document.
Prompts
appName
Prompt for a brief, human-readable application name: capitalized words
separated by spaces (e.g. Order Service). No default — every project needs
its own name. Used throughout generated content in three forms:
- Class names: spaces removed (e.g.
OrderService). - Generated property values: spaces removed and lowercased (e.g.
orderservice— used forspring.application.name). - Prose (POM
<name>/<description>, Javadoc): unmodified.
package — base package
Prompt for the base Java package for the application (e.g. com.example.myapp).
All module sub-packages are derived from this value as described in
Source Structure.
integrations
Prompt for a comma-separated list of type:name pairs, one per external data
source or system (e.g. database:users,rest:orders,graphql:catalog).
- Abbreviate the
databasetype todbin all generated module and package names; use all other types as-is. - Normalize all type and name values to lowercase.
- Silently skip malformed entries (missing colon, blank type or name).
- No default — leave blank to generate a project with no integration modules.
serviceAreas (default: empty)
Prompt for a comma-separated list of service area names
(e.g. orders,inventory,notifications). Generate a service-{name} module for
each non-blank name. Leave blank to generate a single module named service;
if no non-blank names are provided, fall back to the same single service module.
presentationTypes (default: rest)
Prompt for a comma-separated list of presentation-tier types
(e.g. rest,graphql).
- Normalize all type values to lowercase.
- Leave blank (or provide only blank entries) to generate a project with no presentation modules.
includeSpring (default: true)
Whether to generate the Spring Boot wiring.
true(default): theparent/POM inheritsspring-boot-starter-parent, every app-composed module carries a@Configurationclass, and Spring Boot starters are assigned per module by role —common-domaincarries the corespring-boot-starterthat the plaindomain-*and service modules inherit transitively. Theappmodule carries theApplicationclass and is always a monitorable web service — core starter,spring-boot-starter-webmvc,spring-boot-starter-actuator,spring-boot-admin-starter-client, and anAppServletInitializer(WAR deployment), regardless ofpresentationTypes— plusspring-boot-starter-data-jpaand@EnableTransactionManagementwhen adatabaseintegration is present.acceptance-testsadditionally gets shared REST/GraphQL acceptance-test base classes (seeacceptance-tests).false: omit all Spring artifacts — configs, classes, and POM items — producing a plain multi-module Java project with the same module structure.
Module Structure
Generate all modules as siblings of a parent/ directory.
Do not create a pom.xml at the project root.
parent/
<packaging>pom</packaging><name>${appName} Parent</name>;<description>${appName}'s parent POM.</description>- List every sibling module in
<modules>using../modulerelative paths, sorted alphabetically. - List every sibling module in
<dependencyManagement>pinned to${project.version}, sorted alphabetically byartifactId.acceptance-testsis managed here like every other module, but is never referenced as an actual<dependency>by any other module. - Inherit
spring-boot-starter-parent(version 4.1.0) as the parent POM — via<parent>with an empty<relativePath/>, not a BOM import — so Spring artifact versions and sensible plugin defaults are managed centrally. datasource-proxy-spring-boot-starterandspring-boot-admin-starter-clientare third-party artifacts outside the Spring Boot BOM, so each gets its owndependencyManagemententry with an explicit version pinned via a property (datasource-proxy-spring-boot-starter.version,spring-boot-admin-starter-client.version).- Properties: Java 21 via
maven.compiler.release; UTF-8 source encoding viaproject.build.sourceEncoding. <pluginManagement>— declare pinned versions formaven-compiler-plugin,maven-failsafe-plugin,maven-jar-plugin(configured withskipIfEmpty=true),maven-surefire-plugin,modernizer-maven-plugin(bound to theverifyphase withjavaVersionset to${java.version}), andversions-maven-plugin(rule set ignores alpha/beta/milestone/RC versions).<plugins>— activatemodernizer-maven-pluginso every module is checked for outdated API usage duringverify.- Generate two convenience scripts in
parent/—mvn-update-properties-versions.sh(Linux/macOS, LF line endings, executable bit set) andmvn-update-properties-versions.ps1(Windows, CRLF line endings) — that wrapmvn versions:update-propertiesto bump every dependency and plugin version property to its latest stable release.
Child module POMs
Every module except parent/ must declare parent/pom.xml as its parent
using <relativePath>../parent/pom.xml</relativePath>.
common-testing
Shared test infrastructure. <name>${appName} Common Testing</name>;
<description>${appName}'s common testing.</description>.
- Dependencies:
common-domainand every other domain module (domain-{type}-{name},domain-service/domain-service-{name},domain-{type}presentation domain modules). No domain module depends back oncommon-testing— onlyservice/service-{name}does — so this stays acyclic. - If
includeSpring=true:spring-boot-starter-testat compile scope (test-support code other modules compile their tests against); additionallyspring-boot-starter-graphql-testat compile scope when agraphqlpresentation is present (needed forGraphQlAcceptanceTestBase, below). - If
includeSpring=trueand arestpresentation is present: generateRestAcceptanceTestBase(@SpringBootTest(webEnvironment = RANDOM_PORT), exposing aRestTestClientbound to the running server via@LocalServerPort). - If
includeSpring=trueand agraphqlpresentation is present: generateGraphQlAcceptanceTestBase(@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureHttpGraphQlTester, exposing an injectedHttpGraphQlTester).
Per integration: domain-{type}-{name} and integration-{type}-{name}
For each type:name integration entry, generate two modules
(e.g. domain-db-users and integration-db-users):
domain-{type}-{name}— domain classes for this data source. Dependency:common-domain.integration-{type}-{name}— implementation classes (DAOs, repositories, clients, etc.). Dependencies:common-domain,domain-{type}-{name}. Configuremaven-failsafe-pluginwith theintegration-testandverifygoals. IfincludeSpring=trueand the type isdatabase: also adddatasource-proxy-spring-boot-starter(SQL logging).
Per service area: service or service-{name}
Business logic for a service area. <name>${appName} Service[ {Area}]</name>;
<description>${appName}'s[ {area}] service tier (business logic).</description>
(the area name is folded in — trailing for the <name>, mid-sentence for the
description — only for named areas; the single default service module omits it).
Dependencies: common-domain and the area’s own domain-service /
domain-service-{name} module. If includeSpring=true: also common-testing
(test scope).
For each service area, also generate a domain-service (single service) or
domain-service-{name} module holding that area’s domain classes.
<name>${appName} Domain Service[ {Area}]</name>;
<description>${appName}'s[ {area}] service tier domain classes.</description>.
Dependency: common-domain. If includeSpring=true: also spring-boot-starter-validation
(no domain module depends on common-testing).
Per presentation tier: domain-{type} and presentation-{type}
For each presentation type, generate two modules
(e.g. domain-rest and presentation-rest):
domain-{type}— domain classes for this presentation tier (DTOs, etc.), which map to/from the service-tier domain objects ("the service domain deps"). Dependencies:common-domain, everydomain-service/domain-service-{name}module. IfincludeSpring=true: alsospring-boot-starter-validation.<name>${appName} Domain {TYPE}</name>;<description>${appName}'s {TYPE} presentation tier domain classes.</description>({TYPE} isREST/GraphQL/capitalized-otherwise).presentation-{type}— request-handling classes (controllers, etc.), or resolver classes forgraphql. Dependencies:common-domain,domain-{type}, every service module.presentation-graphqladditionally gets<name>${appName} Presentation GraphQL</name>;<description>${appName}'s GraphQL presentation tier.</description>(no dedicated requirement yet for other presentation types'presentation-{type}pom name/description).
app
Assembles the runnable artifact (e.g. Spring Boot uber jar).
<name>${appName} App</name>; <description>${appName}'s application configuration
including building its deployment assembly.</description>.
Depend on all generated modules except acceptance-tests, common-testing,
and app itself.
If includeSpring=true, app is always a monitorable web service regardless of
presentationTypes: it adds the core spring-boot-starter, spring-boot-starter-webmvc,
spring-boot-starter-actuator, and spring-boot-admin-starter-client (actuator needs a
web server to expose its endpoints over HTTP), and always generates an
AppServletInitializer (WAR deployment) class. A database integration additionally
adds spring-boot-starter-data-jpa and @EnableTransactionManagement on Application.
Contains the Spring Boot Application (@SpringBootApplication) class in the .config
sub-package, which @Import`s the `@Configuration class that every app-composed module
carries in its own .config sub-package — each referenced by simple name via its own
import statement (not inline fully-qualified names).
Also creates application.properties in src/main/resources (actuator endpoints
exposed, health details shown, git/env/process info contributors, tracing, Spring Boot
Admin client registration, logging defaults, plus JPA and SQL logging via datasource-proxy
when a database integration is present; spring.application.name is appName in its
generated-property form, e.g. orderservice)
plus six empty per-environment files: application-ci.properties, application-dev.properties,
application-local.properties, application-prod.properties, application-qa.properties,
application-uat.properties.
acceptance-tests
Functional tests that exercise the application end-to-end.
<name>${appName} Acceptance Tests</name>; <description>${appName}'s acceptance
tests.</description>.
- Dependencies:
app,common-domain, alldomain-{type}-{name}modules, alldomain-service/domain-service-{name}modules, alldomain-{type}(presentation) modules (all labeled "this app" in the pom). Do not depend on service (business-logic) modules or integration implementation modules. - Also depends on
common-testing(test scope) unconditionally, and — ifincludeSpring=trueand adatabaseintegration is present —datasource-proxy-spring-boot-starter(SQL logging). - If
includeSpring=trueand agraphqlpresentation is present: alsoorg.springframework.graphql:spring-graphql-test(test scope). - Configure
maven-failsafe-pluginwith theintegration-testandverifygoals. - If
includeSpring=true: generate{appNameClass}AcceptanceTestConfiguration(@Configuration, package{base.package}.at.config) andsrc/test/resources/logback-spring.xml(includes Boot’s base logback config; exists to keep this module’s Spring runtime from picking up `app’s logging configuration).- If a
restpresentation is present: generateAppRestAcceptanceTestBase(package{base.package}.at), extendingcommon-testing’s `RestAcceptanceTestBase,@ContextConfiguration’d with the AT configuration class plus every generated module’s `@Configurationclass (reachable transitively via theappdependency), and — only when adatabaseintegration is present —@EnableAutoConfigurationexcludingDataSourceAutoConfiguration/XADataSourceAutoConfiguration. - If a
graphqlpresentation is present: generateAppGraphQlAcceptanceTestBaseanalogously, extendingGraphQlAcceptanceTestBase.
- If a
Source Structure
Each module’s source root is the package derived from the base package prompt.
Place a package-info.java in each module’s primary leaf package under
src/main/java only — never under src/test/java, since Java rejects two
package-info.java files compiled for the same package (src/test/java packages are
still scaffolded as empty directories for test classes to land in). acceptance-tests
is the one module whose test package holds real test content (the actual *AT
acceptance-test classes), so its lone src/main/java package-info Javadoc describes
"Functional acceptance tests for the application." rather than supporting infrastructure.
Each app-composed module additionally carries a @Configuration class in a .config
sub-package. The package for each module is:
| Module | Package |
|---|---|
common-domain |
${base.package}.common.domain |
common-testing |
${base.package}.common.testing |
domain-{type}-{name} |
${base.package}.domain.{type}.{name} |
integration-{type}-{name} |
${base.package}.integration.{type}.{name} |
service |
${base.package}.service |
service-{name} |
${base.package}.service.{name} |
domain-service |
${base.package}.domain.service |
domain-service-{name} |
${base.package}.domain.service.{name} |
domain-{type} |
${base.package}.domain.{type} |
presentation-{type} |
${base.package}.presentation.{type} |
app |
${base.package}.app |
acceptance-tests |
${base.package}.at |

