본문 바로가기
개발 (ENG)

Spring Boot(Kotlin) — ep.1 Why You Should Use a Multi-Module Architecture in Spring Boot (Kotlin)

by 새싹 아빠 2025. 11. 21.

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)

  1. Why a Multi-Module Architecture? (Architecture Philosophy & Overall Design) ← current article
  2. Designing a Unified API Response Format
  3. Global Exception Handling (GlobalExceptionHandler)
  4. Swagger (OpenAPI) Configuration
  5. Security (JWT) Core Skeleton
  6. JWT TokenProvider Implementation
  7. Redis Configuration
  8. Validation Setup
  9. Logging + MDC (traceId)
  10. application.yml Profile Separation (local/dev/prod)
  11. Multi-Module + JPA Basic Architecture
  12. 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 Server
  • BatchApplicationKt → 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