핵심 한 줄

DDD는 “무엇을(도메인 모델) 어떻게 나눌까”의 전략, 헥사고날은 “그 도메인을 외부(DB·API·UI)로부터 어떻게 격리할까”의 구조다. 둘은 경쟁 관계가 아니라 DDD가 그린 경계를 헥사고날이 코드로 강제하는 보완 관계다.

이 노트의 성격

핵심 개념을 새로 정리한 노트다. 결제 취소 MSA 설계 경험과 연결해 보강하면 좋다.


1. DDD (Domain-Driven Design)

전략적 설계 (Strategic)

  • 유비쿼터스 언어: 도메인 전문가와 개발자가 같은 용어 사용 (코드·대화·문서 일치)
  • 바운디드 컨텍스트: 같은 단어도 맥락마다 의미가 다름 → 경계로 분리 (예: “상품”이 주문 맥락 vs 재고 맥락에서 다른 모델)
  • 컨텍스트 맵: 컨텍스트 간 관계 정의 (MSA 서비스 경계와 직결 → 결제 취소 MSA의 4개 서비스 분리)

전술적 설계 (Tactical)

빌딩블록역할식별 기준
Entity식별자로 구분, 생명주기 보유ID로 동일성 판단
Value Object값으로 구분, 불변속성이 같으면 같음 (예: Money, Address)
Aggregate일관성 경계, 트랜잭션 단위Root를 통해서만 접근
Domain Service엔티티에 안 맞는 도메인 로직여러 애그리거트에 걸친 규칙
Repository애그리거트 저장/조회 추상화영속성 기술 은닉

애그리거트 = 트랜잭션 경계

결제 취소 같은 정합성 핵심 도메인에서 애그리거트 단위를 잘 잡으면 동시성 제어·일관성이 자연스럽게 풀린다. 너무 크면 락 경합, 너무 작으면 정합성 깨짐 → 트레이드오프.


2. 헥사고날 아키텍처 (Ports & Adapters)

핵심 발상

도메인 로직은 바깥세상(DB, 웹, 메시지큐)을 몰라야 한다. 의존성은 항상 바깥→안쪽으로만 흐른다.

[웹 컨트롤러]──┐                          ┌──[JPA 어댑터]
[gRPC]────────┼─▶ (Inbound Port)    (Outbound Port) ◀─┼──[Kafka 어댑터]
[배치]────────┘        │   [도메인 + 유스케이스]   │      └──[외부 API 어댑터]
                       └────────── 코어 ──────────┘
              어댑터(주도하는 쪽)        어댑터(주도되는 쪽)

포트 / 어댑터

  • Inbound Port (Driving): 유스케이스 인터페이스. 외부가 코어를 호출하는 진입점
  • Outbound Port (Driven): 코어가 외부에 요구하는 인터페이스 (예: PaymentRepository)
  • Adapter: 포트의 구현체. 기술 의존(JPA, Kafka, REST)은 전부 여기에 격리

의존성 역전 (DIP)

  • 코어가 Repository 인터페이스를 정의 → JPA 어댑터가 구현
  • 결과: DB를 PostgreSQL→다른 것으로 바꿔도 도메인 코드는 불변. 테스트 시 in-memory 어댑터로 교체 가능

3. DDD ↔ 헥사고날 연결

DDD 개념헥사고날 위치
애그리거트·엔티티·VO코어(도메인) 내부
Domain Service코어 내부
유스케이스(애플리케이션 서비스)Inbound Port
Repository 인터페이스Outbound Port
Repository 구현(JPA 등)Adapter
바운디드 컨텍스트헥사곤 1개 = 컨텍스트 1개 (MSA 서비스 경계)

핵심: DDD는 경계를 설계하고, 헥사고날은 그 경계를 의존성 규칙으로 코드에 강제한다.


4. 실무 트레이드오프 (한계)

  • 오버엔지니어링 위험: 단순 CRUD에 헥사고날 풀세트는 보일러플레이트만 늘린다. 도메인 복잡도가 높을 때만 효과
  • 매핑 비용: 도메인 모델 ↔ 영속성 엔티티 ↔ DTO 변환 코드 증가
  • 러닝커브: 팀 전체가 유비쿼터스 언어·계층 규칙을 공유해야 효과
  • 적용 판단: 결제·정산처럼 규칙이 복잡하고 정합성이 중요한 도메인에 우선 적용, 조회 위주 영역은 단순하게

5. 다음 행동

  • 결제 취소 MSA를 헥사고날로 재설계한 다이어그램 작성 → 포트폴리오 보강
  • 애그리거트 경계와 트랜잭션 경계가 일치했는지 회고
  • NestJS/Spring에서 포트-어댑터 디렉터리 구조 예시 코드 정리

관련 노트