소프트웨어 아키텍처

헥사고날과 클린 아키텍처

meellon 2026. 6. 23. 06:00

학습 목표

헥사고날 아키텍처(Ports and Adapters) 에서 포트·어댑터·애플리케이션 코어 역할을 설명할 수 있다.

클린 아키텍처의존성 규칙(안쪽으로만 의존)을 설명할 수 있다.

의존성 역전(DIP) 이 DB·UI에 도메인이 끌려가지 않게 만드는 원리임을 설명할 수 있다.

레이어드도메인 중심 구조의 차이·트레이드오프를 비교할 수 있다.

문제 상황

  • 레이어드로 나눴는데 Service가 JPA Entity에 묶여 있다
    • DB를 Mongo로 바꾸려면 비즈니스 코드까지 뒤집힌다
  • 단위 테스트마다 DB를 띄워야 한다
    • Repository 구현이 도메인 테스트 전제가 됨
  • OrderControllerOrderServiceOrderRepository는 맞는데 인터페이스가 없다
    • 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