Heeveloper

[DDD Start]1장. 도메인 모델의 시작

2020-06-28
Heeveloper
DDD

1. 도메인

도메인(Doamin)이란 소프트웨어로 해결하고자 하는 문제 영역에 해당한다.
도메인은 여러 하위 도메인으로 구성된다.

2. 도메인 모델

특정 도메인을 객체, 상태 다이어그램 등을 사용해서 개념적으로 표현한 것.

  • 도메인 자체를 이해하기 위한 개념 모델이다
  • 개념 모델에 기반해서 구현 기술(객체지향언어 등)에 맞는 구현 모델 이 따로 필요하다.

3. 도메인 모델 패턴

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

  1. Presentation(UI) Layer : 사용자의 요청을 처리하고 사용자에게 정보를 보여줌. 이 때 사용자는 유저뿐만 아니라 외부 시스템도 될 수 있다.
  2. Application Layer : 사용자의 요청한 기능을 수행. 업무 로직을 직접 구현하지 않고 도메인 계층을 조합해 기능을 실행한다.
  3. Domain Layer : 시스템이 제공할 도메인 규칙 을 제공한다.
  4. Infrastructure Layer : DB나 메세징시스템 등 외부 시스템과의 연동을 처리한다.

도메인 계층은 도메인의 핵심 규칙을 구현한다. ex) 출고 전에 배송지를 변경할 수 없다.
-> 핵심 규칙은 도메인 모델에만 위치하기 때문에 규칙이 바뀌거나 확장해야 할 때 다른 코드에 영향을 덜 주고 변경 내역을 모델에 반영할 수 있게 된다.

4. 도메인 모델 도출

도메인을 모델링할 때 요구사항으로부터 핵심 구성요소, 규칙, 기능을 찾는다.
ex) 출고를 하면 배송지 정보를 변경할 수 없다.

public class Order {
  public void changeShipped() {...}
  public void changeShippingInfo(ShippingInfo newShipping) {...}
  public void cancel() {...}
  public void completePayment() {...}
}

5. Entity와 Value

  • Order 엔티티와 Address 벨류
  • OrderItem 하위 엔티티

Entity

엔티티의 가장 큰 특징은 고유한 값의 식별자 를 갖으며, 다음 중 한 가지 방식으로 생성한다.

  • 특정 규칙에 따라 생성 (ex. 201906181133)
  • UUID (ex. java.util.UUID)
  • 값을 직접 입력 (ex. email)
  • 일련번호 사용(시퀀스나 DB의 자동 증가 칼럼 사용)

Value

벨류 타입은 개념적으로 완전한 하나를 표현할 때 사용한다. (ex. Receiver, Address)

public class ShippingInfo {
  private Receiver receiver;
  private Address address;
  ...
}

public class Receiver {
  private String name;
  private String phoneNumber;
  ...
}

public class Address {
  private String address1;
  private String address2;
  private String zipCode;
  ...
}
  • Receiver는 ‘받는 사람’이라는 도메인 개념을 표현한다.

또한 의미를 명확하게 하기 위해 벨류 타입을 사용하기도 한다.

public class OrderLine {
  private Product product;
  // private int price;
  private Money price;
  ...
}

public Class Money {
  private int value;
  ...
  
  public Money add(Money money) {
    return new Money(this.value + money.value);
  }
  
  // value를 변경할 수 있는 메서드 없음
}
  • ‘돈’을 의미하는 Money 타입을 만들어 사용하면 코드의 가독성을 높여준다.
  • 벨류 타입을 위한 기능을 추가할 수 있다.
  • 데이터 변경 기능을 제공하지 않음으로써 불변(Immutable) 타입으로 설정할 수 있어 안전한 코드를 작성할 수 있다.
    • 불변성의 장점 : 참조 투명성과 스레드에 안전한 특징

벨류 타입의 두 객체가 같은지 비교하기 위해선 모든 속성이 같은지 비교해야 한다.

엔티티 식별자와 밸류 타입

엔티티 식별자의 경우 보통 String 으로 구성된 경우가 많은데, 이런 식별자는 단순한 문자열이 아니라 도메인에서 특별한 의미를 지니는 경우가 많다. 때문에 식별자를 위한 밸류 타입을 사용해서 의미가 잘 드러나도록 할 수 있다.

public class Order {
  private OrderNo id;
  ...
}

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

set 메서드는 도메인의 핵심 개념이나 의도를 코드에서 사라지게 한다.

public class Order {
  ...
  // public void setShippingInfo(ShippingInfo newShipping) {...}
  public void changeShippingInfo(ShippingInfo newShipping) {...}
}
  • changeShippingInfo() 가 배송지 정보를 새로 변경한다는 의미를 가졌다면, setShippingInfo()는 단순히 배송지 값을 설정한다는 것을 뜻한다.
  • 또한 completePayment() 를 통해 OrderState 를 변경하는 것과 달리 setOrderState() 를 통해 단순히 상태값만 변경하게되면, 상태값에 따라 다른 처리를 위한 코드를 함께 구현할지 애매해져 점차 도메인 지식이 코드에서 사라지게 된다.

set 메서드의 다른 문제는 도메인 객체를 생성할 때 완전한 상태가 아닐 수 있다.

// 처음 Order 생성시점에 완전하지 않은 order 생성
Order order = new Order();

order.seOrderLine(lines);

// 주문자(Orderer)를 설정하지 않은 상태에서 주문 완료 처리
order.setState(OrderState.PREPARING)
  • 도메인 객체가 불안전한 상태로 사용되는 것을 막으려면 생성 시점 에, 즉 생성자 를 통해 필요한 데이터를 모두 받아야 한다.
  • 생성자로 필요한 것을 받고 호출하는 시점에 유효성 검사 까지 함께 할 수 있다.
public class Order {
  public Order(Orderer orderer, List<OrderLine> orderLiens, ...) {
    setOrderer(orderer);
    setOrderLines(orderLiens);
    ...
  }
  
  ...
  private setOrderlines(Orderer orderer) {
    verifyAtLeastOneOrMoreOrderLines(orderLiens); // 검증 로직
    ...
  }
}
  • 위의 set 메서드는 앞서 set 메서드와 중요한 차이점이 있는데, 접근 범위가 private 이기 때문에 외부에서 사용할 수 없다.
  • 불변 밸류 타입 을 사용하면 자연스럽게 set 메서드를 구현하지 않는다. set메서드를 사용할 특별한 이유가 없다면 불변 타입의 장점을 살릴 수 있도록 밸류 타입은 불변으로 구현한다.

DTO의 get/set 메서드

DTO(Data Transfer Object)는 계층간 데이터를 서로 주고받을 때 사용하는 일종의 구조체이다. 오래 전 프레임워크는 요청 파라미터나 DB 칼럼값을 설정할 때 set 메서드가 필요했다. DTO가 도메인 로직을 담고 있지는 않기에 get/set 메서드를 제공해도 도메인 객체의 이관성에 영향을 줄 가능성은 작다.

최근의 프레임워크나 개발 도구는 set이 아니어도 private 필드에 직접 값을 할당할 수 있는 기능(ex. Spring - @Annotation)을 제공하고 있으며, 이를 통해 DTO도 불변 객체 가 되어 불변의 장점을 DTO 까지 확장할 수 있다.

6. 도메인 용어

도메인 용어를 사용하여 최대한 도메인 규칙을 코드로 작성하여 가독성을 높이도록 한다.

public Orderstate{
  // STEP1, STEP2, STEP3, STEP4 ,..
  PAYMENT_WAITING, PREPARING, SHIPPED, ...
}



Similar Posts

Comments

-->