[DDD Start] - 도메인 모델 시작하기

기석·2022년 5월 31일
0
post-thumbnail

도메인이란

소프트웨어로 해결하고자 하는 문제 영역.
도메인은 여러 개의 하위 도메인으로 나눌 수 있다.

예) 온라인 서점 - 주문, 배송, 회원, 결제, ...


도메인 모델

도메인 모델은 특정 도메인을 개념적으로 표현한 것.
도메인을 모델을 표현할 때 도메인을 이해하는 데 도움이 된다면 표현 방식이 무엇인지는 중요하지 않다.
도메인 모델은 기본적으롷 도메인 자체를 이해하기 위한 개념 모델이다.
개념 모델을 이용해 코드를 작성할 수 있는 것은 아니기에 구현 모델이 필요하다.


도메인 모델 패턴

일반적인 애플리케이션의 아키텍처는 네 개의 영역으로 구성된다.

  • 사용자 UI, 표현
    • 사용자의 요청을 처리하고 사용자에게 정보를 보여준다.
    • 여기서 말하는 사용자란 소프트웨어 이용자 뿐 아니라 외부 시스템을 의미하기도 한다.
  • 응용
    • 사용자가 요청한 기능을 실행한다. 비즈니스 로직을 직접 구현하지 않으며 도메인 계층을 조합해서 기능을 실행한다.
  • 도메인
    • 시스템이 제공할 도메인 규칙을 구현한다.
  • 인프라스트럭쳐
    • 데이터베이스나 메시징 시스템과 같은 외부 시스템과의 연동을 처리한다.

응용, 도메인, 인프라는 스프링의 컨트롤러, 서비스, 리포지토리와 비슷하게 구분된다.


도메인 모델 도출

도메인을 모델링할 때 기본이 되는 작업은 모델을 구성하는 핵심 구성요소, 규칙, 기능을 찾는 것이다.
그리고 이 과정은 요구사항에서부터 출발한다.
주문 도메인을 예로 들면 요구사항 중

  • 출고를 하면 배송지를 변경할 수 없다.
  • 출고 전에 주문을 취소할 수 있다.
  • 고객이 결제를 완료하기 전에는 상품을 준비하지 않는다.

을 보면 '출고 상태로 변경', '배송지 정보 변경', '주문 취소', '결제 완료' 메서드와 주문 상태 정보를 표현하는 Enum이 필요하다는 것을 알 수 있다.

  • 한 상품을 한 개이상 주문할 수 있다.
  • 각 상품의 구매 가격 합은 상품 가격에 구매 개수를 곱한 값이다.

또, 두 요구 사항에 따르면 주문 항목은 주문할 상품, 상품의 가격, 구매 개수를 포함해야 한다.

이렇게 요구 사항에서 도메인 모델을 점진적으로 만들어 나갈 수 있다.


엔티티와 밸류

엔티티

엔티티의 가장 큰 특징은 식별자를 가진다는 것이다.
스프링 JPA에서는 @Id @GeneratedValue를 사용해 표현했다.
보통 식별자는 특정 규칙이나 UUID, 일련번호 등을 사용해 생성한다.

밸류타입

JPA의 Embeddable 개념과 유사해 보인다.
코드 이해하는 것이 빠를 것이다.

public class DeliveryInfo{
	private String receiverName;           // 받는 사람
    private String receiverPhoneNumber;    // 받는 사람
    
    private String shippingAddress1;       // 주소
    private String shippingAddress2;       // 주소
    private String shippingZipcode;        // 주소
}

위 코드처럼 DeliveryInfo 클래스에서 여러 필드들이 하나의 개념을 표현할 때, 밸류 타입으로 묶어줄 수 있다.

@Getter @Setter
public class Receiver{
	private String name;
    private String PhoneNumber;
}

Address 코드 생략

private class DeliveryInfo{
	private Receiver receiver;
    private Address address;
}

이처럼 단지 하나의 개념을 표현하는 필드들을 묶어주는 것 뿐 아니라 필드 하나에 대해서도 만들 수 있다.

밸류 타입을 위한 기능 추가

public class Money{
	private int value;
    
    public Money add(Money money){
    	return new Money(this.value+money.value)
    }
    ...
}

벨류 객체의 데이터를 변경할 때는 기존 데이터를 변경하기 보다는 새 객체를 만드는 방식을 선호한다. 참조 투명성과 관련한 문제가 발생할 수 있기 때문이다.
이처럼 데이터 변경 기능을 제공하지 않는 타입을 불변이라고 표현한다.

참조 투명성과 관련한 문제

Money price = new Money(1000);
OrderLine line = new OrderLine(product, price, 2); // [price=1000, quantity=2, amounts=2000]
price.setValue(2000); //[price=2000, quantity=2, amounts=2000]

이러면 OrderLine의 price 값이 잘못 반영되는 상황이 발생할 수 있다.

도메인 모델에 set 메서드 넣지 않기

의도치 않은 set 호출로 비즈니스 로직과 맞지 않는 예외상황이 발생할 가능성이 높다.

도메인 용어와 유비쿼터스 언어

코드를 작성할 때 도메인에서 사용하는 용어는 매우 중요하다.
도메인에서 사용하는 용어를 코드에 반영하지 않으면 개발자가 코드를 해석 해야하는 부담을 준다.
예)

public OrderStateWrong{
	STEP1, STEP2, STEP3, STEP4, STEP5, STEP6
}
public OrderState{
	PAYMENT_WAITING, PREPARING, SHIPPED, DELIVERING, DELIVERY_COMPLETED
}

에릭 에반스는 도메인 주도 설계에서 언어의 중요함을 강조하기 위해 유비쿼터스 언어라는 용어를 사용했다. 전문가, 관계자, 개발자가 도메인과 관련된 공통의 언어를 만들고 이를 대화, 문서, 도메인 모델, 코드, 테스트 등 모든 곳에서 같은 용어를 사용한다.

profile
블로그 이사갔어요 https://kiseoky.tistory.com

0개의 댓글