민스씨의 일취일장

TIL | LogT | DDD | 도메인 주도 개발에 대해 알게된 내용과 고민들 본문

Programming Language & Framework

TIL | LogT | DDD | 도메인 주도 개발에 대해 알게된 내용과 고민들

읻민스 2023. 4. 11. 22:14
반응형

도메인 주도 개발에 대해서 공부한 내용과 고민들을 시간순으로 기록하는 글이다.

Domain Driven Design

도메인 주도 디자인 썸네일 이미지이다.
Domain Driven Design

도메인 주도 개발 시작하기 <최범균> 

<도메인 주도 개발 시작하기> 책을 공부하면서 알게된 내용과 생각을 정리해서 기록할 것이다. 책 내용을 발췌하는 부분도 있고 생각을 넣어 재가공하는 부분이 있음을 미리 알린다.


2023년 4월 11일

Chapter 01 도메인 모델 시작하기

도메인이란?

주제(문제, 과제)에 필요한 행동 정보 전체를 말한다.

도메인 모델이란?

프로그램을 특정 도메인을 기준으로 설계하는 방식이다.

도메인 모델을 도출하기 위해선

도메인의 핵심 구성요소, 규칙, 기능을 규정해야한다.

Entity

고유 식별자를 갖고 있으며, 생성 후부터 삭제될 때까지 유지된다.

Value Type

개념적으로 완전한 하나의 정보 단위, 실질 객체를 표현하는 클래스이다.

밸류타입내 데이터 변경기능을 제공하지 않을 때 불변(immutable)객체라고 한다. 불변객체는 참조 투명성과 스레드에 안전한 특징을 갖는다.

알게된 점

  • 도메인 모델을 도출하는 과정에서 말로 적어 놓은 규칙들이 코드의 논리적 구조에 큰 영향을 미치고 더욱 깔끔하고 논리적으로 작성될 수 있도록 도움이 되는 걸 예제를 통해 알게됐다. 이를 통해서 도메인에 대한 세심한 관찰과 고민이 얼마나 중요한지 엿볼 수 있었다.

고민거리

  • Value Type의 데이터는 DB에 테이블로 존재하지 않고 여러 테이블의 값을 참조해 관련 정보를 함께 전달하는 DTO같은 것인가?

2023년 4월 12일

Chapter 02 아키텍처 개요

DIP란?

  • Dependency Inversion Principle
  • 저수준 모듈을 추상화해 인터페이스 객체로 만들어 구현체가 고수준 모듈인 인터페이스에 의존하도록 의존성의 방향을 뒤집을 수 있다.

DIP의 장점

  • 테스트의 용의성

의존하던 저수준 모듈을 완벽히 구현하지 않더라도 인터페이스의 대역 구현개체를 만들어 테스트 할 수 있다.

  • 기능 확장의 편리성

의존성 주입으로 받은 객체를 바꿈으로써 간단하게 기능을 변경, 추가할 수 있다.

도메인 영역의 주요 구성요소

Entity - 고유식별자와 객체 고유의 라이프 사이클을 갖는다.
- 도메인 모델의 데이터와 기능을 제공한다.
- DB 엔티티와 다른점
1. 기능을 제공한다.
2. 두개 이상의 데이터를 하나의 Value로 표현할 수 있다. (RDBMS에선 Value Type을 표현하기 어렵다.)
Value - 고유긱별자를 갖지 않는 객체
- 개념적으로 하나의 대상을 표현한다.
- 불변으로 구현하는 걸 권장한다. 엔티티에서 Value Type 데이터를 변경하는 것은 교체를 의미한다.
Aggregate - 연관된 Entity와 Value를 개념적으로 묶은 것이다.
- 루트 엔티티를 가지며 이를 기준으로 관련 Entity와 Value Type들이 연결돼 있다. 
- Root Entity는 Aggregate의 기능을 제공한다.
Repository - 도메인 모델의 영속성을 처리한다.
- DBMS 테이블에서 객체를 가져오거나 객체를 저장한다.
- Aggregate 단위로 도메인 객체를 다룬다.
Domain Service - 특정 엔티티에 속하지 않는 도메인 로직을 제공한다.

2023년 4월 13일

Chapter 03 애그리거트

애그리거트 경계

애그리거트에 속한 객체를 외부의 다른 애그리거트가 변경하면 안되다. 이를 위해 애그리거트 내의 setter 메서드는 public이 아닌 private로 구현해야 한다. 또 Value Type 객체는 불변으로 구현해야 한다.

애그리거트의 트랜젝션

한 트랜잰션은 한 개의 애그리거트의 수정을 담당해야 한다.

리포지터리와 애그리거트

리포지터리는 애그리거트 단위로 존재하며 기본적으로 애그리거트의 저장(save)과 애거리거트의 호출(findById) 기능을 갖고 있어야 한다. 데이터의 변경이 발생하면 리포지터리는 그 데이터과 관련된 애그리거트 전체가 변화를 원자적으로 저장소에 반영할 수 있도록 구현해야 한다.

ID를 이용한 애그리거트 참조

애그리거트가 다른 애그리거트를 참조할 때 직접 하나의 멤버로 참조(직접참조)하는 것이 아닌 ID만 갖고 있다가 필요한 시점에 ID로 불러오는 방식으로 참조해야 한다. 이를 통해 애그리거트들의 결합도 증가 문제와 직접 참조로 발생할 수 있는 성능 이슈 (lazy loading, eager loading)를 해결할 수 있다. 또 직접 애그리거트를 참조하고 있지 않기 때문에 수정포인트가 줄어 확장성을 높일 수 있다.

ID를 이용한 다른 애그리커트를 참조하는 방식은 DB 테이블에서 외래키(FK)를 이용해 다른 테이블의 데이터를 참조하는 것과 개념적으로는 비슷하다. 하지만 애그리거트를 다루는 건 DB 내의 데이터들을 직접 다루는 게 아니고, DB에서 불러와서 메모리에 올라온 데이터들을 다루는 것이다. 다룬 후 결과를 DB에 반영할 뿐이다.

ID를 이용한 참조방식에서는 참조시 N+1번의 조회를 수행하는 성능 이슈가 발생할 수 있다. 이를 해결하기 위해서 조인을 사용하는 "조회 전용 쿼리"를 사용한다. 이는 즉시로딩, 지연로딩에 대한 고민할 필요가 없도록 해준다.

애그리거트들이 다른 저장소들을 사용하면 조인 쿼리를 사용할 수 없다. 이 때는 캐시를 사용하여 조회 전용 "저장소"를 따로 구성해야 한다.


2023년 4월 17일

Chapter 04 리포지터리와 모델 구현

모듈 위치

리포지터리 인터페이스와 어그리거트는 도메인 영역에 위치하고 리포지터리 구현체는 인프라스트럭쳐 영역에 위치해야 한다.

리포지터리 최소 기본 기능

  1. id로 애그리거트 조회하기
  2. 애그리거트 저장하기

JPA

JPA는 규칙대로 리포지터리 인터페이스를 작성하면 리포지터리 구현체를 스프링 빈(Spring Bean)으로 자동 등록한다.

규칙은 Repositoy<T, D> 인터페이스를 상속하는 것이다.

org.springframework.data.repository.Repository<T, ID>

여기서 T는 Entity 타입이고 Id는 해당 Entity의 식별자(id)이다.

728x90
반응형