yoni
DDD 도메인주도설계철저입문 본문
<<<<목차>>>>
01. 도메인 주도 설계란?
02. 시스템 특유의 값을 나타내기 위한 '값 객체'
03. 생애주기를 갖는 객체 - '엔티티'
04. 부자연스러움을 해결하는 '도메인 서비스'
05. 데이터와 관계된 처리를 분리하자 - '리포지토리'
06. 유스케이스를 구현하기 위한 '애플리케이션 서비스'
09. 복잡한 객체 생성을 맡길 수 있는 '팩토리 패턴'
12. 도메인 규칙을 지키느 '애그리게이트'
[1. 객체]
값 객체란?
-> 시스템 특유의 값을 정의해야 할 때가 있다. 이러한 시스템 특유의 값을 표현하기 위해 정의하는 객체를 값 객체라고 한다
값은 불변이다. 그러나 값을 수정하지 않고서도 목적을 달성할 수 있는 소프트웨어를 만들기는어렵다. 값은 불변일지라도 값을 수정할 필요는 있다. 하지만 '변하지 않는'성질을 갖는 값은 값 자체를 수정할 수 없다. 값 객체의 수정 역시 값과 마찬가지로 대입문을 통해 교환의 형식으로 표현된다. (like temp 방식)
가변 객체와 불변 객체 중 결정을 내리기가 어려울 때는 일단 불편 객체를 적용하는 것이 낫다.
도메인 모델로 선정되지 못한 개념을 값 객체로 정의해야 할지에 대한 기준
-> '규칙이 존재하는가'와 '낱개로 다루어야 하는가' 라는 점을 중요하게 본다.
값 객체로 정의할 만한 가치가 있는 개념을 구현 중에 발견했다면 그 개념은 도메인 모델로 피드백해야 한다. 도메인 주도 설계의 목적인 반복적 개발은 구현 중에 발견된 새로운 사실을 통해 이루어진다.
값의 객체를 도입했을 때의 장점
1. 표현력이 증가한다.
->값이 구성된 내용을 객체만 보고서도 알 수 있다.
2. 무결성이 유지된다.
->값의 벨리데이션을 체크할 수 있어 의미있는 데이터들만 추출할 수 있다.
3. 잘못된 대입 방지하기
4. 로직을 한곳에 모아두기
->중복된 소스를 방지 할 수 있다.
[2. 엔티티]
: 생애주기를 갖는 객체
- 예시
2020년의 나와 2022년의 나는 다른사람인가? 키, 체중, 취미는 변경이 될 수 있지만 이들은 속성일뿐 나라는 사람으로 동일성을 지켜주는 무언가가 있다.
[3. 값 객체와 엔티티의 차이점]
- 값객체
: 생애 주기를 갖지 않거나 생애주기를 나타내는 것이 무의미
: 비가변적
- 엔티티
: 생애주기를 가지며 연속성을 가지면
:가변적
- 같은 대상일 지라도 환경에 따라 모델링 방법이 달라진다.
[4. 도메인서비스란]
- 값 객체나 엔티티로 구현하기 어색한 행동이있는대 이런 어색함을 해결해주는것이 도메인서비스 객체다
- 도메인 서비스를 남용하게 되면
1) 빈혈 도메인 객체가 됨
2) 로지기이 흩어지면 소프트웨어가 변화에 대응하는 유연성이 저해돼 심각하게 정체된다
3) 중복코드를 제거하기 위한 노력을 한시도 포기해서는 안된다.
- 도메인 서비스의 기준
: 도메인 서비스는 도메인 모델을 코드상에 나타냈다는 점에서 값 객체, 엔티티와 같다.
- 구현의 공통은 애플리케이션 서비스
- 톡, 메일과 같이 특정 도메인에서만 사용 되어야 할 구현은 도메인서비스
- 데이터스토어는 본래 도메인에는 없는 존재
- 데이터스토어는 애플리케이션만의 관심사다. 해서 도메인 객체는 오로지 도메인 모델만 나타내는것이 좋다.
- 티켓은 도메인 객체 생성수정삭제는 서비스 객체
- 어떤 처리를 객체 안에 정의했을 때 잘 들어맞지 않는 느낌이 든다면 이 처리를 도메인 서비스로 옮기면 자연스럽게 나타낼 수 있다.
[5. 리포지토리]
- 리포지토리의 일반적인 의미는 보관창고이다.
- 프로그램을 실행할 때 메모리에 로드된 데이터는 프로그램을 종료하면 그대로 사라져 버린다.
- 엔티티는 생애주기를 갖는 객체이기 때문에 프로그램의 종료와 함께 객체가 사라져서는 안된다.
- 객체 인스턴스를 저장할 때는 데이터스토어에 기록하는 처리를 직접 실행하는 대신 리포지토리에 객체의 저장을 맡기면 된다.
- 저장해 둔 데이터에서 다시 객체를 읽어 들일 때도 리포지토리에 객체의 복원을 맡긴다.
- 리포지토리는 인터페이스로 정의된다.
- Optional 타입은 반환되는 객체가 있을 수도 있고 없을 수도 있다는 것을 의미하는 타입니다.
- 관계형 데이터베이스를 다룬다.
[6. 애플리케이션 서비스]
- 유스케이스를 구현하는 객체
- 사용자 등록을 해야하는 시스템에서 사용자 기능을 구현하려면 "사용자 등록하기" 유스케이스와 "사용자 정보 수정하기" 유스케이스각 필요하다.
- 유스케이스를 따라 "사용자 등록하기" 행위와 "사용자 정보 수정하기" 행위를 정의한다.
- 이용자의 목적에 부응하는 프로그램을 의미한다.
- 도메인 객체는 도메인을 코드로 옮긴 것이다.
- 유스케이스는 소위 말하는 CRUD(CREATE, READ, UPDATE, DELETE)처리에 해당한다.
- 파라미터가 추가된다고 하더라도 원본의 함수는 수정하지 않고 파라미터가 추가된 함수를 하나더 생성하여 사용한다.
[7. 도메인 규칙의 유출]
- 애플리케이션 서비스는 도메인 객체가 수행하는 태스크를 조율하는 데만 전념해야 한다.
[8. 응집도]
- 응집도를 높이려면 간단히 클래스를 분리하면 된다.
[9. 서비스란 무엇인가]
- 서비스는 지식을 나타낸 객체다.
- 서비스는 자신의 행동을 변화시키는 것을 목적으로 하는 상태를 갖지 않는다.
하지만 서비스가 전혀 상태를 갖지 않는다고 이해하면 잘못 이해한 것이다.
[10. 팩토리 패턴]
: 복잡한 객체 생성을 맡길 수 있다.
- 객체 생성과 책임지는 객체를 마치 도구를 만드는 공장 과도 같다고 해서 '팩토리'라고 부른다.
- 팩토리는 객체의 생성 과정과 관련된 지식이 정리된 객체다.
- 생성자 메서드가 복잡해진다면 팩토리를 정의한다.
- 생성 절차가 간단하다면 그냥 생성 메서드를 호출하는 쪽이 더 낫다.
- 하던 대로 객체를 생성 하지 말고 팩토리가 필요하지는 않은가 검토하는 습관을 들이자
- 팩토리를 이용해 객체 생성 절차를 캡슐화 하는 것도 로직의 의도를 더 명확히 드러내면서 유연성을 확보할 수 있는 좋은 방법이다.
[11. 애그리게이트란]
- 데메테르의 법칙은 소프트웨어의 유지 보수성을 향상시키고 코드를 더욱더 유연하게 한다.
애그리게이트의 목표와도 부합한다.
- 객체 내부의 데이터는 함부로 외부에 공개되어서는 안된다.
그러나 데이터를 외부에 전혀 공개하지 않으면 리포지토리가 객체를 데이터 스토어에 저장 할 수가 없다.
- 애그리게이트의 경계를 정하는 원칙 중 가장 흔히 쓰이는 것은 "변경의 단위"다
로킹 단위 |
로크의 수 | 병행 제어 | 로킹 오버헤드 | 병행성 수준 | 데이터베이스 공유도 |
커짐 | 적어짐 | 단순해짐 | 감소 | 낮아짐 | 감소 |
작아짐 | 커짐 | 복잡해짐 | 증가 | 높아짐 | 증가 |
[질문]
Q.1) 애그리게이트는 로킹로 생각할 수 있나?
A.1) 그렇다
Q.2) 엔티티의 예시는?
A.2) 등록, 수정, 조회, 삭제 crud 같이 주기가 있는 단위들을 뜻함
Q.3) 엔티티의 값 객체의 사용이 혼돈된다면?
A.3) 엔티티는 키가 있고 값 객체는 키를 가질 수 없다.
Q.3) 엔티티의 값 객체의 사용이 혼돈된다면?
A.3) 엔티티는 키가 있고 값 객체는 키를 가질 수 없다.
Q.4) 지양하는 개발 패턴의 방향은?
A.4) - aaaa.bbb.ccc.eee.acount() 와 같은 결합도가 높은 방향이 아닌 aaaa.acount() 와 같이 캡슐화가 적용된 개발 방향성을 선호
- 참조를 하여 함수를 찾아가는 패턴이 아닌 한 class안에 로직이 구성이 되어있어야 한다.