[etc]우아한 CRUD
우아한테크세미나의 우아한CRUD 정리
1부. Entity 클래스의 설계와 퍼시스턴스 프레임워크의 활용
VO vs DTO
VO
- 값이 같으면 동일하다고 간주되는, 식별성이 없는 작은 객체
- eg) Money, Color
DTO
- 원격호출을 효율화하기 위해 나온 패턴
레이어 간의 경계를 넘어서 데이터를 전달
하는 역할은 과거와 동일- 다만 다양한 객체의 역할을 다 DTO로 칭하는 혼란도 존재
Entity
- 연속성과 식별성의 맥락에서 정의되는 객체
Entity가 뷰, API 응답에 바로 노출될 때의 비용
- 캡슐화를 지키기 어려워진다
- 꼭 필요하지 않은 속성도 외부로 노출되어 향후 수정하기 어려워진다.
외부 노출용 DTO를 따로 만들기
- Entity -> DTO 변환 로직은 컴파일 타임에 체크된다
- DTO는 비교적 구조를 단순하게 가져갈 수 있다.
- DTO의 변화는 외부 인터페이스로 의식해서 관리하는 범위가 된다
- 여러 Entity를 조합할 수 있는 여지가 생긴다
DTO의 이름 고민
- 역할별로 구분된 DTO : 비슷한 이름의 DTO들이 계속해서 생성되어 고민의 여지가 발생
Aggregate로 Entity 간의 선긋기
Aggregate?
- 하나의 단위로 취급되는 연관된 객체군, 객체망
- Entity와 Value Object의 묶음
- 엄격한 데이터 일관성, 제약사항이 유지되어야 할 단위
- Transaction, Lock의 필수 범위
- 불변식이 적용되는 단위
- Document DB와 어울림
Aggregate 1개 당 Repository 1개
- Aggregate Root를 통해서 Aggregate 밖에서 Aggregate 안의 객체로 접근함
Aggregate 경계가 있는 시스템
- 별도의 저장소나 API 서버를 분리할 때 상대적으로 유리
- Aggregate 밖은 eventual consistancy를 목표로 할 수도 있다
- 여러 Aggregate의 변경은 Event, SAGA, TCC 등의 패턴을 활용할 수도 있다
- Aggregate 별로 Cache를 적용하기에도 좋다
- 분리할 계획이 없더라도 코드를 고칠 때 영향성을 파악하기가 유리
Aggregate 식별시 의식할 점
- CUD + 단순R(findById)에 집중
- 모든 R을 다 포용하려고 한다면 깊은 객체 그래프가 나온다
- (JPA를 쓴다면) Cascade를 써도 되는 범위인가?
Aggregate 간의 참조
- 다른 Aggregate의 Root를 직접 참조하지 않고 ID로만 참조하기
여러 Aggregate에 걸친 조회
Service 레이어에서 조합
- DB 성능에 더 유리할 수 있다
- 각각의 쿼리가 단순해진다
- Application/DB 레벨의 캐쉬어 더 유리
Join 필수적인 경우
- Where 절에 다른 Aggregate의 속성이 필요한 경우
Repository vs DAO
- DAO는 퍼시스턴스 레이어를 캡슐화
- DDD의 Repository는 도메인 레이어에 객체 지향적인 컬렉션 관리 인터페이스를 제공
Lazy Loading 다시 생각하기
- Aggregate을 다시 정리하고 복합조회용 객체를 분리하면 Lazy Loading이 필수일지 한번 더 생각해볼 수 있음
- 반대로 Lazy loading이 있어서 깊은 객체 그래프의 Aggregate를 설계하고자하는 유혹에 빠질 수도 있다ㅌㄴ
- Lazy loading이 필요하다는 것은 모델링을 다시 생각해봐야한다는 신호일수도 있다
Immutable과 Rich Domain Object
Immutable 객체의 장점
- Cache 하기에 안전
- 다른 레이어에 메서드 파라미터로 보내도 값이 안 바뀌었다는 확신을 할 수 있다
- DTO류가 여러 레이러를 오간다면 Immutable하면 더 좋다
Rich Domain Object
- Domain Object가 가진 속성과 연관된 행위
- 해당 객체에 있는 것이 책임이 자연스럽다 (Information Expert 패턴)
- 데이터 중심 -> 책임 중심의 설계로 진화할 수 있다.
- 상태를 바꾸는 메서드가 포함될 수도 있다
- 상태를 바꿀 때의 정합성 검사를 포함
- Immutable이 아니게 될 수 있다
- 영속화될 Domain Object 라면 상태를 바꾸는건 시스템의 상태를 바꾸는 경우에 한해야 한다.
- 메서드명도 그 행위를 잘 드러내야 한다.(setTitle() -> chageTitle())
결론
선을 넘지 않는 Entity
- 외부 레이어에 Entity 감추기
- Aggregate 단위로 Entity 간의 경계 의식하기(ID만 참조)
- 복합적인 READ 결과를 담을 클래스 분리
프레임워크는 설계를 거드는 역할
- 프레임워크에서 주는 제약이 설계에 도움을 주기도 한다
- 편의성을 주는 기능이 설계를 해치는지 경계해야 한다
때로는 다른 가치를 위해 더 긴 코드를 만들 수도 있다
- 고치기 쉬운, 협업하기 쉬운, 확장하기 쉬운 코드
2부. Spring Data JDBC Advanced
Spring Data JDBC
- Value Object 지원 (immutable)
- NamingStrategy 지원
- 상속/다형성 매핑 미지원
- 복합키 매핑 미지원
- AttributeConverter 미지원
- Entity 설계시 Aggregate 개념 적용을 강하게 주장!