1. 도메인이란?
온라인 서점을 우리가 구현한다 생각해보자.
이 때 온라인 서점은 소프트웨어로 해결하고자 하는 문제 영역 -> 즉, 도메인에 해당한다.
한 도메인은 여러 개의 하위 도메인으로 나눌 수 있다.
온라인 서점을 예로 들면,
주문, 회원, 혜택, 리뷰, 정산, 배송 등이 있을 것이다.
2. 도메인 전문가와 개발자 간 지식 공유
요구사항을 제대로 이해하지 않은 채 개발을 하다보면 요구하지 않은 엉뚱한 기능들을 만들 수 있다.
이러한 잘못 개발된 코드를 수정하려면 많은 자원이 투입된다.
그렇다면 요구사항을 올바르게 이해하려면 어떻게 해야할까?
바로 개발자와 전문가가 직접 대화하는 것이다.
개발자와 전문가 사이에 내용을 전달하는 전달자가 많을수록 정보과 왜곡되고 손실이 발생하게 된다.
tip) 도메인 전문가라고 해서 항상 올바른 요구사항을 주는 것은 아니다.
그렇기에 개발자는 요구사항을 이해할 때 왜 이런 기능을 요구하는지 또는 실제로 원하는 것이 무엇인지 생각하고 전문가와 대화를 통해
진짜 원하는 것을 찾아야 한다.
3. 도메인 모델
도메인 모델은 특정 도메인을 개념적으로 표현한 것이다.
주문 도메인을 예시로 들자면,
1. 상품을 몇개 살지 선택하고 배송지를 입력
2. 선택한 상품 가격을 이용해 총 지불 금액을 계산
3. 금액 지불을 위한 결제 수단 선택
4. 배송 전이라면 배송지 주소를 변경하거나 주소를 취소 가능
해당 모델을 객체 모델로 구현하면 이렇다.
도메인 모델은 기본적으로 도메인 자체를 이해하기 위한 개념 모델이다.
개념 모델을 이용해서 바로 코드를 작성할 수 있는 것은 아니기에, 구현 기술에 맞는 구현 모델이 따로 필요하다.
도메인은 다수의 하위 도메인으로 구성된다.
각 하위 도메인이 다루는 영역이 서로 다르기에, 같은 용어라도 의미가 다를 수 있다.
도메인에 따라 용어 의미가 결정되므로, 여러 하위 도메인을 하나의 다이어그램에 모델링하면 안된다.
모델의 각 구성요소는 특정 도메인으로 한정할 때 비로소 의미가 완전해지기 때문에각 하위 도메인마다 별도로 모델을 만들어야 한다.
4. 도메인 모델 패턴
일반적인 애플리케이션의 아키텍쳐는 다음과 같다.
사용자 인터페이스(UI) | 사용자의 요청 처리 및 사용자에게 정보를 보여줌. |
응용(Application) | 사용자가 요청한 기능 실행. 비즈니스 로직을 직접 구현하지 않으며 도메인 계층을 조합해 기능 실행 |
도메인 | 시스템이 제공할 도메인 규칙을 구현 |
인프라(Infrastructure) | DB나 Message System처럼 외부 시스템과의 연동 처리 |
도메인 계층은 도메인의 핵심 규칙을 구현한다.
예를 들어 주문 도메인의 경우 '출고 전에 배송지를 변경할 수 있다.', '주문 취소는 배송 전에만 할 수 있다.'라는 규칙을 구현한 코드가
도메인 계층에 위치하게 된다.
핵심 규칙을 구현한 코드는 도메인 모델에서 구현해야 한다.
도메인 모델에만 위치함으로써, 규칙이 바뀌거나 확장되어야할 때 다른 코드에 영향을 덜 줌과 동시에 변경 내역을 모델에 반영할 수 있다.
개념 모델을 만들 때 처음부터 완벽하게 도메인을 표현하는 모델을 만드는 시도를 할 수 있지만, 이는 불가능하다.
초기에 이해한 도메인 지식이 시간이 지남에 따라 새로운 통찰을 얻어 완전히 다른 의미로 해석되는 경우가 있기 때문이다.
그렇기에 처음부터 완벽한 개념 모델을 만들기보다는, 전반적인 개요를 알 수 있는 수준으로 개념 모델을 작성하고
구현하는 과정에서 개념 모델을 구현 모델로 점진적으로 발전시켜 나가야 한다.
5. 도메인 모델 도출
도메인을 모델링할 때 기본이 되는 작업은 모델을 구성하는 핵심 구성요소, 규칙, 기능을 찾는 것이다.
이 과정은 요구사항 분석에서 시작한다.
6. 엔티티와 밸류
- 엔티티
엔티티의 가장 큰 특징은 식별자(id)를 가진다는 것이다.
식별자는 엔티티 객체마다 고유해서 각 엔티티는 서로 다른 식별자를 가지고 있다.
또한 엔티티를 생성하고 속성을 바꾸고 삭제할 때까지 식별자는 유지된다.
엔티티의 식별자는
특정 규칙(예를 들면 현재시간 + 특정 값)에 따라 생성을 하거나
UUID 등의 고유 식별자 생성기를 사용하거나
직접 값을 설정, Sequence 등의 일련번호를 사용한다. - 밸류
해당 코드를 봐보자.
public class ShippingInfo {
private String receiverName;
private String receiverPhoneNumber;
private String shippingAddress1;
private String shippingAddress2;
private String shippingZipcode;
}
receiverName과 receiverPhoneNumber는 서로 다른 데이터를 담고 있지만 두 필드는 개념적으로 받는 사람을 의미한다.
또한 아래의 shippingAddress1, shippingAddress2, shippingZipcode 역시 주소라는 하나의 개념을 표현한다.
밸류는 개념적으로 완전한 하나를 표현할 때 사용한다.
각각의 변수를 묶어서 하나로 표현해보자.
@Getter
@AllArgsConstructor
public class Receiver {
private String name;
private String phoneNumber;
}
@Getter
@AllArgsConstructor
public class Address {
private String address1;
private String address2;
private String zipcode;
}
밸류를 활용하면 아까의 코드는 해당 코드로 변경된다.
@Getter
@AllArgsConstructor
public class ShippingInfo {
private Receiver receiver;
private Address address;
}
밸류 타입이 꼭 두 개 이상의 데이터를 가질 필요는 없다.
의미를 명확하게 표현하기 위해 밸류 타입을 사용하는 경우도 있다.
public class OrderLine {
private Product product;
private int price;
private int quantity;
private int amounts;
}
price와 amounts는 int 타입이지만 해당 변수들은 돈을 의미한다.
그렇기에 돈을 의미하는 Money 라는 타입을 만들어 사용하면 코드를 이해하는데 도움이 된다.
또한 기능을 추가할 수 있다.
public class Money {
private int value;
public Money add(Money money) {
return new Money(this.value + money.value)
}
public Money multiply(int multiplier) {
return new Money(value * multiplier);
}
}
밸류 객체의 데이터를 변경할 때는 기존 데이터를 변경하기보다는 변경한 데이터를 갖는 새로운 밸류 객체를 생성하는 방식을 선호한다.
지금 위에 코드도 생성자를 통해 새로운 객체를 생성해주고 있다.
지금 Money 클래스를 보면 값을 변경하는 기능이 없다. (불변)
밸류 타입을 불변으로 구현하는 이유는 여러가지가 있지만, 가장 중요한 이유는 코드의 안정성 때문이다.
만약 setValue 등의 메서드를 제공한다면 엉뚱한 곳에서 값이 변경될 수 있기 때문이다.
7. 도메인 용어와 유비쿼터스 언어
public OrderState {
STEP1, STEP2, STEP3, STEP4, STEP5
}
이 코드는 개발자가 전체 상태를 단계 별로 표현한 것이다.
이름에 중요한 도메인 규칙이 드러나지 않는다.
실제 이 코드를 이해하려면 각 STEP 들이 어떤 상태를 의미하는지 찾아봐야 한다.
public enum OrderState {
PAYPMENT_WAITING, PREPARING, SHIPPED, ...
}
해당 코드처럼 도메인에서 사용하는 용어를 최대한 코드에 반영하면 변수명을 보고 바로 이해할 수 있다.
도메인 주도 설계에서는 언어의 중요함을 강조하기 위해 유비쿼터스 언어를 강조한다.
전문가, 관계자, 개발자가 도메인과 관련된 공통의 언어를 만들고 모든 곳에서 같은 용어를 사용함으로써
소통과정에서 발생하는 용어의 모호함을 줄이고 개발자는 도메인과 코드 사이에서 불필요한 해석 과정을 줄일 수 있다.
'책 정리 > 도메인 주도 개발 시작하기' 카테고리의 다른 글
Chapter 6. 응용 서비스와 표현 영역 (1) | 2023.12.12 |
---|---|
Chapter 5. 스프링 데이터 JPA를 이용한 조회 기능 (0) | 2023.12.08 |
Chapter 4. 리포지터리와 모델 구현 (0) | 2023.12.05 |
Chapter 3. 애그리거트 (0) | 2023.12.01 |
Chapter 2. 아키텍처 개요 (0) | 2023.11.28 |