DDD Start- 최범균

도메인주도 설계 구현과 핵심 개념 익히기.

1. 도메인 모델 시작

도메인 모델 패턴

도메인 규칙을 객체 지향 기법으로 구현하는 패턴

  • 핵심 규칙을 구현한 코드는 도메인 모델에만 위치, 규칙이 바뀌거나 규칙을 확장해야 할때 다른코드에 영향을 덜주고 변경내역을 모델에 반영
  • 점진적으로 만든 도메인 모델은 요구사항 정련을 위해 도메인 전문가나 다른 개발자와 논의하는 과정에서 공유 하기도 한다.

엔티티와 벨류

  • 엔티티의 가장 큰 특징은 식별자를 갖는다. (ID)
  • 타입을 보고 유추가 가능하게 해서 자연스럽게 코드가 이해되도록 한다.

도메인 모델에 set 넣지 않기

밸류타입은 불변으로 구현한다.

  • set 메소드는 도메인의 핵심개념이나 의도를 코드에서 사라지게 한다.
  • 생성자를 통해 받을수 있도록 한다.

도메인 용어

  • 도메인 용어를 사용하여 구현하는 불필요한 변환과정을 하지 않아도 된다.
  • 코드로 해석하는 과정이 줄어들어 이해하는 시간을 절약한다.

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

  • DB엔티티와 도메인모델 엔티티의 차이.
    • 도메인 모델의 엔티티는 데이터와 도메인기능을 함께 제공한다.

2. 아키텍쳐 개요

DIP

Dependency Inversion Principle

  • 저수준 모듈이 고수준 모듈에 의존
  • 도메인과 응용 영역에 대한 영향을 주지 않거나 최소화 하면서 구현기술을 변경
  • 중간에 요구사항(저수준)이 바뀌어도 응용영역을 변경하지 않아도 된다.

애그리거트 (Aggregate)

  • 도메인 모델의 구성요소는 규모가 커질수록 복잡해진다.
  • 관련객체를 하나로 묶은 군집 이다.
  • 전체구조를 이해하는데 도움이 된다.

리포지터리 (Repository)

도메인 객체를 영속화 하는데 필요한 기능을 추상화

  • 실제 구현 클래스는 인프라 스트럭처 영역에 속한다.

인프라 스트럭처 개요 (Infrastructure)

  • 표현영역/응용영역/도메인영역을 지원
  • 인터페이스를 도메인영역과 응용영역에서 구현하는것이 시스템을 유연하게 만든다. ex. @Transaction - 한줄로 트랜잭션을 처리한다. > 시스템을 유연하게 만든다.

3. 애그리거트

관련한 모델을 하나로 모아 복잡한 모델을 관리하는 기준을 제공

도메인 규칙과 일관성

  • set 을 안넣는 이유
    • 자연스럽게 불변 타입으로 구성하게 되어 일관성이 깨질 확률이 낮아진다.
    • 의미가 드러나는 이름을 사용하게 될 확률이 높아진다.

트랜젝션 범위

  • 한 트렌젝셔에서는 한 애그리거트만 수정 해야 충돌의 가능성이 줄어든다.
  • 한 애거리거트 내부에서 다른 애거리거트 상태를 변경하는 기능을 실행하면 안된다.
  • 한 애거리거트가 다른 애거리거트의 기능에 의존하면 결합도가 높아지게 된다.

ID를 통한 애그리거트 참조

모델의 복잡도가 하향한다.

ID를 통한 참조와 조회 성능

  • N+1 조회 문제 : 조회대상이 N개면 N번 조회 (잘못된 ORM 설계)
  • join 또는 query 를 통해 해결하자.

확장

  • 초기에는 단일서버에 단일 DBMS로 구성이 가능하다.
  • 사용자가 늘고 트래픽이 증가한다.
  • 자연스럽게 부하를 분산하기 위해 도메인별로 시스템을 분리한다.

4. 리포지터리 모델구현

JPA를통한 리포지터리 구현

  • ID로 조회/애그리거트 저장
  • 밸류는 @Embededable 로 매핑 설정
    • 하이버네이트는 clear() 메소드를 호출하면 delete 쿼리로 삭제를 수행
    • 수행되길 원하지 않으면 단일클래스로 구현
  • 밸류타입 프로퍼티는 @Embedded 로 매핑
  • 조회시점에서 완전한 상태가 되게 하려면 Fetch.EAGER (반대는 Fetch.LAZY)

영속성 전파

  • 삭제메소드는 애거리거트에 속한 모든객체를 삭제 해야 한다.
  • CascadeType.PERSIST, CascadeType.REMOVE

5. 리포지터리 조회 기능

  • Specification 을 구현해야 한다.
  • JPA 정적메타모델
    • @StaticMetamodel(~~.class)
    • Criteria 를 사용할때 StaticMetamodel을 통해 구현하는것이 코드의 안정성이나 생산성측면에서 유리하다. (오타의 위험 및 자동완성)
  • Criteria 의 문제점
    • 도메인모델은 구현기술에 의존하지 않아야한다.
    • Specification 인터페이스는 toPredicate() 가 JPA 의 Root 와 CriteriaBuilder 에 의존하고 있다.

동적 인스턴스 생성

  • JPQL - select 절에 new ~~Enttiy()를 넣을 수 있따.
    • ex)SELECT new OrderView(o, m, p) FROM Order o, Member m, Product p ~~
  • 장점 : JPQL을 그대로 사용, 객체 기준으로 쿼리를 작성할 수 있다.
  • @Imutable, @Subselect, @Syncronize : 하이버네이트 전용 애노테이션 테이블이 아닌 쿼리결과를 @Entity로 매핑 할 수 있다.
  • Update가 불가하므로 @Immutable 을 선언한다.
    • 생성된 모델기준으로 생기기때문에 매핑필드변경시 불가

6. 응용서비스와 표현영역

실제 사용자가 원하는 기능을 제공하는것은 응용영역에 위치한 서비스이다.

  • 도메인 로직을 넣고싶은 욕심을 참아야 한다.
  • ex) if member.checkPwd() { member.changePwd() } // 도메인에서 체크하고 구현해야한다.

분산구현

장점 : 한눈에 들어와서 코드의 가독성 증가 
단점 : 코드의 응집력약화, 알아보는데 분석이 필요하다.

응용서비스의 구현

하나의 클래스에서 모두 구현할 때

관련없는 서비스, 코드가 뒤섞이는것을 조심해야 한다.
클래스의 크기(줄 수)가 커질수 있다.
분리하는게 좋음에도 억지로 끼워넣게 된다.
  • 표현영역 코드
    • 애거리거트 자체를 데이터로 주고받으면 코드의 응집도를 낮추게 된다.

HttpServletRequest 나 Session 을 주고받지 말자.

  • 단독 테스트가 어려워지고, 세션, 쿠키 등 표현 서비스에 있어야 하는 정보가 응용서비스로 넘어가게 되어 표현영역의 응집도가 깨지게 된다.

7. 도메인 서비스

  • 여러 애거리거트가 섞이게 되면 코드의 위치 등 책임의 문제가 드러나게 된다.
  • 도메인 개념이 애거리거트에 숨어들어 명시적으로 드러나지 않게 된다.

도메인 서비스를 별도로 구현한다.

  • 하위 패키지를 구분하여 위치 시킨다.

8. 어그리거트 트랜잭션 관리

선점 (Pessimistic) 비선점 (Optimisitic)

  • 선점
    • 사용이 끝날때가지 다른스레드가 해당 애거리거트를 수정하는것을 막는다.
    • 보통 DBMS가 제공하는 행단위 Lock 을 통해 구현한다.
    • LockModeType.PESSIMISTIC_WRITE
    • 교착상태 방지를 위해 최대 대기 시간을 설정해야 한다.
  • 비선점
    • 변경한 데이터를 싲레 DBMS 에 반영하는 시점에 변경 가능 여부를 확인하는 방식
    • 구현
      • 애거리거트 버전을 함께 보내어 확인한다.

9. 도메인 모델과 BOUNDED CONTEXT

  • 도메인을 완벽히 표현하는 단일 모델을 만드는 시도. 이는 실행할 수 없다.
  • 구분되는 경계를 갖는 컨텍스트를 바운디드 컨텍스트 라고 부른다.

10. 이벤트

  • 트렌젝션 처리가 복잡해짐에 따라 속도 및 로직이 뒤섞이는 문제가 발생한다.
  • 이러한 강한 결합 (high coupling)이 생기고, 이를 해결하기 위한 방법중 하나는 이벤트를 활용하는 추상클래스를 활용하는 것이다.
  • 비동기 이벤트 처리 ex)
    • 로컬 핸들러를 비동기로 실행
    • 메시지 큐를 사용 - 글로벌 트렌젝션 문제
    • 이벤트 저장소와 이벤트 포워더 사용
    • 이벤트 저장소와 이벤트 제공 API 사용

11. CQRS

Command Query Responsibility Segregation, 명령을 위한 모델과 상태를 제공하는 조회를 위한 모델을 분리하는 패턴

  • 도메인 로직을 구현하는데 집중
  • 구현해야 할 코드가 더 많아짐
  • 더 많은 구현 기술이 필요


+ Recent posts