Skip to content

A production-ready Spring Boot multi-module project template built with Kotlin. It follows Hexagonal Architecture (Ports and Adapters) principles and Domain-Driven Design (DDD) patterns to ensure maintainability, testability, and scalability.

Notifications You must be signed in to change notification settings

awakelife93/kotlin-clean-architecture-multimodule

Repository files navigation

Kotlin Clean Architecture Multi-Module

Spring Boot (Kotlin) Clean Hexagonal Architecture - Multi-Module Template

Production-ready | Clean Architecture | Multi-Module | Test-Driven

Original Repository: https://github.com/awakelife93/spring-boot-kotlin-boilerplate

This project is migrated from spring-boot-kotlin-boilerplate to implement Hexagonal Architecture and Multi-Module structure.

A production-ready Spring Boot multi-module project template built with Kotlin. It follows Hexagonal Architecture (Ports and Adapters) principles and Domain-Driven Design (DDD) patterns to ensure maintainability, testability, and scalability.

Architecture Overview

Hexagonal Architecture (Ports & Adapters)

                    ┌─────────────────────────────────────────────┐
                    │          Driving Adapters (Input)           │
                    │   demo-internal-api / demo-external-api     │
                    │                demo-batch                   │
                    └─────────────────────┬───────────────────────┘
                                          │
                             ┌────────────▼────────────┐
                             │      Input Ports        │
                             └────────────┬────────────┘
                                          │
                    ┌─────────────────────▼─────────────────────┐
                    │              Application Core             │
                    │                                           │
                    │   ┌───────────────────────────────────┐   │
                    │   │    demo-application (UseCases)    │   │
                    │   │       • Business Logic            │   │
                    │   │       • Orchestration             │   │
                    │   └─────────────────┬─────────────────┘   │
                    │                     │                     │
                    │   ┌─────────────────▼─────────────────┐   │
                    │   │      demo-domain (Entities)       │   │
                    │   │       • Domain Models             │   │
                    │   │       • Business Rules            │   │
                    │   │       • Port Interfaces           │   │
                    │   └───────────────────────────────────┘   │
                    │                                           │
                    └─────────────────────┬─────────────────────┘
                                          │
                             ┌────────────▼────────────┐
                             │     Output Ports        │
                             └────────────┬────────────┘
                                          │
                    ┌─────────────────────▼─────────────────────┐
                    │         Driven Adapters (Output)          │
                    │           demo-infrastructure             │
                    │   • Database (JPA/PostgreSQL)             │
                    │   • Cache (Redis)                         │
                    │   • Message Queue (Kafka)                 │
                    │   • Email Service                         │
                    └───────────────────────────────────────────┘

Multi-Module Structure

root/
├── demo-core/              # Core utilities, configurations, and test fixtures
├── demo-domain/            # Domain entities, value objects, and Port Interfaces
├── demo-application/       # Use cases with Input/Output Ports
├── demo-infrastructure/    # Adapters for Output Ports (DB, Cache, MQ, etc.)
├── demo-internal-api/      # Adapters for Input Ports (Internal REST API)
├── demo-external-api/      # Adapters for Input Ports (External/Public API)
├── demo-batch/            # Adapters for Input Ports (Batch processing)
├── demo-bootstrap/        # Application bootstrap and main entry point
├── gradle/                # Gradle configuration and version catalogs
│   └── libs.versions.toml  # Centralized dependency version management
├── docker/                # Docker compose configurations
└── monitoring/            # Monitoring configurations (Prometheus, Grafana)

Technology Stack

Core Technologies

  • Language: Kotlin 2.0.21
  • Framework: Spring Boot 3.5.5
  • JVM: Java 21 LTS

Key Libraries & Frameworks

Web & API

  • Spring WebMVC / WebFlux
  • Spring Validation
  • SpringDoc OpenAPI (Swagger UI)
  • Jackson for JSON processing
  • WebClient

Security

  • Spring Security
  • JWT
  • BCrypt password encoding
  • Role-based access control (RBAC)

Database & Persistence

  • Spring Data JPA
  • QueryDSL
  • Flyway
  • PostgreSQL / H2

Caching & Messaging

  • Spring Data Redis
  • Apache Kafka (via Spring Kafka)
  • Event-driven architecture support

Testing

  • JUnit 5 / Kotest
  • MockK / Mockito Kotlin & Mockito Inline
  • Spring Boot Test
  • Testcontainers (Integration testing)
  • Spring MockMvc

Development Tools

  • Ktlint / Detekt (Code quality)
  • Spring Boot DevTools (Hot reload)
  • Gradle 8.10 with Kotlin DSL
  • Gradle Version Catalogs (libs.versions.toml) for centralized dependency management
  • Docker & Docker Compose
  • MailHog / PgAdmin / Kafka UI

Monitoring & Logging

  • Spring Actuator / Micrometer / Prometheus
  • Kotlin Logging / Logback
  • Sentry

Build Configuration

Multi-Module Structure

The root build.gradle.kts manages all subprojects with shared configurations through subprojects block.

Key Features

Hexagonal Architecture Module Dependencies:

// demo-core auto-dependency for all modules (except itself)
if (project.name != "demo-core") {
	api(project(":demo-core"))
}

// Test fixtures sharing for specific modules
val modulesUsingTestFixtures = listOf(
	"demo-application", "demo-infrastructure",
	"demo-internal-api", "demo-external-api",
	"demo-batch", "demo-domain"
)

Test Fixtures Strategy:

  • demo-core provides common test utilities, mock data, and base test classes shared across modules
  • Each listed module uses these fixtures for their specific testing needs:
    • demo-application: Use case testing
    • demo-infrastructure: Repository/adapter testing
    • demo-internal-api: Controller integration testing
    • demo-external-api: API endpoint testing
    • demo-batch: Batch job testing
    • demo-domain: Domain model testing
  • Excluded modules:
    • demo-bootstrap: Main application module (no test fixtures needed)
    • demo-core: Source of test fixtures (doesn't consume itself)

Security Vulnerability Management:

// CVE-2025-48924 fix: Force commons-lang3 version globally
configurations.all {
	resolutionStrategy.eachDependency {
		if (requested.group == "org.apache.commons" && requested.name == "commons-lang3") {
			useVersion("3.18.0")
			because("CVE-2025-48924 - Fix Uncontrolled Recursion vulnerability")
		}
	}
}

Executable vs Library Modules:

val executableModules = listOf("demo-bootstrap")
if (project.name !in executableModules) {
	// Library modules: disable bootJar, enable regular jar
	tasks.named<BootJar>("bootJar") { enabled = false }
	tasks.named<Jar>("jar") { enabled = true }
}

Hexagonal Architecture Implementation

Port & Adapter Examples

Output Port (Domain Layer):

// demo-domain/src/main/kotlin/com/example/demo/user/port/UserPort.kt
interface UserPort : UserCommandPort, UserQueryPort

Output Adapter (Infrastructure Layer):

// demo-infrastructure/src/main/kotlin/com/example/demo/persistence/user/adapter/UserRepositoryAdapter.kt
@Repository
class UserRepositoryAdapter(
	private val jpaRepository: UserJpaRepository,
	private val userMapper: UserMapper
) : UserPort {
	override fun findOneById(userId: Long): User? =
		jpaRepository.findOneById(userId)?.let {
			userMapper.toDomain(it)
		}
	// ... other implementations
}

Input (Use Case - Application Layer):

// demo-application/src/main/kotlin/com/example/demo/user/usecase/CreateUserUseCase.kt
@Component
class CreateUserUseCase(
	private val userService: UserService
) : UseCase<CreateUserInput, UserOutput.AuthenticatedUserOutput> {
	override fun execute(input: CreateUserInput): UserOutput.AuthenticatedUserOutput =
		with(input) {
			userService.registerNewUser(this)
		}
}

Input Adapter (REST Controller):

// demo-internal-api/src/main/kotlin/com/example/demo/user/presentation/UserController.kt
@RestController
@RequestMapping("/api/v1/users")
class UserController(
	private val createUserUseCase: CreateUserUseCase
) {
	@PostMapping
	fun createUser(@RequestBody @Valid createUserRequest: CreateUserRequest): UserResponse {
		val input = UserPresentationMapper.toCreateUserInput(createUserRequest)
		val userOutput = createUserUseCase.execute(input)

		val response = UserPresentationMapper.toCreateUserResponse(userOutput)
		return ResponseEntity.status(HttpStatus.CREATED).body(response)
	}
}

Configuration Management

Application Configuration Structure

The project implements a multi-layered configuration approach that balances modularity with maintainability:

1. Module-Specific Configuration Files

Each module manages its own library-specific configurations:

demo-bootstrap/
└── src/main/resources/
    └── application-bootstrap.yml          # Sentry configuration

demo-internal-api/
└── src/main/resources/
    └── application-internal-api.yml       # SpringDoc/Swagger configuration

demo-external-api/
└── src/main/resources/
    └── application-external-api.yml       # Webhook configuration

demo-infrastructure/
└── src/main/resources/
    └── application-infrastructure.yml     # Actuator/Management configuration

2. Core Configuration (Central Management)

demo-core/src/main/resources/
├── application-common.yml                 # Common settings + imports
├── application-dev.yml                    # Development environment
├── application-prod.yml                   # Production environment
├── application-local.yml                  # Local development
├── application-secret-local.yml           # Local secrets
├── application-secret-dev.yml             # Development secrets
└── application-secret-prod.yml            # Production secrets

3. Configuration Import Strategy

Core configuration imports all module-specific settings:

# demo-core/src/main/resources/application-common.yml
spring:
	config:
		import:
			- "optional:classpath:application-bootstrap.yml"        # Sentry
			- "optional:classpath:application-internal-api.yml"     # SpringDoc
			- "optional:classpath:application-external-api.yml"     # Webhook
			- "optional:classpath:application-infrastructure.yml"   # Management

4. Environment-Specific Overrides

Environment files can override any module setting:

# application-prod.yml
springdoc:
	swagger-ui:
		enabled: false # Override from demo-internal-api module
	api-docs:
		enabled: false

sentry:
	dsn: # Override from demo-bootstrap module (empty for prod)
	logging:
		minimum-event-level: ERROR

webhook:
	slack:
		url: # Override from demo-external-api module (empty for prod)

Note: IDE may show "Cannot resolve configuration property" warnings for cross-module properties. This is expected and can be ignored as the configuration works correctly at runtime.

5. Configuration Loading Order

Spring Boot loads configurations in this priority order:

  1. Environment-specific files (application-prod.yml)
  2. Module-specific files (application-bootstrap.yml)
  3. Common configuration (application-common.yml)

This ensures environment settings always take precedence over module defaults.

Key Features

1. Database Management

2. Spring Batch Configuration

3. Webhook Integration

// Usage examples
webHookProvider.sendAll(
	"Subscription request received from method ${parameter.method?.name}.",
	mutableListOf("Request Body: $body")
)

webHookProvider.sendSlack(
	"Failed to send message to Kafka",
	mutableListOf("Error: ${exception.message}")
)

webHookProvider.sendDiscord(
	"Batch processing completed",
	mutableListOf("Results: $results")
)

4. Email Testing

  • MailHog Integration: Email testing tool with SMTP port 1025
  • Configuration: Settings in application-local.yml and application-secret-local.yml

5. Code Quality Tools

  • Ktlint: Official lint rules, configuration in .editorconfig
    • Report output: build/reports/ktlint
  • Detekt: Static analysis, rules in detekt.yml
    • Report output: build/reports/detekt

6. Testing Strategies

Mockito-based Testing:

Kotest & MockK Testing:

// Example: Bypassing Spring Security in tests
listeners(SecurityListenerFactory())

Then("Call DELETE /api/v1/users/{userId}").config(tags = setOf(SecurityListenerFactory.NonSecurityOption)) {
	// ... test implementation
}

7. Kafka Integration

8. Event-Driven Examples

User Registration Flow:

User Cleanup Flow:

9. Monitoring & Observability

Note: Replace {ip address}:8085 with your actual IP address for proper metrics collection

10. Docker & Infrastructure

Getting Started

Prerequisites

  • Java 21 or higher
  • Docker & Docker Compose (for infrastructure services)
  • Gradle 8.10 (wrapper included)

Quick Start

1. Start Infrastructure Services

cd docker && ./setup.sh

For detailed setup information, see Docker Setup Guide which explains:

  • Network configuration and auto-creation
  • Individual service management
  • Service dependencies and startup order

2. Run the Application

./gradlew :demo-bootstrap:bootRun

Development Commands

Build & Test

# Build all modules
./gradlew build

# Run tests
./gradlew test

# Run specific module tests
./gradlew :demo-application:test

Code Quality

# Run ktlint check
./gradlew ktlintCheck

# Format code with ktlint
./gradlew ktlintFormat

# Run detekt analysis
./gradlew detekt

Service Access URLs

Application Services

Infrastructure Services

  • MailHog (Email Testing): http://localhost:8025
  • PgAdmin (PostgreSQL Management): http://localhost:8088
  • Kafka UI (Kafka Management): http://localhost:9000
  • Redis: localhost:6379 (CLI/Client access)
  • PostgreSQL: localhost:5432 (Database connection)
  • Kafka: localhost:9092 (Broker connection)
  • Zookeeper: localhost:2181 (Coordination service)

Monitoring Services

Author

Hyunwoo Park

About

A production-ready Spring Boot multi-module project template built with Kotlin. It follows Hexagonal Architecture (Ports and Adapters) principles and Domain-Driven Design (DDD) patterns to ensure maintainability, testability, and scalability.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published