학습 목표
헥사고날 아키텍처(Ports and Adapters) 에서 포트·어댑터·애플리케이션 코어 역할을 설명할 수 있다.
클린 아키텍처의 의존성 규칙(안쪽으로만 의존)을 설명할 수 있다.
의존성 역전(DIP) 이 DB·UI에 도메인이 끌려가지 않게 만드는 원리임을 설명할 수 있다.
레이어드와 도메인 중심 구조의 차이·트레이드오프를 비교할 수 있다.
문제 상황
- 레이어드로 나눴는데 Service가 JPA Entity에 묶여 있다
- DB를 Mongo로 바꾸려면 비즈니스 코드까지 뒤집힌다
- 단위 테스트마다 DB를 띄워야 한다
- Repository 구현이 도메인 테스트 전제가 됨
OrderController→OrderService→OrderRepository는 맞는데 인터페이스가 없다- mock 없이 통합 테스트만 가능
- "클린 아키텍처 적용" 했더니 DTO·Mapper·Interface 파일만 늘었다
- 도메인 규칙은 여전히 Service 한 줄 전달
레이어드는 폴더 분리를 줬다. 이번엔 의존 방향을 뒤집어 도메인·유스케이스를 중심에 두는 스타일이다.
1. 왜 도메인 중심인가
레이어드는 이름이 기술 계층(Presentation / Business / Persistence)에 맞춰져 있다.
| 관점 | 레이어드 | 헥사고날·클린 |
|---|---|---|
| 중심 | 계층(종종 DB·UI) | 도메인·유스케이스 |
| 외부 | 위·아래 계층 | 플러그인 가능한 어댑터 |
| 교체 대상 | 한 계층 전체 | DB·UI·메시지 등 인프라 단위 |
| 테스트 | 계층 통합에 기대기 쉬움 | 포트 mock으로 코어 단독 테스트 |
- 목표: 비즈니스 규칙이 프레임워크·DB·HTTP에 의존하지 않게 한다
- Richards/Ford — 헥사고날은 아키텍처 스타일 중 하나로, 파이프라인·모듈러와 함께 자주 언급
2. 헥사고날 — 포트와 어댑터
헥사고날 아키텍처(Alistair Cockburn, Ports and Adapters)는 애플리케이션 코어를 가운데 두고, 외부 세계를 어댑터로 연결한다.

| 요소 | 역할 | 예 |
|---|---|---|
| Application Core | 도메인 모델 + 유스케이스 | Order, PlaceOrderService |
| Port (inbound) | 외부가 코어를 호출하는 API | PlaceOrderUseCase 인터페이스 |
| Port (outbound) | 코어가 외부를 호출하는 API | OrderRepository 인터페이스 |
| Adapter (primary) | 사용자·시스템 입력 구현 | REST Controller, CLI, Consumer |
| Adapter (secondary) | DB·메시지·외부 API 출력 구현 | JPA Repository, Kafka producer |
[REST Adapter] ──calls──> [Inbound Port / Use Case] ──> [Domain]
│
└──calls──> [Outbound Port]
▲
[JPA Adapter] implements
- Primary (driving) — 코어를 움직이는 쪽 (HTTP 요청, 배치 트리거)
- Secondary (driven) — 코어가 사용하는 쪽 (저장소, 결제 게이트웨이)
- 포트는 인터페이스, 어댑터는 구현 — 코어는 JPA·Spring MVC를 import하지 않음
3. 클린 아키텍처 — 링과 의존성 규칙
클린 아키텍처(Robert C. Martin)는 동일한 의도를 동심원으로 표현한다.

| 링 (안 → 밖) | 내용 | 예 |
|---|---|---|
| Entities | 핵심 비즈니스 규칙 | Money, OrderLine |
| Use Cases | 애플리케이션 규칙·오케스트레이션 | CancelOrder, RefundPayment |
| Interface Adapters | 포맷 변환·MVC·게이트웨이 | Controller, Presenter, Repository impl |
| Frameworks & Drivers | DB, Web, UI 프레임워크 | Spring, PostgreSQL, React |
의존성 규칙 (Dependency Rule)
- 소스 코드 의존은 항상 안쪽을 향한다
- 바깥 링은 안쪽을 알지만, 안쪽은 바깥을 모른다
- 경계를 넘을 때 인터페이스(DIP) 로 연결
Frameworks → Interface Adapters → Use Cases → Entities
(outer) (controllers, (app (domain
repo impl) rules) rules)
- 헥사고날의 코어 ≈ Entities + Use Cases
- 헥사고날의 어댑터 ≈ Interface Adapters + Frameworks
4. 의존성 역전 (DIP)
DIP — 고수준 모듈이 저수준 구현에 의존하지 않고, 둘 다 추상화(포트) 에 의존한다.
// outbound port — domain / application package
public interface OrderRepository {
Optional<Order> findById(OrderId id);
void save(Order order);
}
// use case depends on port, not JPA
public class CancelOrderService {
private final OrderRepository orders;
// ...
}
// secondary adapter — infrastructure package
@Repository
class JpaOrderRepository implements OrderRepository { }
| 레이어드 흔한 실패 | DIP 적용 후 |
|---|---|
Service가 EntityManager 직접 사용 |
OrderRepository 포트만 주입 |
Domain이 @Column 어노테이션 |
Domain은 순수 POJO |
테스트에 @DataJpaTest 필수 |
In-memory fake repository |
- 패키지 구조 예:
domain·application·adapter.in.web·adapter.out.persistence- import 방향이 안쪽만 참조하는지 ArchUnit으로 검증 가능
5. 레이어드와 비교

| 레이어드 | 헥사고날·클린 | |
|---|---|---|
| 경계 단위 | 수평 계층 | 코어 vs 어댑터 |
| 의존 방향 | 위 → 아래 (이상적) | 밖 → 안 (포트 경유) |
| DB 위치 | 맨 아래 계층 | 바깥 어댑터 |
| 적합 | 익숙한 CRUD, 빠른 출시 | 규칙 복잡, 인프라 교체·테스트 중요 |
| 비용 | 상대적으로 단순 | 인터페이스·매핑 증가 |
- 둘은 배타적이 아님 — 레이어드 안의 Presentation을 어댑터로, Business를 유스케이스로 재해석할 수 있음
- 문제는 이름만 레이어드이고 의존이 DB 쪽으로 쏠린 경우 — 클린/헥사고날은 그 역전을 강제하는 규칙
6. 흔한 실패와 실무 팁
| 패턴 | 증상 | 대응 |
|---|---|---|
| Anemic + ports | 인터페이스만 있고 도메인에 행동 없음 | Entity에 규칙, Service는 orchestration만 |
| Mapper 지옥 | DTO 5종·Mapper 5개 | 경계 최소화, 동일 모델 허용 구간 정하기 |
| 과도한 추상화 | CRUD에 UseCase 클래스 20개 | 유스케이스는 진짜 규칙 있는 곳만 |
| 패키지 by layer만 | service에 domain+infra 혼재 |
by feature 또는 hexagonal package |
| 양방향 의존 | domain이 adapter DTO import | 의존성 규칙·ArchUnit |
adapter.in.web → application, domain
adapter.out.persistence → application, domain (port impl only)
application → domain
domain → (nothing outer)
- 모놀리스 안에서도 헥사고날 적용 가능 — MSA는 다음 스타일 편에서 서비스 분해 단위로 이어짐
7. 정리
- 헥사고날 — 코어 + 포트(인터페이스) + 어댑터(구현), primary/secondary 구분
- 클린 아키텍처 — Entities → Use Cases → Adapters → Frameworks, 안쪽으로만 의존
- DIP — DB·UI가 도메인을 끌고 가지 않게 하는 핵심 원칙
- 레이어드 대비 도메인·테스트·교체에 유리, 보일러플레이트 비용 있음
- 규모·복잡도에 맞게 적용 — 형식만 클린인 구조는 피한다
다음에 다룰 것
- 마이크로서비스 아키텍처
- 서비스 분해, API Gateway, BFF, 장애 격리
해당 내용은 Fundamentals of Software Architecture (Mark Richards, Neal Ford), Clean Architecture (Robert C. Martin) 의 내용을 기반으로 합니다.
'소프트웨어 아키텍처' 카테고리의 다른 글
| 이벤트 기반 아키텍처 (0) | 2026.06.25 |
|---|---|
| 마이크로서비스 아키텍처 (0) | 2026.06.24 |
| 레이어드 아키텍처 (0) | 2026.06.22 |
| 아키텍처 품질 속성 (0) | 2026.06.21 |
| 이해관계자와 Concern (0) | 2026.06.20 |