[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 개념 적용을 강하게 주장!

참조