--- name: multi-module-maven description: > Use when working in a multi-module Maven project. Covers parent POM conventions, shared dependency management, inter-module rules, build ordering, and Spring Boot 4 modular starter selection. --- # Multi-Module Maven ## Typical Structure ``` my-app/ ├── pom.xml ← Parent POM (packaging = pom) ├── my-app-domain/ ← Pure Java domain — no Spring │ └── pom.xml ├── my-app-application/ ← Use cases — depends on domain │ └── pom.xml ├── my-app-infrastructure/ ← JPA, Redis, HTTP clients │ └── pom.xml └── my-app-web/ ← Spring Boot app, REST — depends on all above └── pom.xml ``` ## Parent POM ```xml com.example my-app 1.0.0-SNAPSHOT pom my-app-domain my-app-application my-app-infrastructure my-app-web org.springframework.boot spring-boot-starter-parent 4.1.0 21 1.6.3 com.example my-app-domain ${project.version} com.example my-app-application ${project.version} org.mapstruct mapstruct ${mapstruct.version} org.apache.maven.plugins maven-compiler-plugin ${java.version} ${java.version} org.projectlombok lombok org.mapstruct mapstruct-processor ${mapstruct.version} ``` ## Child Module POM (domain — no Spring) ```xml com.example my-app 1.0.0-SNAPSHOT my-app-domain org.projectlombok lombok true ``` ## Child Module POM (web — the runnable app) ```xml ... my-app-web com.example my-app-application com.example my-app-infrastructure org.springframework.boot spring-boot-starter-webmvc org.springframework.boot spring-boot-maven-plugin ``` ## Dependency Rules | Module | Can depend on | Cannot depend on | |--------|---------------|------------------| | `domain` | Nothing | Everything | | `application` | `domain` | `infrastructure`, `web` | | `infrastructure` | `domain`, `application` | `web` | | `web` | All modules | — | ## Boot 4 Modular Starters Spring Boot 4 splits the framework into focused modules (`spring-boot-`, root package `org.springframework.boot.`). Pick one starter per technology, per module: - Several starters were renamed: `spring-boot-starter-web` → `spring-boot-starter-webmvc`, `spring-boot-starter-oauth2-client` → `spring-boot-starter-security-oauth2-client` (and the other OAuth2 starters gained the `security-` prefix); `spring-boot-starter-data-jpa` keeps its name - Starters follow `spring-boot-starter-`; test starters follow `spring-boot-starter--test` (no plain `spring-boot-starter-test` needed alongside them) - Flyway/Liquibase no longer arrive transitively — add `spring-boot-starter-flyway` / `spring-boot-starter-liquibase` in the module that owns migrations (usually `infrastructure`) - AOP starter is `spring-boot-starter-aspectj` (renamed from `spring-boot-starter-aop`) - WAR deploys to external Tomcat use `spring-boot-starter-tomcat-runtime` - `spring-boot-starter-classic` / `spring-boot-starter-test-classic` bundle the old monolithic set — transitional only, don't use in new modules - Optional Maven dependencies are excluded from the repackaged jar by default — set `true` on `spring-boot-maven-plugin` if you rely on them ## Gotchas - Agent puts `spring-boot-maven-plugin` in parent POM — only in the runnable module - Agent adds `` in parent instead of `` — adds to all modules' classpath - Agent creates circular dependencies between modules — enforce the dependency direction above - Agent imports Spring in `domain` module — domain must be framework-free - Agent uses `${project.version}` for inter-module versions — correct, but update parent version to update all - Agent adds `spring-boot-starter-web` — renamed `spring-boot-starter-webmvc` in Boot 4 - Agent adds `spring-boot-starter-aop` — renamed `spring-boot-starter-aspectj` in Boot 4 - Agent adds bare `flyway-core`/`liquibase-core` expecting Boot to configure them — use `spring-boot-starter-flyway` / `spring-boot-starter-liquibase` - Agent adds `spring-boot-starter-test` next to `spring-boot-starter-webmvc-test` — the `-test` starters are self-contained in Boot 4 - Agent reaches into auto-configuration classes from shared modules — their members are no longer public API in Boot 4 - Agent overrides Boot-managed dependency versions without a compatibility reason - prefer the Boot BOM defaults