
단순 기능만 보면 회원/상품/주문 CRUD 시스템이다.
그런데 이걸 그냥 CRUD 테이블 구조로 만들면 이후 확장에 바로 한계가 온다.
핵심은 “주문 도메인”이 중심이라는 것이다.
→ 즉, 이 시스템은 단순 CRUD가 아니라
상태 변화와 비즈니스 규칙이 존재하는 도메인 모델이다.
처음 보면 주문과 상품은 다대다다.
하지만 JPA에서 @ManyToMany는 거의 사용하지 않는다.
왜?
그래서 우리는 이렇게 푼다.
Order 1 : N OrderItem N : 1 Item
OrderItem이 단순 중간 테이블이 아니라
비즈니스 개념이 있는 엔티티가 된다.
이 설계 하나로 시스템 확장성이 완전히 달라진다.
상품은 도서/음반/영화로 나뉜다.
공통 속성:
→ 공통 부모 Item
JPA 상속 전략을 사용해 하나의 테이블에 DTYPE으로 구분했다.
실무에서는 상속을 무조건 쓰기보다
구조 복잡성 vs 유지보수성을 비교해야 한다.
이건 JPA에서 정말 중요한 개념이다.
연관관계의 주인은
비즈니스 주인이 아니라
외래키를 가진 쪽이다.
Ex.
왜냐하면
외래키를 가지지 않은 쪽을 주인으로 잡으면
불필요한 UPDATE 쿼리가 추가로 발생한다.
이건 단순 문법 문제가 아니라
SQL 발생 구조와 직결된 설계 문제다.
Setter가 열려 있으면
엔티티 상태 변경 지점이 무한대로 늘어난다.
→ 도메인 로직이 흩어진다.
→ 유지보수 난이도 급상승
그래서
위주로 설계하는 해야한다.
⸻
기본 전략:
@ManyToOne(fetch = LAZY)
왜?
• EAGER는 예측이 안 된다.
• JPQL 사용 시 N+1 지뢰가 된다.
• 운영 중 쿼리 튀어나오는 순간 디버깅 난이도 상승
실무에서의 원칙은:
“기본은 LAZY, 필요하면 fetch join”
⸻
private List<OrderItem> orderItems = new ArrayList<>();
이건 단순 취향 문제가 아니다.
엔티티 설계에서 이런 디테일이 쌓여서
코드 안정성이 만들어진다.
controller → service → repository → domain
이 구조는 단순하지만
트랜잭션 경계와 도메인 책임 분리가 명확하다.
이 예제는 단순 CRUD 예제가 아니라
출처
스프링 부트와 JPA 활용 (김영한, 인프런, 2019)