Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

36장. 실제 코드 구조로 보는 헥사고날 아키텍처

35장에서 우리는
헥사고날 아키텍처를 살펴봤다.

  • 도메인은 외부를 모르고
  • 포트는 역할을 정의하며
  • 어댑터는 외부 기술을 담당한다

이제 질문은 이것이다.

이 구조를 실제 코드로는 어떻게 나눌 것인가?


전체 구조

헥사고날 아키텍처는
보통 다음과 같은 디렉토리 구조를 가진다.

src/
 ├─ domain/
 ├─ application/
 ├─ port/
 ├─ adapter/
 │   ├─ in/
 │   └─ out/
 └─ config/

각 영역의 역할


1️⃣ domain/

비즈니스 로직이 존재하는 영역이다.

domain/
 ├─ Order.java
 ├─ OrderStatus.java
 └─ OrderService.java

여기에는:

  • 엔티티
  • 도메인 로직
  • 상태 전이 규칙

이 들어간다.

외부 기술에 대한 코드는 절대 들어가지 않는다


2️⃣ application/

유스케이스를 담당하는 영역이다.

application/
 └─ CreateOrderUseCase.java

역할:

  • 도메인 호출
  • 흐름 제어

3️⃣ port/

도메인이 외부와 통신하기 위한 인터페이스다.

port/
 ├─ in/
 │   └─ CreateOrderCommand.java
 └─ out/
     ├─ OrderRepositoryPort.java
     └─ EventPublisherPort.java

port/in

외부에서 들어오는 요청 정의

  • UseCase 인터페이스
  • Command 객체

port/out

외부로 나가는 요청 정의

  • DB 저장
  • 이벤트 발행
  • 외부 API 호출

4️⃣ adapter/

외부 기술을 구현하는 영역이다.


adapter/in

adapter/in/
 └─ OrderController.java
  • HTTP Controller
  • Kafka Consumer

adapter/out

adapter/out/
 ├─ persistence/
 │   └─ OrderRepositoryImpl.java
 ├─ messaging/
 │   └─ KafkaEventPublisher.java
 └─ outbox/
     └─ OutboxAdapter.java

5️⃣ config/

객체를 연결하는 영역이다.

config/
 └─ AppConfig.java

여기서:

  • 구현체 생성
  • 의존성 주입

흐름을 따라가 보자

주문 생성 요청이 들어오는 경우:

Controller → UseCase → Domain → Port → Adapter → DB

실제 흐름

  1. Controller가 요청을 받는다
  2. UseCase 호출
  3. Domain 로직 실행
  4. RepositoryPort 호출
  5. Adapter가 DB 저장

이벤트 발행 흐름 (Outbox)

Domain → EventPort → OutboxAdapter → DB
  • Domain은 이벤트 발행을 요청만 한다
  • 실제 저장은 Outbox Adapter가 수행

이벤트 소비 흐름 (Inbox)

KafkaConsumer → UseCase → Domain
  • Consumer는 Adapter
  • Domain은 처리만 수행

중요한 규칙

1️⃣ domain은 adapter를 몰라야 한다

domain → adapter (X)
adapter → domain (O)

2️⃣ port를 통해서만 통신한다

domain → port → adapter

3️⃣ 외부 기술은 adapter에만 존재한다

  • DB
  • Kafka
  • Redis
  • API

잘못된 구조 예시

다음과 같은 코드는 피해야 한다.

class OrderService {
    KafkaProducer kafka;
    JdbcTemplate jdbc;
}

문제:

  • 도메인이 외부 기술에 의존

올바른 구조 예시

class OrderService {
    private final OrderRepositoryPort repository;
    private final EventPublisherPort publisher;
}

이 구조의 효과

1️⃣ 코드가 명확해진다

  • 어디에 무엇을 넣을지 명확

2️⃣ 테스트가 쉬워진다

new OrderService(fakeRepository, fakePublisher);

3️⃣ 기술 교체가 쉬워진다

  • Kafka → SQS 변경
  • DB 변경

→ adapter만 수정


실무에서 자주 하는 실수

1️⃣ port 없이 바로 adapter 사용

→ 의존성 다시 깨짐

2️⃣ domain에 framework 코드 넣기

→ 구조 무너짐

3️⃣ application 레이어 생략

→ 흐름이 domain에 몰림


정리

헥사고날 아키텍처는
개념만으로는 부족하다.

실제 코드 구조로 나누어야 의미가 있다


이 장의 핵심

  • 헥사고날은 디렉토리 구조로 구현된다
  • domain, port, adapter로 분리한다
  • 의존성은 항상 domain 방향으로 향한다
  • 외부 기술은 adapter에만 존재해야 한다
  • 구조를 지켜야 아키텍처가 유지된다