DDD Start- 최범균
도메인주도 설계 구현과 핵심 개념 익히기.
1. 도메인 모델 시작
도메인 모델 패턴
도메인 규칙을 객체 지향 기법으로 구현하는 패턴
- 핵심 규칙을 구현한 코드는 도메인 모델에만 위치, 규칙이 바뀌거나 규칙을 확장해야 할때 다른코드에 영향을 덜주고 변경내역을 모델에 반영
- 점진적으로 만든 도메인 모델은 요구사항 정련을 위해 도메인 전문가나 다른 개발자와 논의하는 과정에서 공유 하기도 한다.
도메인 규칙을 객체 지향 기법으로 구현하는 패턴
- 핵심 규칙을 구현한 코드는 도메인 모델에만 위치, 규칙이 바뀌거나 규칙을 확장해야 할때 다른코드에 영향을 덜주고 변경내역을 모델에 반영
- 점진적으로 만든 도메인 모델은 요구사항 정련을 위해 도메인 전문가나 다른 개발자와 논의하는 과정에서 공유 하기도 한다.
엔티티와 벨류
- 엔티티의 가장 큰 특징은 식별자를 갖는다. (ID)
- 타입을 보고 유추가 가능하게 해서 자연스럽게 코드가 이해되도록 한다.
- 엔티티의 가장 큰 특징은 식별자를 갖는다. (ID)
- 타입을 보고 유추가 가능하게 해서 자연스럽게 코드가 이해되도록 한다.
도메인 모델에 set 넣지 않기
밸류타입은 불변으로 구현한다.
- set 메소드는 도메인의 핵심개념이나 의도를 코드에서 사라지게 한다.
- 생성자를 통해 받을수 있도록 한다.
밸류타입은 불변으로 구현한다.
- set 메소드는 도메인의 핵심개념이나 의도를 코드에서 사라지게 한다.
- 생성자를 통해 받을수 있도록 한다.
도메인 용어
- 도메인 용어를 사용하여 구현하는 불필요한 변환과정을 하지 않아도 된다.
- 코드로 해석하는 과정이 줄어들어 이해하는 시간을 절약한다.
- 도메인 용어를 사용하여 구현하는 불필요한 변환과정을 하지 않아도 된다.
- 코드로 해석하는 과정이 줄어들어 이해하는 시간을 절약한다.
도메인 영역의 주요 구성요소
- DB엔티티와 도메인모델 엔티티의 차이.
- 도메인 모델의 엔티티는 데이터와 도메인기능을 함께 제공한다.
- DB엔티티와 도메인모델 엔티티의 차이.
- 도메인 모델의 엔티티는 데이터와 도메인기능을 함께 제공한다.
2. 아키텍쳐 개요
DIP
Dependency Inversion Principle
- 저수준 모듈이 고수준 모듈에 의존
- 도메인과 응용 영역에 대한 영향을 주지 않거나 최소화 하면서 구현기술을 변경
- 중간에 요구사항(저수준)이 바뀌어도 응용영역을 변경하지 않아도 된다.
Dependency Inversion Principle
- 저수준 모듈이 고수준 모듈에 의존
- 도메인과 응용 영역에 대한 영향을 주지 않거나 최소화 하면서 구현기술을 변경
- 중간에 요구사항(저수준)이 바뀌어도 응용영역을 변경하지 않아도 된다.
애그리거트 (Aggregate)
- 도메인 모델의 구성요소는 규모가 커질수록 복잡해진다.
- 관련객체를 하나로 묶은 군집 이다.
- 전체구조를 이해하는데 도움이 된다.
- 도메인 모델의 구성요소는 규모가 커질수록 복잡해진다.
- 관련객체를 하나로 묶은 군집 이다.
- 전체구조를 이해하는데 도움이 된다.
리포지터리 (Repository)
도메인 객체를 영속화 하는데 필요한 기능을 추상화
- 실제 구현 클래스는 인프라 스트럭처 영역에 속한다.
도메인 객체를 영속화 하는데 필요한 기능을 추상화
- 실제 구현 클래스는 인프라 스트럭처 영역에 속한다.
인프라 스트럭처 개요 (Infrastructure)
- 표현영역/응용영역/도메인영역을 지원
- 인터페이스를 도메인영역과 응용영역에서 구현하는것이 시스템을 유연하게 만든다. ex. @Transaction - 한줄로 트랜잭션을 처리한다. > 시스템을 유연하게 만든다.
- 표현영역/응용영역/도메인영역을 지원
- 인터페이스를 도메인영역과 응용영역에서 구현하는것이 시스템을 유연하게 만든다. ex. @Transaction - 한줄로 트랜잭션을 처리한다. > 시스템을 유연하게 만든다.
3. 애그리거트
관련한 모델을 하나로 모아 복잡한 모델을 관리하는 기준을 제공
관련한 모델을 하나로 모아 복잡한 모델을 관리하는 기준을 제공
도메인 규칙과 일관성
- set 을 안넣는 이유
- 자연스럽게 불변 타입으로 구성하게 되어 일관성이 깨질 확률이 낮아진다.
- 의미가 드러나는 이름을 사용하게 될 확률이 높아진다.
- set 을 안넣는 이유
- 자연스럽게 불변 타입으로 구성하게 되어 일관성이 깨질 확률이 낮아진다.
- 의미가 드러나는 이름을 사용하게 될 확률이 높아진다.
트랜젝션 범위
- 한 트렌젝셔에서는 한 애그리거트만 수정 해야 충돌의 가능성이 줄어든다.
- 한 애거리거트 내부에서 다른 애거리거트 상태를 변경하는 기능을 실행하면 안된다.
- 한 애거리거트가 다른 애거리거트의 기능에 의존하면 결합도가 높아지게 된다.
- 한 트렌젝셔에서는 한 애그리거트만 수정 해야 충돌의 가능성이 줄어든다.
- 한 애거리거트 내부에서 다른 애거리거트 상태를 변경하는 기능을 실행하면 안된다.
- 한 애거리거트가 다른 애거리거트의 기능에 의존하면 결합도가 높아지게 된다.
ID를 통한 애그리거트 참조
모델의 복잡도가 하향한다.
모델의 복잡도가 하향한다.
ID를 통한 참조와 조회 성능
- N+1 조회 문제 : 조회대상이 N개면 N번 조회 (잘못된 ORM 설계)
- join 또는 query 를 통해 해결하자.
- N+1 조회 문제 : 조회대상이 N개면 N번 조회 (잘못된 ORM 설계)
- join 또는 query 를 통해 해결하자.
확장
- 초기에는 단일서버에 단일 DBMS로 구성이 가능하다.
- 사용자가 늘고 트래픽이 증가한다.
- 자연스럽게 부하를 분산하기 위해 도메인별로 시스템을 분리한다.
- 초기에는 단일서버에 단일 DBMS로 구성이 가능하다.
- 사용자가 늘고 트래픽이 증가한다.
- 자연스럽게 부하를 분산하기 위해 도메인별로 시스템을 분리한다.
4. 리포지터리 모델구현
JPA를통한 리포지터리 구현
- ID로 조회/애그리거트 저장
- 밸류는 @Embededable 로 매핑 설정
- 하이버네이트는 clear() 메소드를 호출하면 delete 쿼리로 삭제를 수행
- 수행되길 원하지 않으면 단일클래스로 구현
- 밸류타입 프로퍼티는 @Embedded 로 매핑
- 조회시점에서 완전한 상태가 되게 하려면 Fetch.EAGER (반대는 Fetch.LAZY)
- ID로 조회/애그리거트 저장
- 밸류는 @Embededable 로 매핑 설정
- 하이버네이트는 clear() 메소드를 호출하면 delete 쿼리로 삭제를 수행
- 수행되길 원하지 않으면 단일클래스로 구현
- 밸류타입 프로퍼티는 @Embedded 로 매핑
- 조회시점에서 완전한 상태가 되게 하려면 Fetch.EAGER (반대는 Fetch.LAZY)
영속성 전파
- 삭제메소드는 애거리거트에 속한 모든객체를 삭제 해야 한다.
- CascadeType.PERSIST, CascadeType.REMOVE
- 삭제메소드는 애거리거트에 속한 모든객체를 삭제 해야 한다.
- CascadeType.PERSIST, CascadeType.REMOVE
5. 리포지터리 조회 기능
- Specification 을 구현해야 한다.
- JPA 정적메타모델
- @StaticMetamodel(~~.class)
- Criteria 를 사용할때 StaticMetamodel을 통해 구현하는것이 코드의 안정성이나 생산성측면에서 유리하다. (오타의 위험 및 자동완성)
- Criteria 의 문제점
- 도메인모델은 구현기술에 의존하지 않아야한다.
- Specification 인터페이스는 toPredicate() 가 JPA 의 Root 와 CriteriaBuilder 에 의존하고 있다.
- 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 을 선언한다.
- 생성된 모델기준으로 생기기때문에 매핑필드변경시 불가
- 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() } // 도메인에서 체크하고 구현해야한다.
실제 사용자가 원하는 기능을 제공하는것은 응용영역에 위치한 서비스이다.
- 도메인 로직을 넣고싶은 욕심을 참아야 한다.
- ex) if member.checkPwd() { member.changePwd() } // 도메인에서 체크하고 구현해야한다.
분산구현
장점 : 한눈에 들어와서 코드의 가독성 증가
단점 : 코드의 응집력약화, 알아보는데 분석이 필요하다.
장점 : 한눈에 들어와서 코드의 가독성 증가
단점 : 코드의 응집력약화, 알아보는데 분석이 필요하다.
응용서비스의 구현
하나의 클래스에서 모두 구현할 때
관련없는 서비스, 코드가 뒤섞이는것을 조심해야 한다.
클래스의 크기(줄 수)가 커질수 있다.
분리하는게 좋음에도 억지로 끼워넣게 된다.
- 표현영역 코드
- 애거리거트 자체를 데이터로 주고받으면 코드의 응집도를 낮추게 된다.
HttpServletRequest 나 Session 을 주고받지 말자.
- 단독 테스트가 어려워지고, 세션, 쿠키 등 표현 서비스에 있어야 하는 정보가 응용서비스로 넘어가게 되어 표현영역의 응집도가 깨지게 된다.
관련없는 서비스, 코드가 뒤섞이는것을 조심해야 한다.
클래스의 크기(줄 수)가 커질수 있다.
분리하는게 좋음에도 억지로 끼워넣게 된다.
- 표현영역 코드
- 애거리거트 자체를 데이터로 주고받으면 코드의 응집도를 낮추게 된다.
HttpServletRequest 나 Session 을 주고받지 말자.
- 단독 테스트가 어려워지고, 세션, 쿠키 등 표현 서비스에 있어야 하는 정보가 응용서비스로 넘어가게 되어 표현영역의 응집도가 깨지게 된다.
7. 도메인 서비스
- 여러 애거리거트가 섞이게 되면 코드의 위치 등 책임의 문제가 드러나게 된다.
- 도메인 개념이 애거리거트에 숨어들어 명시적으로 드러나지 않게 된다.
도메인 서비스를 별도로 구현한다.
- 하위 패키지를 구분하여 위치 시킨다.
- 여러 애거리거트가 섞이게 되면 코드의 위치 등 책임의 문제가 드러나게 된다.
- 도메인 개념이 애거리거트에 숨어들어 명시적으로 드러나지 않게 된다.
도메인 서비스를 별도로 구현한다.
- 하위 패키지를 구분하여 위치 시킨다.
8. 어그리거트 트랜잭션 관리
선점 (Pessimistic) 비선점 (Optimisitic)
- 선점
- 사용이 끝날때가지 다른스레드가 해당 애거리거트를 수정하는것을 막는다.
- 보통 DBMS가 제공하는 행단위 Lock 을 통해 구현한다.
- LockModeType.PESSIMISTIC_WRITE
- 교착상태 방지를 위해 최대 대기 시간을 설정해야 한다.
- 비선점
- 변경한 데이터를 싲레 DBMS 에 반영하는 시점에 변경 가능 여부를 확인하는 방식
- 구현
- 애거리거트 버전을 함께 보내어 확인한다.
선점 (Pessimistic) 비선점 (Optimisitic)
- 선점
- 사용이 끝날때가지 다른스레드가 해당 애거리거트를 수정하는것을 막는다.
- 보통 DBMS가 제공하는 행단위 Lock 을 통해 구현한다.
- LockModeType.PESSIMISTIC_WRITE
- 교착상태 방지를 위해 최대 대기 시간을 설정해야 한다.
- 비선점
- 변경한 데이터를 싲레 DBMS 에 반영하는 시점에 변경 가능 여부를 확인하는 방식
- 구현
- 애거리거트 버전을 함께 보내어 확인한다.
9. 도메인 모델과 BOUNDED CONTEXT
- 도메인을 완벽히 표현하는 단일 모델을 만드는 시도. 이는 실행할 수 없다.
- 구분되는 경계를 갖는 컨텍스트를 바운디드 컨텍스트 라고 부른다.
- 도메인을 완벽히 표현하는 단일 모델을 만드는 시도. 이는 실행할 수 없다.
- 구분되는 경계를 갖는 컨텍스트를 바운디드 컨텍스트 라고 부른다.
10. 이벤트
- 트렌젝션 처리가 복잡해짐에 따라 속도 및 로직이 뒤섞이는 문제가 발생한다.
- 이러한 강한 결합 (high coupling)이 생기고, 이를 해결하기 위한 방법중 하나는 이벤트를 활용하는 추상클래스를 활용하는 것이다.
- 비동기 이벤트 처리 ex)
- 로컬 핸들러를 비동기로 실행
- 메시지 큐를 사용 - 글로벌 트렌젝션 문제
- 이벤트 저장소와 이벤트 포워더 사용
- 이벤트 저장소와 이벤트 제공 API 사용
- 트렌젝션 처리가 복잡해짐에 따라 속도 및 로직이 뒤섞이는 문제가 발생한다.
- 이러한 강한 결합 (high coupling)이 생기고, 이를 해결하기 위한 방법중 하나는 이벤트를 활용하는 추상클래스를 활용하는 것이다.
- 비동기 이벤트 처리 ex)
- 로컬 핸들러를 비동기로 실행
- 메시지 큐를 사용 - 글로벌 트렌젝션 문제
- 이벤트 저장소와 이벤트 포워더 사용
- 이벤트 저장소와 이벤트 제공 API 사용
11. CQRS
Command Query Responsibility Segregation, 명령을 위한 모델과 상태를 제공하는 조회를 위한 모델을 분리하는 패턴
- 도메인 로직을 구현하는데 집중
- 구현해야 할 코드가 더 많아짐
- 더 많은 구현 기술이 필요
Command Query Responsibility Segregation, 명령을 위한 모델과 상태를 제공하는 조회를 위한 모델을 분리하는 패턴
- 도메인 로직을 구현하는데 집중
- 구현해야 할 코드가 더 많아짐
- 더 많은 구현 기술이 필요
'java > design_pattern' 카테고리의 다른 글
개발자를 위한 코드리뷰 (0) | 2020.06.23 |
---|---|
객체지향적 사고를 가져야 하는 이유 (0) | 2016.02.05 |
Java Design Pattern 총 정리 (0) | 2014.07.01 |
Facade Pattern - 퍼사드 패턴 (0) | 2014.07.01 |
Bridge Pattern - 브릿지 패턴 (0) | 2014.07.01 |