[♻️Refactor] 생명주기(Lifecycle) 기반 JPA 연관관계 매핑 최적화 (Item 엔티티 중심)#142
[♻️Refactor] 생명주기(Lifecycle) 기반 JPA 연관관계 매핑 최적화 (Item 엔티티 중심)#142minsubyun1 wants to merge 1 commit intodevfrom
Conversation
|
해당 PR의 리뷰를 자세히 하기 위해 리뷰가 늦은 점 죄송합니다. DDD에서 애그리거트 경계를 고려하면 하나의 애그리거트 내부에서는 객체 참조를 허용하되 애그리거트 간에는 직접 객체 참조보다 ID 참조를 우선적으로 고려하는 방식이 일반적으로 권장됩니다. 또한 이러한 방식은 향후 모듈 분리나 MSA로 확장할 때도 유리합니다. 애그리거트 간이 객체 연관관계가 아니라 ID 중심으로 연결되어 있으면 서비스 분리 시에도 관계를 비교적 명확하게 유지할 수 있기 때문입니다. 반면 ID 참조를 사용할 경우 DB 차원의 참조 무결성에만 의존하기 어려워지고 잘못된 ID 저장 가능성이나 도메인 검증 누락으로 인한 고아 데이터 발생 가능성도 존재합니다. 따라서 이 경우에는 애플리케이션이 정합성 책임을 더 많이 가져가야 하며 생성/수정 시점의 검증 로직을 더 명확하게 설계해야 합니다. 저는 이러한 장단점을 고려했을 때 결국 핵심은 애그리거트 경계를 어디에 두는가라고 생각합니다. 애그리거트는 하나의 트랜잭션 안에서 원자적으로 변경되어야 하거나 도메인상 항상 함께 유지되어야 하는 규칙을 기준으로 결정해야 합니다. 예를 들어 저희 서비스에서 “물류는 하위 모든 물류 물품이 완료된 후에만 완료 상태가 될 수 있다”는 규칙이 있습니다. 이는 동일한 일관성 경계 안에서 관리되어야 할 불변식 후보라고 볼 수 있습니다. 결과적으로 여러 애그리거트가 함께 참여하는 흐름은 하나의 큰 객체 관계로 묶기보다 애플리케이션 서비스나 오케스트레이션 패턴 등을 통해 협력시키고 애그리거트 내부에서는 객체 관계를 활용해 불변식을 명확하게 표현하는 것이 실수를 줄이고 유지보수성을 높이는 방향이라고 생각합니다. 참고 자료: |
zldzldzz
left a comment
There was a problem hiding this comment.
당시의 작성할 때의 관계에 대해 깊은 지식 없이 관계를 추가했던 것 같습니다. 죄송합니다.
⛓️💥 Issue Number
close [♻️Refactor] 도메인 모델 관계 개선 (@onetomany) #141
🔎 배경 및 문제점
기존 NexERP의 도메인 모델은 다대다 관계를 풀어내기 위해 중간 테이블을 만들어 1:N (
@OneToMany)과 N:1(@ManyToOne) 양방향 관계로 풀어내어, 객체 지향적인 탐색(부모 객체 -> 자식 컬렉션을 직접 조회)과 비즈니스 로직의 직관성을 확보하도록 했습니다. 이는 제가 기존에 ORM JPA 연관관계를 공부하면서 학습했던 방식과 일치하기도 했고, 이론적으로 올바른 형태라고 생각했습니다. 김영한 - 자바 ORM 표준 JPA 프로그래밍그러나, 기술진을 통해 받은 피드백의 주요 내용을 정리하자면
@oneToMany를 거는 것 자체가 잘못된 것은 아니지만, 라이프 사이클적으로 실질적인 이유가 있지 않은 이상,@oneToMany가 걸린 양방향이 많아질수록마스터-트랜잭션 (Item - InventoryItem) 간의 데이터가 폭발하거나, 도메인 간 의존성이 높아져 코드 수정이나 확장이 더 어려울 수 있다는 것이었습니다.생명주기(Lifecycle) 관점의 엔티티 매핑 재평가 및 조치
그래서 연관관계를 맹목적으로 끊기보다, 기존에 있던 관계에 있어서 데이터의 생성/소멸 주기와 마스터/트랜잭션 데이터 여부를 기준으로 수정 타겟/현행 유지/추후 과제 정도로 단계를 나누어 평가를 진행했습니다.
🛠 수정 타겟: Item <-> InventoryItem / LogisticsItem (수정 완료)
@oneToMany가 없이도 사이드 이팩트 없이 코드가 멀쩡하게 돌아갑니다.Item엔티티 내의@OneToMany리스트 매핑을 일괄 삭제하고, 연관된 서비스 계층의 코드들을 모두 테스트 완료했습니다.🔒 현행 유지 01. Inventory <-> InventoryItem
🔒 현행 유지 02. Company (1) <-> (N) Member
🔒 현행 유지 03. Project(1) <-> Member_Project(N)
🔗 추후 과제 Member(1) <-> Member_Project(N) 단방향 전환
평가 기준 (불일치): 직원은 프로젝트 참여 여부와 관계없이 존재하고, 한 명의 직원이 시간이 흐름에 따라 수십, 수백 개의 프로젝트에 참여할 수 있죠. 그래서 Member 엔티티가 오히려 List<Member_Project>를 가지고 있으면 Item 엔티티에서 조치했던 것처럼 메모리 비대화 위험이 어느 정도 있다고 판단했습니다.
추후 조지할 내용: 현재 저희 NexERP 규모로서는 해당 위험이 발생할 가능성이 낮긴 해서 현행을 유지하지만, 추후 조치가 가능하다면, @onetomany 삭제 후 관련된 모든 Repository 쿼리를 MemberProjectRepository에서 직접 하도록 하여 필요한 조회 기능을 유지하는 것이 바람직해 보입니다.
💡 고민한 지점
이론의 정석과 실무적 트레이드오프의 균형
참고 자료를 모두 보아도, 사실 Bad 케이스나 장단이 있을 뿐 정석은 없다고 말씀해 주십니다.
모든 프로젝트의 라이프 사이클이 다를 것이고, 그에 따라 각 도메인에 대한 매핑 평가도 달라질 것입니다. 그래서 JPA 표준 설계 방식과 연관관계 지양 전략 사이에서 처음 고민을 하게 되었습니다. 정석이란 존재하지 않기 때문에 본인이 평가한 엔티티 간 라이프 사이클에 따라 매핑을 하는 것이 바람직하다고 생각하게 됐습니다. 앞으로는 모든 엔티티를 그냥 연결하기 보다, 마스터 데이터와 내역 데이터 또한 엄격히 분리해서 설계하여 유연하면서도 단단한 모델을 구축하면 좋을 것 같습니다.
📸 참고자료
김영한 - 자바 ORM 표준 JPA 프로그래밍
제미니 - JPA 연관관계 어떻게 걸까요?
✅ PR 유형
어떤 변경 사항이 있나요?
✅ PR 체크 리스트