Post

[헥사고날 아키텍처] Day 5: 점진적 전환 - 기존 Spring 서비스를 안전하게 바꾸기

[헥사고날 아키텍처] Day 5: 점진적 전환 - 기존 Spring 서비스를 안전하게 바꾸기

이 글은 AI(Claude)의 도움을 받아 작성하고, 작성자가 검토·편집했습니다.

서론: 전체 재작성은 아키텍처 전략이 아니다

기존 Service와 JPA Entity가 얽힌 시스템을 한 번에 헥사고날 구조로 바꾸면 오랜 기간 기능 개발이 멈추고 회귀 위험이 커진다. 가장 많이 변하고 테스트가 어려운 업무 흐름 하나를 골라 경계를 만들고, 새 기능부터 그 경계를 따르게 하는 방식이 현실적이다.

1. 전환 대상 선택

1
2
3
4
5
좋은 첫 후보:
  규칙이 복잡하고 변경이 잦음
  외부 API/DB 결합으로 테스트가 어려움
  입출력과 성공 기준이 명확
  다른 기능과 데이터 경계를 나눌 수 있음

가장 거대한 핵심 모듈보다 작은 성공을 증명할 수 있는 유스케이스가 낫다.

2. 현재 행동을 고정하기

1
2
3
Characterization Test:
  현재 입력에 현재 시스템이 내는 결과를 기록
  이상해 보여도 우선 회귀 기준으로 확보

기존 버그를 새 구조에서 고칠지, 호환을 위해 유지할지 별도 결정한다. 리팩터링과 정책 변경을 한 배포에 섞지 않는다.

3. Port를 기존 코드 앞에 세우기

1
새 Use Case → Outbound Port ← Legacy Adapter → 기존 Service/Repository

처음부터 JPA 모델을 모두 분리하지 않아도 된다. Legacy Adapter가 기존 구현을 감싸고, 안쪽은 새 계약에만 의존하게 한다. 이후 Adapter 내부를 단계적으로 교체한다.

4. 기능 단위 패키지와 모듈

1
2
3
4
5
6
7
8
9
10
order/
  domain
  application
  adapter/in/web
  adapter/out/persistence

payment/
  domain
  application
  adapter/...

기능 모듈 사이 호출은 공개 Use Case/이벤트를 통하게 하고 다른 모듈의 Repository를 직접 참조하지 않는다. 하나의 배포 단위여도 내부 경계가 명확한 모듈러 모놀리스가 된다.

5. DB 경계는 가장 늦게 분리될 수 있다

코드 모듈을 나눴다고 즉시 데이터베이스도 분리할 필요는 없다. 우선 테이블 소유권과 쓰기 책임을 한 모듈로 정한다.

1
2
order 모듈만 orders 테이블 쓰기
다른 모듈은 order Query Port 또는 이벤트 사용

공유 DB 안에서도 소유권을 지킨 뒤 필요할 때 스키마·DB 분리를 검토한다.

6. 성공 지표

1
2
3
4
5
  핵심 규칙 테스트 시간이 줄었는가
  변경 시 수정 모듈 수가 줄었는가
  외부 Adapter 교체가 Use Case에 영향을 덜 주는가
  장애 범위와 배포 회귀가 줄었는가
  새 개발자가 업무 흐름을 찾기 쉬운가

인터페이스 수와 패키지 수를 성공 지표로 삼지 않는다.

7. 시리즈 종합 체크리스트

  1. 의존성을 Domain과 Use Case 방향으로 역전했다. (Day 1)
  2. Use Case는 조율하고 Domain은 불변식을 지키게 했다. (Day 2)
  3. Web·DB·메시지·외부 API를 Adapter로 번역했다. (Day 3)
  4. Domain·Port·Adapter별 테스트와 아키텍처 규칙을 갖췄다. (Day 4)
  5. Legacy Adapter와 기능 모듈로 한 유스케이스씩 점진 전환했다. (Day 5)

시리즈 마무리

헥사고날 아키텍처는 육각형 그림이나 인터페이스의 개수가 아니다. 업무 규칙이 기술 선택보다 오래 살아남도록 의존성 방향을 관리하는 방법이다. 변화와 테스트 비용이 큰 경계부터 적용하고, 효과를 지표로 확인해야 한다. 구조는 목적이 아니라 더 안전하게 변경하기 위한 수단이다.

This post is licensed under CC BY 4.0 by the author.