I created this as a Maven multi-module Spring Boot setup in IntelliJ, with one runnable module and clear layer separation.
Actual module structure I created
SpringBootMultiModule/
pom.xml (parent, packaging=pom)
entity/
dto/
repository/
services/
controller/
entity: JPA entities (User.java)dto: transfer objects (UserDto.java)repository: Spring Data JPA repository (UserRepository.java)services: business layer (UserService,UserServiceImpl)controller: runnable Spring Boot app + REST (MultiModuleApplication,UserController)
Step 1: Create parent Maven project in IntelliJ
- Create a Maven project.
- Set parent packaging to
pom. - Use Spring Boot parent in root POM.
In my root pom.xml:
spring-boot-starter-parentversion:3.4.4java.version:24lombok.version:1.18.38- modules:
entity,dto,repository,services,controller
I also kept internal modules in dependencyManagement so versions are centralized.
Step 2: Create child modules and connect to parent
For each module (entity, dto, repository, services, controller):
- Create Maven module under parent.
- Add parent reference:
<parent>
<groupId>com.example</groupId>
<artifactId>springboot-multimodule</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
- Keep module-specific dependencies only.
Step 3: Build dependency chain module-by-module
I wired dependencies like this:
entity->spring-boot-starter-data-jpadto->lombokrepository-> depends onentity+spring-boot-starter-data-jpaservices-> depends onentity,dto,repository, andspring-boot-startercontroller-> depends onservices,dto, andspring-boot-starter-web
This keeps REST concerns out of business and persistence modules.
Step 4: Keep only controller module runnable
My runnable app class is:
controller/src/main/java/com/example/multimodule/MultiModuleApplication.java
And only controller/pom.xml contains spring-boot-maven-plugin.
So the startup responsibility is isolated in one module.
Step 5: Add data + endpoint flow
From my project:
- Entity:
entity/.../User.java - DTO:
dto/.../UserDto.java - Repository:
repository/.../UserRepository.java - Service interface:
services/.../UserService.java - Service implementation:
services/.../impl/UserServiceImpl.java - Controller endpoint:
controller/.../controller/UserController.java
This gave me a clean CRUD path: Controller -> Service -> Repository -> Entity.
Step 6: Configure runtime in controller module
In controller module I used:
spring-boot-starter-web- H2 runtime dependency
application.propertiesfor local run settings
That made local testing quick with in-memory DB.
Step 7: Build and run commands I used
From project root:
./mvnw clean verify
./mvnw spring-boot:run -pl controller
After startup:
- API base:
http://localhost:8080/api/users - H2 console:
http://localhost:8080/h2-console
Step 8: IntelliJ workflow that worked well
- Import root
pom.xmlas Maven project. - Let IntelliJ auto-detect modules.
- Use Maven tool window to reload after each POM change.
- Run
MultiModuleApplicationfromcontrollermodule.
Important notes from this setup
- Keep module boundaries strict, especially around controller vs service vs repository.
- Keep one runnable module (
controller) to avoid startup confusion. - Put shared version configuration in parent POM.
- If using Java 24 + Lombok, ensure compiler/plugin config is consistent across modules.
Final takeaway
For this project, multi-module was not just about splitting folders. It made responsibilities explicit:
- web in
controller - business in
services - data access in
repository - models in
entityanddto
That structure made the codebase easier to evolve and reason about.
Repository
GitHub: https://github.com/arkomandal/spring-boot-multimodule