티스토리 뷰

Programing

DDD - #1 도메인

oh-yes 2019. 6. 8. 20:54
DDD_#1

DDD Start의 1장 내용을 정리

도메인

소프트웨어로 해결하려고 하는 문제의 영역.

  • 하나의 도메인은 다시 하위 도메인으로 나눌 수 있는데, 이렇게 나눠놓은 하위 도메인끼리 연동하여 완전한 기능을 제공하게 된다.
  • 이렇게 문제의 영역을 해결하는 과정에서 꼭 모든 기능을 소프트웨어로 구현해야 할 필요는 없다. 즉, 해당 도메인에서 필요한 기능을 외부 업체 또는 기능을 활용하여 연동 할 수도 있다.

도메인 모델

문제 영역을 개념적으로 표현

  • 도메인에 대한 모델을 여러 이해 당사자가 이해할 수 있는 개념적 모델링을 할 수 있다.
    • 단순히 구현 수준에서의 모델이 아닌, 여러 이해 당사자가 이해할 수 있는 모델로서 작성하는 것이 의미가 있다.
  • 표현하는 방법은 해당 도메인에 따라, 달라질 수 있다
    • UML, 그래프 등 가장 상황에 알맞는 표현을 사용하는 것이 좋다.
  • 구현 모델은 개념 모델과 달라질 수 있는데, 개념 모델을 최대한 따르도록 작성하는 것이 좋다.

도메인은 여러 하위 도메인으로 구성 될 수 있는데, 하위 도메인이 다루는 영역별로 같은 혹시 비슷한 용어도 다르게 해석될 수 있다. 다시 말해, 여러 하위 도메인을 하나의 표현 모델로 모델링하면 혼선이 발생할 수 있기 때문에 좋지 않은 방법이라고 할 수 있다.

도메인 모델 패턴

  • 표현(Presentation)
    사용자의 요청을 처리, 여기서 사용자는 실 사용자 혹은 외부 시스템이 될 수도 있다.
  • 응용(Application)
    요청 받은 기능을 실행, 업무 로직을 직접 구현하지 않으며, 도메인 계층을 조합하여 기능을 실행
  • 도메인(Domain)
    도메인을 규칙을 실행
  • 인프라스트럭쳐(Infrastructure)
    저수준에서의 즉, 데이터베이스 혹은 메시징과 같은 시스템 종속적인 부분을 처리

엔티티/벨류

엔티티와 벨류를 제대로 구분해야 도메인을 올바르게 설계하고 구현할 수 있기 때문에 구분하여 사용하는 것은 매우 중요하다.

엔티티

class Order(
	val orderNumber: String, // 식별자
	...
)
  • 고유의 식별자를 가진다.
    • 식별자를 위한 고유의 벨류 값을 만들어서 사용 할수도 있다.
  • 엔티티의 식별자를 생성하는 시점은 도메인의 특징 혹은 사용하는 기술에 따라 달라질 수 있다.
    • 도메인의 유의미한 규칙을 사용
      • 비슷한 도메인 상황이더라도, 사용하는 식별자 생성 규칙이 달라질 수 있다.
    • UUID
    • 값을 직접 입력
      • 회원가입에서 사용하는 이메일 혹은 아이디일 경우
    • 일련번호 사용
      • 데이터베이스의 시퀀스(오라클 등) 혹은 자동증가 값을 사용하는 경우

벨류

class OrderLine(
	var product: Product,
	var price: Int,
	var quantity: Int,
	var amounts: Int
)
  • 값을 표현하는 클래스/객체로서 개념적으로 완전한 하나를 표현할 떄 사용
  • 일반적으로 하나 이상의 필드/멤버 값을 포함하는 경우가 많으나, 반드시 하나 이상을 포함해야 하는 것은 아니다. 의미를 좀 더 명확하게 사용하기 위해 사용하는 경우도 있다. 또한, 벨류 타입을 위한 기능을 추가할 수 도 있다.
  • 벨류 객체의 데이터를 변경 할때는 기존 데이터를 변경하는 방식(세터를 이용하는 방식 등)보다는 새로운 벨류 객체를 생성하는 방식을 선호한다.(Immutable한 접근)

    세터를 이용하는 방식을 사용할 경우, 참조 투명성과 관련된 문제가 생길 수 있다. 즉, 참조 값이 바뀌지 않은 상태에서 상태 값에 대한 변경이 의도하지 않은 상황에서 생길 수 있다.

도메인 모델에 세터를 사용하지 않기

  • 세터를 사용하는 방식은 도메인의 핵심 개념이나 의도를 명확하게 표현하기 힘들게 한다
  • 도메인 객체를 생성하는 시점에 완벽한 상태가 아닐 수 있게 되므로, 잠정적인 에러 가능성을 가질 수 밖에 없다.
  • 이를 막기 위해서는, 객체를 생성하는 시점에 완벽한 상태 값을 주입해주는 것이다. 즉, 생성자 혹은 팩토리 매소드 등을 활용하여 필요한 객체 혹은 데이터를 모두 받아야 한다.
class Order private constructor (
	val orderNumber: OrderNo,
	val totalAmount: Money,
	val shippingInfo: ShippingInfo
	val orderLine: List<OrderLine>,
	val orderState: OrderState = OrderState.PAYMENT_WAITING,
	val createdAt: LocalDateTime = LocalDateTime.now(),
	val updatedAt: LocalDateTime = LocalDateTime.now()
) {
	companion object {
		fun create(
			orderNumber: OrderNo,
			totalAmount: Money,
			shippingInfo: ShippingInfo
			orderLine: List<OrderLine>
		) = Order(
			orderNumber = orderNumber,
			totalAmount = totalAmount,
			shippingInfo = shippingInfo,
			orderLine = orderLine,
			orderState = OrderState.PAYMENT_WAITING,
			createdAt = LocalDateTime.now(),
			updatedAt = LocalDateTime.now()			
		)
	}
}

Kotlin의 기본값 할당(Default Value Assignment)을 활용하면 필수 값과 옵션 값을 나눠서 할당 받을 수 있어서 편리하다.

도메인 용어

  • 도메인의 용어를 코드에서 충분하게 반영하지 않으면, 개발자들은 코드에 대한 의미 해석에 부담을 주게 된다.
// 이게 뭔말이야...
emum class OrderState {
	STEP1, STPE2, STEP3 ...
}
  • 실제 도메인에 맞는 유의미한 단어 선택을 통하여, 코드를 해석하는 과정을 줄이는 것이 중요하다. 이를 위해서는 적절한 단어 선택을 위한 이해 관계자들의 노력이 필요하다.
emum class OrderState(val statement: String) {
	PAYMENT_WAITING("입금대기"),
	PREPARING("배송준비"), 
	SHIPPED("출고"), 
	DELIVERING("배송중"), 
	DELIVERY_COMPLETED("배송완료")
}

'Programing' 카테고리의 다른 글

DDD - #3 애그리거트(Aggregate)  (0) 2019.07.10
DDD - #2 아키텍처  (0) 2019.06.08
[Sublime Text 3] 다시 설치하기.  (0) 2016.05.30
[Intellij] 01. 인텔리J 시작하기(맥)  (0) 2016.02.22
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함