느낀 점
① 도메인 주도적으로 개발을 하지 않아도 기능 구현은 할 수 있다. 하지만 도메인 주도 개발을 하는 궁극적인 이유는 코드 리팩토링을 하는 이유와 같다고 생각한다. DDD를 한다는 것은
- 애그리거트라는 이름으로 같은 역할을 하는 클래스끼리 군집화하는 것
- 금액을 표현하는 필드를 int 가 아닌 Money 클래스로 표현하는 것
- 도메인 로직은 응용 계층이 아닌 도메인 계층에 모아 응집도가 높이는 것
... 등등
같은 원칙을 지켜가면서 개발을 하는 것인데 이는 코드를 이해하는 것이 보다 쉬워지고, OCP 원칙을 지킬 수 있기 때문이다. 그래서 새로 생성하는 프로젝트 뿐만이 아니라 기존 프로젝트에서 조금씩 조금씩 도메인 주도 개발화를 해보는 것도 괜찮을 것 같다.
② 애플리케이션을 역할에 따라 4개의 계층 (표현, 응용, 도메인, 인프라스트럭쳐) 으로 구분하는게 신박했다. OSI 7계층이 떠올랐다. 책을 읽기 전에는 DDD에서 패키지를 묶는 이유가 같은 도메인으로 그룹화하기 위해 묶는 것인 줄만 알았는데 계층을 표현하기 위해서도 패키지 구성을 하는구나를 깨달았다.
와닿았던 것
1. 한 클래스에 모든 도메인 규칙을 넣기가 버거운가?
도메인 규칙을 구현하는 내용이 너무 복잡하거나, 외부 시스템을 사용하는 로직이라 한 클래스(애그리거트) 에 넣기에 무리일 수 있다.
그렇다면 CalculationService
처럼 특정 기능만 구현한 클래스를 따로 빼는 방법도 있다. (p.237)
2. 이 책에서는 검증 메소드명의 접두어로 verify
를 붙인다.
무언가를 검증하는 메소드를 작성할 때 verify 를 쓸지 validate 를 쓸지 헷갈렸는데 찾아보니 이런 차이가 있다.
💡 verification: 기능이 모든 조건을 만족하도록 개발을 잘 했는가
💡 validation: 기능이 사용자의 요구하는대로 만들어졌는가
▶ "verification" as:
"A test of a system to prove that it meets all its specified requirements at a particular stage of its development."
▶ validation is defined as:
"An activity that ensures that an end product stakeholder’s true needs and expectations are met."
3. 필요한건 금액을 표현하는 int 형 변수 하나지만 도메인을 표현하기 위해 Money 클래스(밸류)로 빼는구나! (p.49)
int price;
대신 Money price;
를 쓴다.
※ 장점 : 금액 계산 같은 금액 관련 메소드를 Money
클래스에서 구현할 수 있다.
public class Money {
private int value;
public Money add(Money money) {...}
}
4. 필드의 의미를 표현하고 싶을 때 변수명을 어떻게 잘 지을지만 고민했었는데 클래스명(엔티티/밸류) 으로도 그 의미를 알릴 수 있다! (p.53)
어떻게? String id;
대신 OrdeNo id;
를 쓴다.
5. 습관적으로 클래스에 @Setter 를 붙이곤 했다.
그럼 실수든 고의든 어디에서나 정보를 변경할 수 있기 때문에 유지보수에 어렵다. 그리고 밸류는 불변을 유지해야 한다. 정보를 변경하고 싶은가? 생성자를 활용해 새로운 값을 가진 객체를 생성하는 방법이 있다. (p.55)
6. DIP (의존 역전 원칙) (p.73)
💡 장점이 무엇인가?
① 구현을 담당하는 클래스 교체가 용이하다: 인터페이스이기 때문에
② 테스트하기 쉽다: 활용 클래스를 테스트하려면 구현 클래스를 mock 객체로 하면 되고 구현 내용을 테스트 하려면 활용 클래스까지 올라갈 필요 없이 구현 클래스만 테스트 대상으로 삼으면 된다.
💡 DIP 구현 방법
1. 구현내용이 담긴 메소드(저수준) 를 인터페이스로 만든다.
2. 메소드를 사용하는 클래스(고수준)에서 인터페이스를 참조한다. → 여기서 인터페이스는 고수준이다.
3. 저수준(구현클래스)은 고수준(인터페이스)를 참조하게 되므로 저수준이 고수준에 의존하는 의존 역전 원칙 (DIP)를 구현한 것이다.
7. 주문 화면에서 주소를 변경할 때, 회원 주소도 동시에 변경하는 경우처럼 두 개의 애그리거트를 변경하고 싶은가?
한 애그리거트에서 둘 다 하려하지 말고 상위 메소드에서 두 개를 변경하라! (p.111)
@Transactional
public class ChangeOrderService {
public void changeAddress(OrderId id, ShippingInfo newShippingInfo) {
// 주문 애그리거트 변경
Order order = orderRepository.findById(id);
order.changeAdress(newShippingInfo);
// 회원 애그리거트 변경
Member member = memberRepository.findById(id);
member.changeAdress(newShippingInfo.getAddress());
}
}
8. 엔티티와 밸류의 개발적인 관점(JPA 기준)에서 차이
엔티티는 @Entity
를, 밸류는 @Embeddable
로 매핑한다. (밸류가 상속구조일 때 제외) (p.155)
9. DDD 를 JPA로 구체적으로 구현하는 방법
4장부터 참고하자
10. 강결합을 끊는 방법
환불 요청 시 주문+결제+통지 애그리거트 로직이 동시에 처리가 필요한데, 이를 강결합이라고 한다.
이를 끊는 방법으로 (스프링) 이벤트 기법을 사용하였다. (p.303)
11. CQRS 패턴(p.346)
CQRS(Command and Query Responsibility Segregation) 패턴 이란?
상태 변경(생성/변경/삭제) 할 때와 데이터를 조회할 때 다른 모델을 쓴다. 저장소도 다를 수 있고 로직도 다를 수 있다.
왜 쓰는가?
상태 변경할 때의 범위와 조회할 때 범위가 다르고, 웹에서는 조회 트래픽이 월등히 높기 때문에 서로 영향 받지 않기 위해 분리가 필요하다.
(회사에서 검색 데이터를 만들 때 조회용 DB를 따로 쓰라고 전달받았었는데 이게 CQRS 패턴이었나보다.)
레퍼런스
책 - 도메인 주도 개발 시작하기 (최범균)
https://saucelabs.com/resources/blog/to-validate-or-verify
글 읽어주셔서 언제나 감사합니다. 좋은 피드백, 개선 피드백 너무나도 환영합니다.
[후기] 도메인 주도 개발 시작하기
'SearchDeveloper > 책' 카테고리의 다른 글
[정리] 그림으로 배우는 리눅스 구조 (1) | 2024.06.08 |
---|