Reviewed technical note

Spring/JPA에서 도메인 모델 경계가 무너지는 순간

도메인 모델 패턴, 헥사고날 아키텍처, JPA 성능 지식을 블로그 글로 연결하기 위한 첫 기준.

Spring/JPA 애플리케이션에서 도메인 모델은 처음에는 깨끗하다. 문제는 시간이 지나며 컨트롤러, 서비스, 엔티티, 조회 최적화 코드가 서로의 책임을 침범할 때 시작된다.

경계가 무너지는 대표 신호

  • 엔티티가 API 응답 형식을 직접 의식한다.
  • 서비스가 트랜잭션 스크립트처럼 모든 규칙을 순서대로 나열한다.
  • 조회 성능 문제를 해결하려고 도메인 규칙이 리포지토리 쿼리로 이동한다.
  • 테스트가 도메인 규칙이 아니라 컨트롤러 응답 모양에만 붙는다.
  • JPA 지연 로딩, fetch join, OSIV 같은 인프라 선택이 도메인 설계를 끌고 간다.

이 상태에서는 기능은 동작해도 변경이 어려워진다. 새 정책 하나를 추가하기 위해 API 계층, 서비스 계층, 영속성 계층을 동시에 만져야 한다.

도메인 모델과 JPA는 분리해서 생각해야 한다

JPA 엔티티는 도메인 모델을 담을 수 있지만, JPA 자체가 도메인 모델을 만들어주지는 않는다. 도메인 규칙은 언어와 메서드, 불변식으로 표현해야 한다. 영속성 최적화는 그 다음 문제다.

헥사고날 아키텍처가 유용한 이유도 여기에 있다. 포트와 어댑터를 나누면 웹 API, DB, 외부 시스템이 도메인 규칙의 중심으로 들어오는 일을 막을 수 있다.

성능 최적화는 경계 밖에서 시작한다

JPA 성능 문제는 중요하다. 하지만 성능 최적화를 도메인 규칙 안에 섞으면 장기적으로 더 비싼 문제가 된다. 조회 모델, fetch join, batch size, projection은 대부분 읽기 경로의 선택이다. 쓰기 모델의 불변식과 분리해서 다뤄야 한다.

블로그 자동 운영 관점에서는 이 주제가 좋은 시리즈가 된다.

  1. 도메인 모델이 이끄는 개발
  2. 헥사고날 아키텍처와 포트
  3. JPA 조회 성능과 fetch join
  4. OSIV와 트랜잭션 경계
  5. 아키텍처 테스트로 경계 지키기

위키에 이미 Spring/JPA와 아키텍처 자료가 충분하므로, 각 글은 단순 요약이 아니라 “경계가 깨지는 구체적 증상과 복구 방법” 중심으로 작성하는 편이 좋다.