728x90
1. 레이어드 아키텍처 개념
: 애플리케이션의 비즈니스 로직(도메인)과 외부 시스템을 철저히 분리
2. 계층(Layer) 구조
1) 포트(Port)
: 외부와 통신하기 위한 인터페이스
2) 어댑터(Adapter)
: 포트를 구현하는 외부 기술 구체화(포트의 구현체이자, Controller 역할 수)
3) 핵심 도메인(Core Domain)
: 순수 비즈니스 로직, 외부 의존 없음 (레이어드 아키텍처의 도메인과 동일)
com.example
├─ user
│ ├─ domain
│ │ ├─ User.kt
│ │ └─ UserDomainService.kt
│ ├─ Port // Interface 역할 수행
│ │ └─ UserRegistrationPort.kt
│ ├─ adapter
│ │ ├─ in // 입력 어댑터 (Controller, REST API)
│ │ │ └─ UserController.kt
│ │ └─ out // 출력 어댑터 (DB, Kafka 등)
│ │ └─ UserRepositoryAdapter.kt
│ └─ config
└─ common
3. 예제 코드
: 헥사고날 아키텍처 기반으로 “회원 가입 & 쿠폰 발급”로직을 가볍게 구현단하면?
1) 도메인 서비스
package com.example.user.domain
class UserDomainService {
fun canRegister(email: String): Boolean {
return !email.endsWith("@banned.com")
}
}
2) Port(Interface)
package com.example.user.application
import com.example.user.domain.User
interface UserRegistrationPort {
fun saveUser(user: User): User
}
3) Adapter(Out - DB)
package com.example.user.adapter.out
import com.example.user.application.UserRegistrationPort
import com.example.user.domain.User
import org.springframework.stereotype.Component
@Component
class UserRepositoryAdapter(
private val userRepository: UserJpaRepository
): UserRegistrationPort {
override fun saveUser(user: User): User {
return userRepository.save(user.toEntity()).toDomain()
}
}
3) Adapter(In - Controller)
package com.example.user.adapter.`in`
import com.example.user.application.UserRegistrationPort
import com.example.user.domain.User
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/users")
class UserController(
private val userRegistrationPort: UserRegistrationPort
) {
@PostMapping
fun register(@RequestBody request: UserRequest): User {
val user = User(request.email, request.name)
return userRegistrationPort.saveUser(user)
}
}
4. 주요 장점
"도메인 순수성 보장"
: Core Domain은 외부 의존이 없음
"유연한 외부 교체"
: DB → Mongo, REST API → Kafka 등 쉽게 교체 가능
"테스트 용이성"
: Domain은 순수 Kotlin으로 단위 테스트 가능, 외부 의존을 mocking으로 처리
"유스케이스 흐름 명확화"
: Application Service(Port)에서 흐름 관리
5. 주요 단점
"구조 복잡도 증가"
- Layered Architecture보다 패키지와 클래스가 늘어남
- Port/Adapter/Domain/Controller 등 계층이 많아지고, DI 구조도 복잡
- 작은 프로젝트나 단순 CRUD에서는 과한 추상화로 느껴질 수 있음
"학습 곡선"
- 개발자가 Port, Adapter, Domain의 역할을 직관적으로 이해해야 함
(특히 “이게 Application Service인지 Port인지” 헷갈리기 쉬움)
- Layered만 쓰던 개발자가 처음 접하면 혼란 발생
"Boilerplate 코드 증가"
- Interface/Port 작성, Adapter 구현, DTO 매핑 등 반복 코드가 늘어남
- 작은 프로젝트에서 초기 생산성이 떨어질 수 있음
"유스케이스 흐름 표현 제한"
- Port는 Interface이므로 복잡한 유스케이스 흐름을 표현하려면 여러 Port 호출 + Adapter 구현 필요
- Layered에서처럼 Application Service 한 곳에서 흐름 조율하는 것보다 코드가 분산됨
"DI/설정 부담"
- Adapter 주입, Port 구현체 설정 등 스프링 DI 설정이 많아짐
- 잘못 설계하면 순환 참조나 잘못된 의존성 주입 발생 가능
728x90
'Kotlin Spring > Kotlin Spring 강의 내용' 카테고리의 다른 글
6) 개발 architecture (4) EDA(Event-Driven Architecture) 패턴 (1) | 2025.08.31 |
---|---|
6) 개발 architecture (3) 클린 아키텍처(Clean Architecture) (2) | 2025.08.31 |
6) 개발 architecture (1) 레이어드 아키텍처(Layered Architecture) (0) | 2025.08.31 |
5) 요구사항 분석 및 ERD 설계 (0) | 2025.08.31 |
4) Spring @controller, @service, @repository (0) | 2025.08.29 |