Spring Boot is one of the most popular Java frameworks for building scalable web applications. But writing good code isn’t enough—structuring your project properly is equally crucial. The right folder/package structure:
- Enhances code readability and maintainability
- Helps teams scale and collaborate
- Aligns with domain modeling and microservices architecture
In this blog, we’ll explore the most commonly adopted folder structures across the industry, with examples, pros & cons, and when to use what.
🧱 1. Standard Layered Architecture
👤 Best For: Monoliths, small-to-mid web apps
This is the most traditional structure used by most teams starting with Spring Boot.
com.example.myapp
├── controller // REST Controllers
├── service // Business logic
├── repository // Spring Data Repositories
├── model // JPA Entities or POJOs
├── dto // Request/Response DTOs
├── config // Spring Boot Configs
├── exception // Custom exceptions, handlers
├── mapper // MapStruct or manual mappers
└── MyAppApplication.java
✅ Pros
- Easy to learn and organize
- Aligned with typical 3-tier architecture
❌ Cons
- Can get bloated in large projects
- Tightly couples layers
🧠 Example
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public UserDto getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
}
🧩 2. Feature-Based Modular Structure (Vertical Slice)
🚀 Best For: Medium to large modular apps
Organized by feature instead of layers. Ideal when teams work on distinct modules.
com.example.myapp
├── features
│ ├── user
│ │ ├── controller
│ │ ├── service
│ │ ├── repository
│ │ ├── dto
│ │ ├── model
│ │ └── mapper
│ └── product
│ ├── controller
│ ├── service
│ ├── repository
│ └── dto
├── config
├── exception
└── MyAppApplication.java
✅ Pros
- Scales well with multiple teams
- Each feature is self-contained
❌ Cons
- Slight learning curve
- Shared logic must be carefully managed
🧱 3. Hexagonal Architecture (Ports & Adapters / Clean Architecture)
🏛️ Best For: Domain-Driven, long-lived enterprise systems
Separates domain, application logic, and infrastructure clearly.
com.example.myapp
├── domain
│ ├── model
│ └── service
├── application
│ └── usecases
├── infrastructure
│ ├── persistence
│ └── rest
├── web
│ └── controller
├── config
└── MyAppApplication.java
✅ Pros
- Testable and decoupled
- Clean separation of concerns
❌ Cons
- More boilerplate and upfront design
- Not suited for trivial apps
🔌 Example:
public interface UserRepository {
Optional<User> findById(Long id);
}
Implemented by an adapter in infrastructure.persistence
.
🧳 4. Multi-Module Maven Structure
🧬 Best For: Microservices, shared modules
Each feature is an independent module, often maintained in different repos.
project-root/
├── common/ // Shared DTOs, utils
│ └── src/main/java/...
├── user-service/
│ └── src/main/java/com/example/user
├── product-service/
│ └── src/main/java/com/example/product
└── pom.xml // Parent POM
✅ Pros
- Clear modular boundaries
- Reusable components
❌ Cons
- Maven complexity
- Shared versioning can be tricky
🌊 5. Reactive Structure (WebFlux / Handler-Based)
⚛️ Best For: Reactive, async applications
Uses functional routing over MVC annotations.
com.example.myapp
├── handler // Functional endpoint handlers
├── router // RouterFunction configs
├── service
├── repository
├── model
└── MyAppApplication.java
Example Router
@Bean
public RouterFunction<ServerResponse> route(UserHandler handler) {
return RouterFunctions
.route(GET("/users/{id}"), handler::getUser);
}
✅ Pros
- Non-blocking, high throughput
- Lightweight functional style
❌ Cons
- Functional style is less familiar
- Debugging stack traces is harder
⚔️ 6. CQRS-Oriented Structure
🧠 Best For: High-scale systems, Event Sourcing
Separates read and write logic.
com.example.myapp
├── command
│ ├── handler
│ ├── service
│ ├── controller
├── query
│ ├── service
│ ├── controller
├── events
│ └── listeners
└── MyAppApplication.java
✅ Pros
- Optimized for scale
- Clear boundaries
❌ Cons
- Higher complexity
- Eventual consistency challenges
🕸️ 7. GraphQL-Driven Structure
🔍 Best For: GraphQL-first APIs
com.example.myapp
├── graphql
│ ├── resolver
│ ├── schema
│ ├── dto
│ └── service
├── repository
└── MyAppApplication.java
✅ Pros
- Logical schema-based grouping
- Supports GraphQL-first development
❌ Cons
- Requires GraphQL learning
- Less RESTful structure
📊 Comparison Table
Structure Type | Use Case | Pros | Cons |
---|---|---|---|
Layered | Monoliths | Easy, traditional | Bloated in large apps |
Feature-Based | Modular, team-split projects | Scalable, modular | Shared code management |
Hexagonal | Clean DDD applications | Decoupled, testable | Design-heavy |
Multi-Module | Microservices, shared libs | Isolated, reusable | Maven/Gradle complexity |
Reactive | WebFlux-based async apps | Lightweight, fast | Less IDE-friendly |
CQRS | Event-based systems | High throughput | Complex to maintain |
GraphQL | GraphQL-first BFF or APIs | Schema-aligned | Requires schema tooling |
🛠️ Which Structure Should You Use?
Your Need | Recommended Structure |
---|---|
Building a quick CRUD API | Layered |
Multi-team, large-scale product | Feature-based |
Long-term investment, strong domain logic | Hexagonal / Clean |
Need shared libraries or services | Multi-module |
Reactive WebSockets / streaming | Reactive |
Split reads and writes cleanly | CQRS |
GraphQL-first application | GraphQL-based |
📁 Final Thoughts
There’s no one-size-fits-all. The right structure depends on your:
- Team size
- Codebase complexity
- Domain modeling
- Delivery expectations
Start simple, and evolve your structure as the system grows.
🧰 Bonus: Generate Feature Skeleton with Spring Boot CLI
spring init --dependencies=web,data-jpa --build=maven --package-name=com.example.user user-service
Want a GitHub Starter Template?
👉 Drop a comment or message, and I’ll share a starter repo matching your preferred structure—layered, modular, reactive, or clean architecture.