JPA-DDD 1 프로젝트 시작하기 - 1
시작하기전에
DDD / JPA기반의 프로젝트를 처음 도입하며 고민한 내용을 모아보았습니다.
모든 예제를 자세하게 다루지는 않습니다.
구체적인 예시보다는 방향성을 중점으로 봐주시면 감사하겠습니다.
같이 작업한 전OO님과 많은 의논을하며 공부했습니다. 감사합니다
의존성
1 | dependencies { |
DOMAIN 모델링 - 방향설정하기 ★★★★
- 방향을 잘못정하면 관계가
복잡해진다
-> DDD의 의도와 맞지않음 - 책임 관계가 모호해진다
- ….
- 자세한 권한설정및 방향은 뒤에서 다룸.
예제
공장
에서 만든 제품을 매장
에 공급
한다고 가정
방향설정 정책 정하기
1. 핵심이 되는 도메인이 무엇인지 설정하기 -> root 에그리거트로
- root 에그리거트가 책임을 가지는 영역을 정한다
- root -> 하위도메인 방향으로만 제어할것
- 하위에서는 key만 가지고가기(1:n)
- one to one 에는 논란이있음 -> 뒤에서 설명함
2. root 에그리거트끼리의 참조
- root 에그리거트끼리는 서로 참조하지 않는것을 원칙으로한다. (
알고있다
는 의존관계를 최소화) - 참조가 반드시 필요하다고 생각된다면 방향성도 단방향으로 고려한다
- 다른 에그리거트에서의 참조 권한은 readonly를 고려한다
3. root 에그리거트끼리의 관계(행위)와 이 관계에 의존된 도메인들
- 관계,행위라고하면 아래 예시에서 Supplement에 해당한다.
- 관계를 매핑하는 entity (행위) -> root쪽으로 방향을가지되,
readonly
를 권장한다. - insert/update 가
영속성
으로 관리되면안된다
. service transaction으로 제어해야한다
4. MSA를 고려하자
- 추후 각 root 에그리거트들은 하나의module로서 하나의 appliation으로 동작할수있음을 유의하자
- 서로의 연관관계는 가능한
약
결합으로 한다
도메인 나누기
만약에 관계를 나타내는 도메인에도, 하위 도메인이 있다면?
네이밍 변경을고려. 공장에서 상점에 공급한다는 의미로 변경. MarketFactory -> Supplement
완성된 에그리거트 영역
여담 - DDD에서 package 구조
대부분의 spring 기반의 프로젝트는 아래와 같은 구조를 하고있을것
1 | |-- controller |
하지만 페키지 구조를 만드는 방법은 매우 다양
DDD의 레이어별로 나누는 방법
1 | |-- presentation |
그리고 도메인 내부에서 레이어를 나누는 방법도 있다
1 | |-- order |
무엇이 정답이다 라고 말할수는 없는것 같다
도메인과 서비스의 크기에따라, 구성원들의 합의에 다라 달라질것이다.
제가 담당하는 서비스의 페키지구조는 아래와 같습니다
1 | |-- order |
( 참조 : https://stylishc.tistory.com/144 )
JPA 에서 단방향 관계 맺기 ★★★
단방향과 양방향
db에서의 연관관계란 (rdb 기준)
- join을위해서 테이블과 테이블이 key를 통해 참조하는것
- 양쪽에 공통적으로 가지고 있는 key가 존재, 2개 테이블의 데이터로만 본다면 방향성이 있을까?
- 결과적으로 데이터가 나왔을때도
방향
이라는것이 정해져있지 않고, join기준에 따라서 쿼리가 바뀌며, 이에따라 기준이 바뀐다고 볼 수 있음.
jpa에서 연관관계
- DDD(객체지향)에서의 연관관계란
권한
과책임
을 가진다. - 권한 / 책임에 따라서 로직과 디자인에 영향을 준다.
- 방향성에따라 복잡도 및 side effect가 발생할 수 있다.
개발편의성
모든 로직을 root 에그리거트를 기준으로 작성하는 경우 불편해질수 있습니다
예를들면 Car
에 대한 접근을 하려면
- MarketRepository를 통해 Market에 대한 정보를 가지고온 다음
- 하위 도메인인 Department에게
Car
조회를 해달라고 위임합니다. - Department는
Car
의 정보를Market
에 전달하고 그 데이터를 활용합니다.
하위도메인에 대한 접근이 매우 잦아진다면 그 플로우가 복잡해 질 수 있습니다.
그런경우 그 도메인을 root도메인으로 승격시키는것을 고려해볼수도 있습니다.
권한
- 양방향 관계를 가진다는것은 하위도메인에서 상위도메인의 데이터를 조회/변경을 할 수 있다는 의미이다. 그리고 변경을 하고나서 save가 이루어지는 경우 상위 도메인의 내용이 변경 될 수 있음을 의미한다
- 사실상 상위/하위 관계가 없어지는것과 같다고 생각한다.
이문서에서는
모든 관계는 단방향기준으로 공유드립니다
기본적으로 양방향 관계는 가능한 지양하겠습니다.
1:1 - @OneToOne
이번예제에서는
- 자식이 부모키와 동일한것을 사용하는경우,
- 자식이 부모키를 fk로 가지고있는 경우
2가지로 분류하어 서술합니다.
parent
1 |
|
child
한쪽에서만 key를 소유하고있어서 단방향으로 관계를 맺음
1 |
|
or
1 |
|
N:1 & 1:N - @ManyToOne & @OneToMany
1:N 관계에서는 N쪽에서(자식쪽) 부모의 key를 가집니다.
parent
1 |
|
child
1 |
|
관계 annotation들(@OneToOne등)의 주요 options
크게 2가지 종류의 annotation으로 데이터의 연관관계를 표현하게된다.
도메인 객체
로서의 관계를 먼저설명하고, / 이후 컬럼
으로서의 관계에 대해 설명한다
도메인 객체
로서의 관계를 정의하는 anotation으로는
@OneToOne, @ManyToOne … 것들이 있다.
cascade
1 |
- 영속성을 의미한다
- 부모를 저장할때 자식도 같이저장, / 제할때 같이삭제 등등 영속성을 관리할때 사용한다
- 종류
- ALL - 모두
- PERSIST
- MERGE
- REMOVE
- REFRESH
- DETACH
fetch
1 |
데이터를 가지고올때 fetch하는 방식을 정의한다.
크게 LAZY / EAGER 가 있다
LAZY :
- 접근시도시 로딩
- xxxToMany 의 디폴트
EAGER :
- 부모데이터 로드시 함깨 로드
- xxxToOne 의 디폴트값
orphanRemoval (고아객체 허용/삭제여부)
1 |
- 관계에서 child 에서 참조하고있는 부모의 키 (fk)를 nulll로 셋팅하게되면 관계를 끊은것으로보고, 고아객체가된다
- 일반적인 케이스에서는 고아객체를 접근 할 수 없다(root 를 통해서만 접근하므로 ^^; 그렇게 되어야한다)
- orphanRemoval 이 true로 설정되어있다면 고아객체가 되는상황에서 child를 제거한다.
optional
1 |
관계를 가지는 도메인이 필수값인
경우 optional을 false로 줄 수 있다.
객체 생성시 해당 값이 null이면 exception이 난다
컬럼 annotation(@Column) 의 주요 options
두번째로 @Column의 옵션이다.
이항목은 사용하고있는 infrastructure 의 스팩과 닿아있다고 생각하면 편하다.
사용하고있는 infrastructure의 해당 컬럼의 속성에따라 실제 만들어지는 쿼리도 달라지게된다.
1 |
- name: 테이블 컬럼이름
- nullable : 해당 컬럼이 nullable인지 여부
- unique : 해당 컬럼이 unique인지 여부
- length : 해당 컬럼의 길이. string 인경우에만 사용하도록하자. default : 255