This series documents a practical, production-ready way to structure a Spring Boot (Kotlin) server from scratch. By establishing a solid API server setup once, you can reuse the same architecture for any future project and accelerate development dramatically.
📚 Spring Boot(Kotlin) Server Setup — Series Overview
(A total of 12 articles will be published in this series)
- Why a Multi-Module Architecture? (Architecture Philosophy & Overall Design) ← current article
- Designing a Unified API Response Format
- Global Exception Handling (GlobalExceptionHandler)
- Swagger (OpenAPI) Configuration
- Security (JWT) Core Skeleton
- JWT TokenProvider Implementation
- Redis Configuration
- Validation Setup
- Logging + MDC (traceId)
- application.yml Profile Separation (local/dev/prod)
- Multi-Module + JPA Basic Architecture
- Downloadable Complete Project Template (git)
1. Why Use a Multi-Module Architecture?
When starting a Spring Boot project, most developers structure it like this:
src/main/kotlin
├─ controller
├─ service
├─ repository
├─ domain
This is a single-module architecture. It works fine for small projects, but issues begin to accumulate over time.
⚠️ 1. Problems with a Single-Module Structure
1) Controllers and Services grow endlessly as features expand
Even minor feature additions quickly lead to massive growth:
- Login / Social Login
- Push Notifications
- Schedule Management
- Image Upload
- Search
- Admin Features
- External API Integrations
- Batch Jobs
2) Layered architecture rules break down over time
At first, everything looks clean:
- Controller → Service → Repository
But as the project grows, these anti-patterns appear:
- Controllers calling Repositories directly
- Services making external API calls
- Business logic spread across Controller/Service/Repository
- Returning Entities directly to the outside world
This happens because the structure doesn't enforce boundaries — there’s nothing preventing layers from being mixed together.
3) Passing external input directly into the inner layers is dangerous
Client input is untrusted external data. Yet many single-module projects do something like this:
@PostMapping("/signup")
fun signup(@RequestBody request: SignupRequest) =
userService.signup(request)
The problem is that SignupRequest often flows directly into:
Service → Repository → Entity
Why is that dangerous?
- Changes in external API contracts break internal logic
- Potential security vulnerabilities (unexpected field injection)
- Domain integrity collapses (DTOs influencing business rules)
- Higher maintenance cost (boundaries become unclear)
This is why we need layered mapping (DTO → Service Model → Entity).
In short:
- Request DTO (api layer) — exclusively for receiving client input
- Service Model (application layer) — tailored for business logic
- Entity (domain layer) — pure domain object mapped to DB
A multi-module architecture makes this separation natural and enforceable. It structurally prevents untrusted data from leaking into deeper layers.
4) Batch jobs and API logic start mixing together
API servers must respond rapidly, 24/7. Batch jobs handle heavy background tasks.
If both run in the same server/module:
- A batch failure → API downtime
- Heavy batch operations → slower API responses
- Modifying batch logic → requires restarting the entire API server
- Violates SRP (Single Responsibility Principle)
⚡ Multi-Module Architecture to the Rescue
To solve these problems, this series uses the following structure:
/api → Controllers, Security, Filters, API Request/Response DTOs
/application → Services, TokenProvider, Domain rules, Repository interfaces
/batch → Schedulers, Cron-based tasks
🟦 2. Benefits of a Multi-Module Architecture
🟢 Benefit 1) Clear responsibility separation (SRP)
| Module | Responsibility |
|---|---|
| api | Handles client requests and responses (Web Layer, Controllers, Security, Filters) |
| application | Business logic (Service), Domain rules, TokenProvider, Repository interfaces |
| batch | Scheduled / bulk operations, background processing |
Each module focuses solely on its role. Even as the service grows, the codebase remains clean and maintainable.
🟢 Benefit 2) Layered DTO separation happens naturally
- Client → Request DTO (api)
- Request DTO → Service Model (application)
- Service Model → Entity (domain)
Advantages:
- External spec changes don't corrupt Domain models
- Better security (no hidden fields leaking in)
- Entities remain pure and consistent
- Service layer handles clearly separated input models
By placing Request/Response DTOs in api and domain/service logic in application, we structurally prevent “external data creeping into inner layers”.
🟢 Benefit 3) API and Batch servers run independently
In IntelliJ:
ApiApplicationKt→ API ServerBatchApplicationKt→ Batch Server
They run on separate run configurations. In a production environment, they can be deployed independently as well.
🟢 Benefit 4) Easier testing
- api module: Controller / Security / Filter tests
- application module: Service and business logic tests
- batch module: Scheduler and batch-processing tests
🟢 Benefit 5) Scales easily for large systems
As the system grows, you can introduce more modules:
/api
/application
/batch
/notification
/file
/auth
Each feature can evolve independently, keeping complexity under control.
🟦 3. Module Map for This Series
Here is the module coverage for each article:
| Step | Topic | Related Modules |
|---|---|---|
| 1 | Why a multi-module architecture? | Overview |
| 2 | API Response Format | api / application |
| 3 | Global Exception Handling | api / application |
| 4 | Swagger (OpenAPI) | api |
| 5 | Security (JWT) | api |
| 6 | JWT TokenProvider | application |
| 7 | Redis Configuration | application |
| 8 | Validation Setup | api / application |
| 9 | Logging + MDC(traceId) | api |
| 10 | Profile Separation | all |
| 11 | Multi-Module + JPA Architecture | api / application |
| 12 | Downloadable Template (zip) | all |
🟩 Coming Next
The next article begins with actual implementation.
👉 2. Designing a Unified API Response Format — Why This Must Be Done First
By establishing a consistent response structure first, your exception handling, security, and logging layers will remain cohesive and predictable.
Next:
https://jaemoi8.tistory.com/31
Spring Boot (Kotlin) — ep.2 Designing the API Response Format
📚 Spring Boot (Kotlin) Server Setup — Series OverviewWhy Multi-Module Architecture? (Architecture Philosophy & System Design)Designing the API Response Format ← You are hereGlobal Exception Handling (GlobalExceptionHandler)Swagger (OpenAPI) Configur
jaemoi8.tistory.com