Maven multi-module architectures are the standard design pattern for structuring enterprise Java and Kotlin codebases into modular, decoupled, and maintainable systems. By organizing code into parent, aggregator, and child modules, teams enforce architectural layer boundaries, eliminate version duplication, and optimize continuous integration build times through incremental parallel compilation.
This guide details parent vs aggregator POM design, centralized dependency management, Bill of Materials (BOM) patterns, and parallel reactor build optimizations.
+-------------------------------------------------------------------------------+
| PARENT POM vs. AGGREGATOR POM |
+-------------------------------------------------------------------------------+
| Characteristic | Parent POM (Inheritance) | Aggregator POM (Reactor) |
+---------------------+------------------------------+--------------------------+
| XML Element | `<parent>...</parent>` | `<modules><module>...` |
| Purpose | Share dependencies, plugin | Build multiple submodules|
| | management, and properties | in a single `mvn` command|
| Directory Location | Anywhere in repo or remote | Must be at root above |
| | Maven repository artifact | module subdirectories |
| Packaging Type | `<packaging>pom</packaging>` | `<packaging>pom</packaging>`|
| Common Practice | Combined in single root POM | Combined in single root POM|
+---------------------+------------------------------+--------------------------+
Multi-Module Directory Layout:
root-project/ (Aggregator + Parent POM)
├── pom.xml (Central <dependencyManagement>, <pluginManagement>, <modules>)
├── core-api/
│ └── pom.xml (Interface types, zero heavy dependencies)
├── core-service/
│ └── pom.xml (Depends on core-api)
├── web-gateway/
│ └── pom.xml (REST controllers, depends on core-service)
└── integration-tests/
└── pom.xml (Runs full E2E test suites)
The <dependencyManagement> section in the root parent POM defines default dependency versions, exclusions, and scopes centrally across all child modules without forcing submodules to inherit unwanted JARs.
<!-- Root Parent pom.xml -->
<dependencyManagement>
<dependencies>
<!-- Spring Boot BOM Import -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.3.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Internal Module Version Alignment -->
<dependency>
<groupId>com.enterprise</groupId>
<artifactId>core-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
Child modules declare dependencies without version tags, guaranteeing complete version alignment across the entire repository:
<!-- core-service/pom.xml -->
<dependencies>
<dependency>
<groupId>com.enterprise</groupId>
<artifactId>core-api</artifactId>
<!-- Version omitted: cleanly inherited from root parent POM -->
</dependency>
</dependencies>
In continuous integration pipelines, Maven computes a Directed Acyclic Graph (DAG) of inter-module dependencies (the Maven Reactor) and compiles independent modules concurrently.
# Parallel build using 1 thread per CPU core
mvn clean install -T 1C -DskipTests
# Build only the modified module and all submodules that depend on it
mvn test -pl core-service -amd
# Resume a failed build from the failing module without recompiling upstream
mvn install -rf web-gateway