도메인 주도 개발 시작하기(1)
1-1. 도메인이란?
도메인 (domain): 소트프트웨어로 해결하고자 하는 문제 영역
•
1개의 도메인은 하위 도메인으로 더 나눌 수 있다
e.g.)
◦
한 하위 도메인은 다른 하위 도메인과 연동하여 완전한 기능을 제공한다
•
특정 도메인을 위한 소프트웨어라고 해서 도메인이 제공해야 할 모든 기능을 직접 구현하는것은 아니다
◦
일부 기능을 자체 시스템으로 구현하고, 나머지 기능은 외부 업체의 시스템을 이용할 수 있다
•
도메인마다 고정된 하위 도메인이 존재하는 것은 아니다
1-2. 도메인 전문가와 개발자 간 지식 공유
개발에 앞서 요구사항을 올바르게 이해하는것이 중요하다
•
요구사항을 올바르게 이해하는 방법에는 여러가지 방법이 있지만 비교적 간단한 방법은 개발자와 전문가가 직접 대화 하는것이다
◦
개발자와 전문가 사이에 내용을 전파하는 전달자가 많으면 많을수록 정보가 왜곡되고 손실이 발생하게 되며, 최초 요구사항과 다른걸 만들게 된다
•
전문가 만큼은 아니지만 이해관계자와 개발자도 도메인 지식을 갖춰야 한다
Garbage in, Garbage Out!
→ 잘못된 값이 들어가면 잘못된 결과가 나온다
→ 전문가나 관련자가 요구한 내용이 항상 올바른 것은 아니며 때론 본인들이 실제로 원하는 것을 정확하게 표현하지 못할 때도 있다
→ 개발자는 요구사항을 이해할 때 왜 이런 기능을 요구하느지 또는 실제로 원하는 게 무엇인지 생각하고 전문가와 대화를 통해 진짜로 원하는것을 찾아야 한다
1-3. 도메인 모델
도메인 모델: 특정 도메인을 개념적으로 표현하는 것
•
도메인을 이해하려면 도메인이 제공하는 기능과 도메인의 주요 데이터 구성을 파악해야 한다
◦
기능과 데이터를 함께 보여주는 객체 모델은 도메인을 모델링하기 적합하다
•
도메인 모델을 표현할 때 클래스 다이어그램이나 상태 다이어그램과 같은 UML 표기법만 사용해야 하는 것은 아니다
◦
관계가 중요한 도메인이라면 그래프를 이용해서 도메인을 모델링할 수 있다
◦
도메인을 이해하는데 도움이 된다면 표현 방식이 무엇인지는 중요하지 않다
•
도메인 모델은 기본적으로 도메인 자체를 이해하기 위한 개념 모델이다
•
개념 모델을 이용해서 바로 코드를 작성할 수 있는 것은 아니기에 구현 기술에 맞는 구현 모델이 따로 필요하다
◦
개념 모델과 구현 모델은 서로 다른것이지만 구현 모델이 개념 모델을 최대한 따르도록 할 수는 있다
•
하위 도메인과 모델
◦
도메인은 다수의 하위 도메인으로 구성된다
◦
각 하위 도메인이 다루는 영역은 서로 다르기 때문에 같은 용어라도 하위 도메인마다 의미가 다를 수 있다
◦
도메인에 따라 용어 의미가 결정되므로 여러 하위 도메인을 하나의 다이어그램에 모델링하면 안된다
◦
각 하위 도메인마다 별도로 모델을 만들어야 한다
1-4. 도메인 모델 패턴
도메인 모델은 아키텍처 상의 도메인 계층을 객체 지향 기법으로 구현하는 패턴을 말한다
•
일반적인 애플리케이션 아키텍처는 4개의 영역으로 구성된다
◦
표현 ( User interface or Presentation )
▪
사용자의 요청을 처리하고 정보를 보여준다
•
사용자는 소프트웨어를 사용하는 사람뿐만 아니라 외부 시스템일 수도 있다
◦
응용 (Application)
▪
사용자가 요청한 기능을 실행한다
▪
비즈니스 로직을 직접 구현하지 않으며 도메인 계층을 조합해서 기능을 실행한다
◦
도메인
▪
시스템이 제공할 도메인 규칙을 구현한다
◦
인프라스트럭처
▪
데이터베이스나 메시징 시스템과 같은 외부 시스템과의 연동을 처리한다
•
도메인 계층은 도메인의 핵심 규칙을 구현한다
e.g.)
예시 코드
•
도메인 모델 이란 용어는 도메인 자체를 표현하는 개념적인 모델을 의미하지만, 도메인 계층을 구현할 때 사용하는 객체 모델을 언급할 때에도 도메인 모델 이란 용어를 사용한다
•
개념 모델과 구현 모델
◦
개념 모델은 순수하게 문제를 분석한 결과물이다
▪
데이터베이스, 트랜잭션 처리, 성능, 구현 기술과 같은것을 고려하고 있지 않다
▪
개념 모델을 구현 가능한 형태의 모델로 전환하는 과정을 거치게 된다
◦
개념 모델을 만들 때 처음부터 완벽하게 도메인을 표현하는 모델을 만드는 시도를 할 수 있지만, 실제로 이것은 불가능하다
▪
소프트웨어를 개발하는 동안 개발자와 관계자들은 해당 도메인을 더 잘 이해하게 된다
▪
프로젝트 초기에 이해한 도메인 지식이 시간이 지나 새로운 통찰을 얻으면서 완전히 다른 의미로 해석되는 경우도 있다
▪
프로젝트 초기에 완벽한 도메인 모델을 만들더라도 결국 도메인에 대한 새로운 지식이 쌓이면서 모델을 보완하거나 변경하는 일이 발생한다
◦
처음부터 완벽한 개념 모델을 만들기보다는 전반적인 개요를 알 수 있는 수준으로 개념 모델을 작성해야 한다
1-5. 도메인 모델 도출
•
도메인을 모델링할 때 기본이 되는 작업은 모델을 구성하는 핵심 구성요소, 규칙, 기능을 찾는것이다
•
요구사항에서 도메인 모델을 점진적으로 만들어나갔다
◦
일부는 구현수준까지 만들고, 일부는 이름 정도만 결정했다
◦
요구사항에서 출발하여 만든 모델은 요구사항 정련을 위해 도메인 전문가나 다른 개발자와 논의하는 과정에서 공유하기도 한다
◦
모델을 공유할 때는 화이트보드나 위키와 같은 도구를 사용해서 누구나 쉽게 접근할 수 있도록 하면 좋다
•
문서화
◦
문서화를 하는 주된 이유는 지식을 공유하기 위함이다
◦
실제 구현은 코드에 있으므로 코드를 보면 다 알 수 있지만, 코드는 상세한 모든 내용을 다루고 있기 때문에 코드를 이용해서 전체 소프트웨어를 분석하려면 많은 시간을 투자해야 한다
◦
전만적으로 상위 수준에서 정리한 문서를 참조하는 것이 소프트웨어 전반을 빠르게 이애하는데 도움이 된다
▪
전체 구조를 이해하고 더 깊게 이해할 필요가 있는 부분을 코드로 분석해나가면 된다
◦
도메인 지식이 잘 묻어나도록 코드를 작성하지 않으면 코드의 동작 과정은 해석할 수 있어도 도메인 관점에서 왜 코드를 그렇게 작성했는지 이해하는 데는 도움이 되지 않는다
1-6. 엔티티와 밸류
•
도출한 모델은 엔티티(Entity) 와 밸류(Value) 로 구분할 수 있다
•
엔티티와 밸류를 제대로 구분해야 도메인을 올바르게 설계하고 구현할 수 있다
◦
이 둘의 차이를 명확하게 이해하는 것은 도메인을 구현하는 데 있어 중요하다
1-6-1. 엔티티
•
엔티티의 가장 큰 특징은 식별자를 가진다
•
식별자는 엔티티 객체마다 고유해서 각 엔티티는 서로 다른 식별자를 갖는다
◦
e.g.) 주문 도메인에서는 각 주문은 주문번호를 가지고 있는데, 이 주문번호는 각 주문마다 서로 다르다
•
엔티티의 식별자는 변경되지 않는다
◦
엔티티의 식별자는 변경되지 않고 고유하기 때문에 두 엔티티 객체의 식별자가 같으면 두 엔티티는 같다고 판단할 수 있다
1-6-2. 엔티티의 식별자 생성
•
엔티티의 식별자는 다음 중 한가지 방식으로 생성한다
1.
특정 규칙에 따라 생성
2.
UUID 나 Nano ID 와 같은 고유 식별자 생성기 사용
3.
값을 직접 입력
4.
일련번호 사용 (Sequence 나 DB 의 Auto Increment Column 사용 )
•
리포지터리(Repository) 는 도메인 객체를 데이터베이스에 저장할 때 사용하는 구성요소이다
◦
Auto Increment Column 을 사용할 경우 Repository 는 DB 가 생성한 식별자를 구해서 엔티티 객체에 반영한다
1-6-3. 밸류 타입
•
밸류 타입은 개념적으로 완전한 하나를 표현할 때 사용한다
예시 코드
•
밸류 타입이 꼭 두 개 이상의 데이터를 가져와야 하는 것은 아니다
◦
의미를 명확하게 표현하기 위해 밸류 타입을 사용하는 경우도 있다
•
밸류 객체의 데이터를 변경할 때는 기존 데이터를 변경하기도바는 변경한 데이터를 갖는 새로운 밸류 객체를 생성하는 방식을 선호한다
•
밸류 타입을 불변으로 구현하는 이유
◦
안전한 코드를 작성할 수 있다
1-6-4. 엔티티 식별자와 밸류 타입
•
엔티티 식별자를 위한 밸류 타입을 사용해서 의미가 잘 드러나도록 할 수 있다
예시 코드
1-6-5. 도메인 모델에 set 메서드 넣지 않기
•
도메인 모델에 get/set 메소드를 무조건 추가하는 것은 좋지 않은 버릇이다
◦
set 메소드는 도메인의 핵심 개념이나 의도를 코드에서 사라지게 한다
•
불변 밸류 타입을 사용하면 자연스럽게 밸류 타입에는 set 메소드를 구현하지 않게 된다
•
set 메소드를 구현해야 할 특별할 이유가 없다면 불변 타입의 장점을 살릴 수 있도록 밸류 타입은 불변으로 구현해야 한다
•
DTO 의 get/set 메소드
◦
DTO (Data Transfer Object) 는 프레젠테이션 계층과 도메인 계층이 데이터를 서로 주고받을 때 사용하는 일종의 구조체이다
◦
DTO 가 도메인 로직을 담고 있지 않기 때문에 get/set 메소드를 제공해도 도메인 객체의 데이터 일관성에 영향을 줄 가능성이 높지 않다
1-7. 도메인 용어와 유비쿼터스 언어
에릭 에반스는 도메인 주도 설계에서 언어의 중요함을 강조하기 위해 유비쿼터스 언어(Ubiquitous Language) 라는 용어를 사용했다
•
코드를 작성할 때 도메인에서 사용하는 용어는 매우 중요하다
예시 코드
•
최대한 도메인 용어를 사용해서 도메인 규칙을 코드로 작성하게 되므로 (의미를 변환하는 과정에서 발생하는) 버그도 줄어든다
•
이해 관계자, 개발자가 도메인과 관련된 공통의 언어를 만들고 사용하면 소통 과정에서 발생하는 용어의 모호함을 줄일 수 있다