Maven
Spring Boot
Java
Software Development
Dependency Management

Maven module using spring-boot

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A Maven module can absolutely use Spring Boot, but the cleanest design is usually a multi-module build where only one module is the executable application. The other modules hold shared domain code, data access, or integration logic and are consumed as ordinary Maven dependencies.

A Typical Multi-Module Shape

A common layout is:

  • parent module with packaging set to pom
  • library modules for shared code
  • one bootable module containing the main class and Boot plugin
text
1my-app/
2  pom.xml
3  common/
4  service/
5  data/
6  api/

The parent POM aggregates the modules and centralizes version management.

Parent POM

The parent usually imports Spring Boot dependency management and declares the child modules.

xml
1<project>
2  <modelVersion>4.0.0</modelVersion>
3
4  <groupId>com.example</groupId>
5  <artifactId>my-app</artifactId>
6  <version>1.0.0-SNAPSHOT</version>
7  <packaging>pom</packaging>
8
9  <parent>
10    <groupId>org.springframework.boot</groupId>
11    <artifactId>spring-boot-starter-parent</artifactId>
12    <version>3.3.2</version>
13    <relativePath/>
14  </parent>
15
16  <modules>
17    <module>common</module>
18    <module>data</module>
19    <module>service</module>
20    <module>api</module>
21  </modules>
22
23  <properties>
24    <java.version>17</java.version>
25  </properties>
26</project>

This gives child modules consistent dependency versions without repeating them everywhere.

Library Modules Versus the Boot Module

Non-bootable modules should usually be ordinary JARs.

For example, a service module might look like this:

xml
1<project>
2  <parent>
3    <groupId>com.example</groupId>
4    <artifactId>my-app</artifactId>
5    <version>1.0.0-SNAPSHOT</version>
6  </parent>
7
8  <artifactId>service</artifactId>
9
10  <dependencies>
11    <dependency>
12      <groupId>com.example</groupId>
13      <artifactId>data</artifactId>
14      <version>${project.version}</version>
15    </dependency>
16    <dependency>
17      <groupId>org.springframework.boot</groupId>
18      <artifactId>spring-boot-starter</artifactId>
19    </dependency>
20  </dependencies>
21</project>

The bootable module depends on the others and contains the entry point.

xml
1<project>
2  <parent>
3    <groupId>com.example</groupId>
4    <artifactId>my-app</artifactId>
5    <version>1.0.0-SNAPSHOT</version>
6  </parent>
7
8  <artifactId>api</artifactId>
9
10  <dependencies>
11    <dependency>
12      <groupId>com.example</groupId>
13      <artifactId>service</artifactId>
14      <version>${project.version}</version>
15    </dependency>
16    <dependency>
17      <groupId>org.springframework.boot</groupId>
18      <artifactId>spring-boot-starter-web</artifactId>
19    </dependency>
20  </dependencies>
21
22  <build>
23    <plugins>
24      <plugin>
25        <groupId>org.springframework.boot</groupId>
26        <artifactId>spring-boot-maven-plugin</artifactId>
27      </plugin>
28    </plugins>
29  </build>
30</project>

Put the main Class in the Bootable Module

The executable module should contain the @SpringBootApplication class.

java
1package com.example.api;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6@SpringBootApplication(scanBasePackages = "com.example")
7public class MyApplication {
8    public static void main(String[] args) {
9        SpringApplication.run(MyApplication.class, args);
10    }
11}

Using a root package that covers all modules often removes the need for explicit scanning configuration.

Build and Run

Build from the parent directory so Maven resolves module dependencies in one reactor build.

bash
mvn clean package
java -jar api/target/api-1.0.0-SNAPSHOT.jar

You can also run just the boot module once the reactor build has installed or built the dependencies.

bash
mvn -pl api spring-boot:run

Why Only One Module Usually Gets the Boot Plugin

The Spring Boot Maven plugin repackages a module into an executable archive. That is perfect for the application module and usually wrong for shared libraries.

If every module is repackaged as a boot jar, you often get awkward artifacts and unnecessary complexity. Keep library modules as normal jars unless you have a specific reason not to.

Keep Dependency Direction Clean

A good multi-module Boot build usually points inward toward stable code. For example, api can depend on service, and service can depend on data, but data should not depend back on api.

That keeps modules reusable and prevents circular dependencies in Maven and in Spring configuration. If two modules need shared DTOs or utilities, move that code into a neutral module such as common instead of making them depend on each other directly.

This structure also makes testing easier. Library modules can be tested without starting the whole Boot application, while the executable module focuses on wiring and integration.

Common Pitfalls

A common mistake is putting the Boot plugin in every module. Most modules in a multi-module project are libraries, not applications.

Another mistake is building a child module in isolation before its sibling modules are available. Reactor builds from the parent avoid this problem.

Developers also often place the @SpringBootApplication class in a narrow package, which prevents component scanning from reaching beans in sibling modules.

Summary

  • A Maven module can use Spring Boot as either a library or the executable app module.
  • In multi-module builds, the parent POM should aggregate modules and manage versions.
  • Usually only one module contains the Boot main class and spring-boot-maven-plugin.
  • Shared modules should stay as ordinary jars.
  • Build from the parent so Maven resolves the entire module graph correctly.

Course illustration
Course illustration

All Rights Reserved.