hongcheol / CS-study

cs지식을 정리하는 공간
MIT License
248 stars 30 forks source link

DDD(Domain Driven Design) #113

Open lksa4e opened 3 years ago

lksa4e commented 3 years ago

DDD

DDD(도메인 주도 설계)

DDD의 등장

아키텍쳐 분석 설계와 프로젝트 진행에 있어서 가장 중요한 것중의 하나가 "고객의 요구 사항을 어떻게 파악할것이며 이를 어떻게 모델화 하느냐?" 입니다. 모델 자체가 목적이 아니라 모델이라는 공통된 언어를 사용하여 고객과 소통을 하고 요구 사항을 가급적 정확하게 도출하여 나중에 미도출된 요구사항이나 잘못 분석된 요구사항이 없도록 하는 것이 중요한 포인트 중에 하나입니다.

그리고 프로젝트중에는 프로젝트 구성원, 고객, 각기 다른팀, 비지니스 전문가, 분석가, 아키텍트들간의 생각하고 있는 개념이나 목표가 서로 다른 경우가 있습니다. 그래서 시스템을 구축해놓고 나면 결국 각자가 생각한것과 다른 결과물이 나오거나 구현이 서로 기대했던 바와 달라서 모듈간 통합에서 문제가 발생하거나 많은 비용과 자원이 소요되는 경우가 있습니다.

이런 모든 문제의 공통적인 원인은 프로젝트의 구성원 자체가 현재 프로젝트의 목적,현황,디자인,업무,각자의 역할등 프로젝트에 대한 Context 에 대해서 제대로 이해하지 못해서 발생하는 문제입니다. 어떻게 보면 Communication의 문제입니다. 프로젝트내에서 이 Context 가 제대로 공유된다면 같은 생각과 같은 현황 파악으로 인해서 프로젝트가 잘못된 방향으로 흘러가는 것을 상당 부분 예방할 수 있습니다.

이러한 관점에서 시작된것이 Domain Driven Design 인데, 기본적으로 도메인(업무 또는 현업)과 개발팀간 그리고 각기 다른 업무 개발팀간의 의사소통 방법과 소프트웨어 디자인 방법에 대해서 설명하고 있습니다.

Domain이란?

도메인의 사전적 의미는 '영역'이라는 뜻입니다. 소프트웨어 개발에서 말하는 도메인은 '프로그램이 쓰이는 대상 분야'이고, 소프트웨어로 해결하고자 하는 문제 영역입니다.

도메인이 무엇인가에 대해서 쉽게 이해하려면 "도메인이 무엇인지"보다는 "도메인에 포함되는 것이 무언인가?" 에 대해서 생각하면 쉽습니다.

이커머스 시스템을 예로 들면, 이커머스 분야에서는 주문, 상품, 결제 같은 개념이 필요합니다. 이 개념들이 이커머스 시스템의 도메인에 속합니다. 의료 시스템에서는 주문, 배송이란 개념은 필요 없다. 병원, 의사, 진료 같은 개념이 필요합니다. 즉, 시스템마다 포함하는 개념 및 요소가 다르게 되고, 이를 도메인이라고 부릅니다. image

DDD란?

1) 비즈니스 Domain별로 나누어 설계하는 방식입니다.

2) DDD의 핵심 목표는 "Loosly coupling", "High cohesion"입니다. (어플리케이션 또는 그 안의 모듈간의 의존성은 최소화하고, 응집성은 최대화)

3) DDD는 Strategic Design과 Tactical Design으로 나눌 수 있습니다. Strategic Design은 개념 설계이고 Tactical Design은 프로그래밍하기 위한 구체적 설계라고 할 수 있습니다.

도메인 모델을 정제하기 위한 프로세스

image

  1. 시나리오 정의 내리기
  2. 시나리오에 맞춰 모델 구성
  3. 해당 모델로 코딩
  4. 시나리오를 수정
  5. 문제가 생기면 위의 사항들을 반복

1. 시나리오 구성 (Strategic Design)

Domain을 기준으로 Context에 대해 생각하고 이를 기준으로 디자인 하는 것을 Strategic Design이라고 합니다.

Context 란 특정 객체 혹은 상황이 벌어지는 주변 환경을 말합니다. 가장 쉬운 예로 가게 안에 접시에 담긴 피자와 혹은 길에 버려진 피자를 생각해 봅시다. 같은 피자이지만 피자가 매장 안의 접시에 있는지 혹은 길에 있는지에 따라 유료, 무료의 차이가 생기게 되고 사실상 다른 것으로 간주될 수 있습니다. 이처럼 같은 사물이나 행동 양상이 벌어지는 상황에 집중하여 디자인을 하는 것이 Strategic Design 의 핵심이라고 할 수 있다.

Strategic Design을 이해하기 위해 간단한 예로 주택을 짓는 경우를 생각해 봅시다. 우리가 원하는 집을 짓기 위해서 우리가 하는 행동 절차는 다음과 같습니다.

  1. 어떤 주택을 지을지 생각을 해 본다.
  2. 주택을 지을 때 어떤 핵심적인 가치에 집중하여 집을 지을지를 선택합니다. 가령 헛간이 넓어야 한다던가, 수영장이 커야 한다. 가 중점적인 사항을 명시합니다.
  3. 그 형상을 실제 집으로 만들어 내기 위해 모델링을 하고
  4. 이를 토대로 구체적인 설계도를 그려 나갑니다. 이 설계도에는 집의 아주 구체적인 부분들이 명시되게 됩니다.

이렇게 DDD를 통한 설계 과정에서 사용되는 용어는 다음과 같습니다.

먼저 집 전체에 대한 설계의 전체를 우리는 domain 이라고 부르며, 커다란 집의 각각의 부분 집합인 헛간, 농장, 수영장 등 큰 파트들을 subdomain 이라고 부릅니다. 또 각각의 subdomain에 대해 각 subdomain의 문맥적 상황을 bounded context 라고 부르며, 실제 subdomain의 구체적인 형상을 나타내는 것을 domain model 이라고 부릅니다.

여기서 bounded context 가 가지는 의미는 바로 특정 모델이 어떤 bounded context 에 속하는 가에 따라서 다른 의미를 가지기 때문입니다. 가령, 주택 건축시에 정문에서 caretaker라는 모델이 있다면 이는 바로 경비원을 말하는 것일 겁니다. 하지만 caretaker라는 단어가 메인 주거 건물 안에서 가지는 의미는 이와 다를 수 있습니다.

이 내용을 간단히 정리하면 다음과 같습니다.

Context

의미를 결정하는 것 처럼 보이는 단어 나 문장이 나타나는 설정으로, 모델에 관한 문장은 context 안에서만 이해될 수 있다. 주택을 구성하는 각 부분 구간들에 대한 환경을 말한다.

Model

도메인의 특정 양상을 묘사하는 추상화 시스템으로 도메인과 관련된 문제를 해결하는 데 사용된다.

Ubiquitous Language

소프트웨어를 만들기 위해서는 많은 사람들이 원활히 소통해야 하고 여기에는 다양한 용어들이 사용된다.

가령, 기획자, 디자이너, 개발자가 모인 자리에서 각자 서로의 언어로 대화를 한다면 이는 원활한 커뮤니케이션을 심각하게 저해하게 된다. 이 경우에 필요한 것이 Ubiquitous Language이며 이는 domain model 을 둘러싼 언어구조를 말한다.

이 언어는 팀 전체가 각각의 업무 파트에서 공통적으로 사용될 수 있어야 하며, 실제 개발의 측면에서 모든 기획자, 디자이너, 개발자가 공통된 어휘를 사용해야 서로간에 이견이 없을 것이며 이러한 공통된 어휘를 ubiquitous language라고 합니다.

Bounded Context

위에서 설명한 Context에 대한 구체적인 설명으로, 특정 모델이 정의되고 적용될 수 있는 영역을 이야기 합니다. 주택을 짓는 경우에 빗대어 생각해 볼 때, Bounded Context는 주택 전체를 구성하는 헛간, 농장, 수영장, 메인 주택 등의 큰 요소들 각각을 둘러싼 상황을 의미합니다.

특정 모델은 어떤 bounded context에 놓이는가에 따라 다르게 이해될 수 있습니다. 실제 소프트웨어를 구축함에서의 예를 들면 가령 sales를 담당한하는 subdomain이 있을 수 있고, 이를 지원하는 support와 accounting 라는 subdomain 이 존재할 수 있습니다. 이러한 각각의 subdomain이 놓인 환경인 bounded context 내에서 특정 모델 customer 가 보여지는 시각은 매우 상이할 수 있습니다. sales 팀에서 고객을 보는 시각은 주로 사회적 관심사, 좋아하는 것, 욕구 등의 것일 겁니다. 하지만 accounting의 측면에서는 사용자는 그저 하나의 계정으로써 그 사람의 결제정보 만이 중요한 정보일 수 있습니다. 즉 각기 다른 bounded context에서 ubiquitous language는 비록 표현은 같지만 다른 의미를 가지게 됩니다.

Context Map

각 bounded context들 사이의 관계를 말하며 즉, 주택 건축시에 헛간, 뒷간, 수영장 등 큰 요소들이 어떤식으로 서로 연관이 되어 있는지를 나타낸다.

Domain Model

Domain Model 이란 실제 세계를 반영하는 구체적인 설계로, 주택 건축시에 주택을 구성하는 메인 주택의 구체적인 설계도를 말한다. ### 2. 모델 구성하기 (Tactical Design) Strategic Design을 통해 설계한 모델을 프로그래밍을 통해 구체적인 설계를 하는 것이 Tactical Desgin입니다. DDD를 하기 위하여 도메인부터 생각해 봐야합니다. 쇼핑몰을 만들 때 주문이라는 도메인을 보죠. 이 도메인에는 어떠한 시나리오들이 있을까요? - 사용자가 제품을 주문서를 통해 주문한다. - 사용자가 주문한 내용을 취소한다. - 사용자가 주문서를 확인한다. 이 정도만 봐도 도메인 모델들이 보여집니다. <사용자>, <제품>, <주문서> 등이 있습닏아. ```Java * 주문서 모델 public class OrderForm { private String orderId; private String userId; private List itemList; private String address; } * 사용자 모델 public class User { private String userId; private String userName; private String password; } * 아이템 모델 public class Item { private String itemId; private String itemName; } ``` 위는 모델을 간단하게 코드로 구현한 것입니다. 하지만 모델을 구성하면서 참고해야할 DDD의 권고 사항과 기본 요소들이 더 있습니다. - Entity, Value - 모델에 set 메서드를 무조건 추가하는것은 자제 - Aggregate

Entity & Value

>Entitye 고유한 식별자를 갖는 모델 > Value 1. Value 모델은 불변을 원칙으로 합니다. 2. 식별자가 존재하지 않습니다. 3. 의미를 명확하게 표현하거나 두 개 이상의 데이터가 하나의 범위로 묶일 수 있는 경우 Value 모델로 이용합니다. 위와 같은 정의를 보았을 때 Entity는 쉽게 이해가는 모델입니다. 위에서 정의한 OrderForm, User, Item들은 각각 id로 식별자가 존재하기 때문에 모두 Entity 모델입니다. 하지만 Value의 경우는 조금 다르게 해석됩니다. 예시를 보겠습니다. 위의 모델 예시에서 주문서 모델 내에 address(주소)는 단순히 String으로 저장해도 이용하는데 문제는 없습니다. 하지만 시, 도, 군, 상세주소 등으로 나눌 수 있겠죠? 즉, 두 개 이상의 데이터가 하나의 범위로 묶일 수 있습니다. 그리고 주소는 주소 그 자체가 식별자이지 따로 식별자를 두는 것은 어색합니다. User의 경우에는 userName이 중복이 가능하다면 userName만 가지고는 식별할 수는 없으니 식별자를 두는것이 타당하지만요. 따라서 Address를 Value 모델로 바꾸면 아래와 같습니다. ```Java * 개선된 주문서 모델 public class OrderForm { // Entity 모델 private String orderId; private String userId; private List itemList; private Address address; } private class Address { // Value 모델 private String city; private String dong; private String detailAddress; } ```

set 메서드를 마음대로 추가하면 안되는 이유

- 도메인의 핵심 개념이나 의도를 망칠 수 있다. - set 메서드를 사용하는 것은 도메인 객체가 불완전한 상태로 존재 할 수 있다는 것이다. - 불완전한 상태로 존재하는 도메인 자체는 또 다른 도메인으로 나뉠 수 있다. - 불완전한 상태의 도메인을 막으려면 생성 시점에 필요한 것을 전달해 주어야 한다. 즉, 도메인의 개념에 맞게 사용하려면 set 메서드가 아닌 생성자를 사용해서 객체를 선언하고 사용해야한다는 것입니다. 예를 들어서 위의 User(사용자) 도메인 모델에서 password를 관리하는 상황을 예시로 들어보겠습니다. 일반적으로 DB에 password 그대로 저장하는 일은 드뭅니다. 보안을 위해 인코딩을 거쳐서 저장하는 것이 일반적이죠. DDD 설계 원칙을 적용하지 않았을 때에는 아래와 같이 생각할 수 있습니다. > User를 처음 등록할 때 Controller에서 User객체로 받았다면 그 객체에서 password를 get->인코딩->set->User객체 DB 저장 해야겠네?! 위와 같은 상황에서 DDD설계 원칙을 적용한다고 했을 때 <인코딩을 하는 행위>는 가 아니라 가 되는 것입니다. 단순히 set메서드를 만들어 필드값만 변경하는 메소드를 만들지 말고 Entity가 해야할 행위를 구현하는 메서드를 만들라는 의미입니다. 물론 단순히 필드값을 변경하는 행위가 있을 수 있고 set과 다름없이 동작하는 로직이더라도 그 메소드의 이름은 이 아니게 될겁니다. 그래서 User 엔티티 모델이 encodePassword라는 메소드를 가지게 하고 비지니스 로직 중간에 해당 메서드를 통해서 사용하는 형태가 됩니다. ```Java * password 인코딩이 추가된 User 모델 public class User { private String userId; private String userName; private String password; public static void encodePassword() { encode(this.password); } } ```

Aggregate

- 관련 객체를 하나로 묶은 군집 - Aggregate는 군집에 속한 객체들을 관리하는 루트 엔티티를 갖는다. - Aggregate로 묶어서 바라보면 좀 더 상위 수준에서 도메인 모델 간의 관계를 파악할 수 있다. - Aggregate에 속한 객체는 유사하거나 동일한 라이프사이클을 갖는다. - 한 Aggregate에 속한 객체는 다른 애그리거트에 속하지 않는다. - 대부분의 Aggregate는 한개의 엔티티 객체를 가지며, 두 개 이상의 앤티티로 구성되는 애그리거트는 드물게 존재한다. - 각 Aggregate는자기 자신을 관리할뿐 다른 애그리거트는 관리하지 않는다. (예) 주문 애그리거트는 배송지 변경, 주문 상품 변경 등 자신은 관리하지만 회원 비밀번호 변경, 상품 가격 변경 등은 하지 않는다. ![image](https://user-images.githubusercontent.com/55073084/135731371-7405bc51-3a61-4128-8245-85515bd83f69.png) *주문(Order) Aggregate: 주문, 배송지 정보, 주문자, 주문목록, 총 결제금액의 하위 모델이 있다. 이때 이 하위 개념을 표현한 모델을 하나로 묶어서 '주문'이라는 상위 개념으로 표현할 수 있다. 즉, 주문은 Root Aggregate가 된다. Root Aggregate를 중점으로 종속되어 있는 엔티티들은 동일한 라이프사이클(하나의 트랜잭션)을 가진다.
lksa4e commented 3 years ago