1. 도메인 주도 설계란?
해당 글은 필자의 생각의 흐름대로 작성한 글이며 주저리주저리 작성했기 때문에 많이 지루할 수 있다. 전부 다 안읽어도 되며, 필요한 부분만 찾아서 읽는 것을 추천한다.
참고로, 일부 내용은 참고도서를 인용하였고, 참고도서는 아래와 같이 표현하였다.
"인용 문구"[1]
저작권에 문제가 된다면 꼭 제보를 해주길 바란다.
필자는, 이번 스터디에서 컨텍스트 매핑, 애그리거트, 바운디드 컨텍스트 등 실무에서 사용해본적 없는 생소한 용어를 이해하는데 어려움이 있다. 게다가, 번역서마다 표현이 조금씩 다르다. ...애그리게이트, 애그리게잇, 애그리거트
어떤 용어를 사용하면 좋을까?
- "반 버논"의 책들은 모두 "애그리게잇"으로 번역되었다.
- 일본 저자의 책인 "도메인 주도 설계 철저 입문" 에는 애그리게이트 로 번역을 하였고
- 최범균 님의 "ddd start" 와 최근 출판한 "도메인 주도 설계로 시작하는 마이크로서비스 개발" 이라는 책에서는 "애그리거트"로 표현하였다.
- 에릭 에반스의 "도메인 주도 설계 - 소프트웨어의 복잡성을 다루는 지혜" 에서는, AGGREGATE(집합체) 라고 번역되었다.
필자의, 스터디에서는 국내 서적에서 표현한 "애그리거트" 라는 표현을 사용하겠다. 또한, "유비쿼터스 언어"라는 단어는 일부 서적에서 "보편 언어"로 번역하였는데, 이 글에서는 "유비쿼터스 언어"라고 표현하겠다. 추가로, 이 글에서 잘못된 내용이 있다면 꼭 제보해주기를 바란다. 필자의 허접한 코드에 대한 코드리뷰 역시 환영한다. (주변에 코드리뷰 해줄 사람이 없다..)
[1주차] 1. 도메인 주도 설계
[1주차] 2. 전략적 설계
[1주차] 3. 전술적 설계
[2주차] 4. 엔티티
[2주차] 5. 값객체
[2주차] 6. 애그리거트
[3주차] 7. 도메인 서비스
[3주차] 8. 애플리케이션 서비스
[3주차] 9. 테스트 코드 작성하기
[4주차] 10. 리포지토리
[4주차] 11. 리포지토리 구현
[5주차] 12. 아키텍처
[5주차] 13. 의존성
[5주차] 14. 팩토리
[6주차] 15. 도메인 이벤트
[6주차] 16. 데이터 무결성
7주차, 8주차 미정..
1장에서는 도메인 주도 설계에 대해서 간략하게 알아본다.
도메인은 '영역'이라는 뜻이다. 소프트웨어 개발에서 말하는 도메인은 '프로그램이 쓰이는 대상 분야' 이고, 소프트웨어로 해결하고자 하는 문제 영역이다.
도메인에 대해서 이해하기 어렵다면,
"도메인이 무엇인지"보다는 "도메인에 포함되는 것이 무엇인가?" 에 대해서 생각해보면 좋다.
이커머스 시스템을 예로 들면, 이커머스 분야에서는 주문, 상품, 결제 같은 개념이 필요하다. 이 개념들이 이커머스 시스템의 도메인에 속한다. 의료 시스템에서는 주문, 배송이란 개념은 필요 없다. 병원, 의사, 진료 같은 개념이 필요하다. 시스템 마다 포함하는 개념, 요소가 다를 것이다.
[인용] 도메인 주도 설계 철저 입문 4page ~
소프트웨어의 목적은 도메인에서 이용자들이 직면한 문제를 해결하는 것이다. 이용자들의 문제를 정확히 이해해야 하며, 문제가 무엇인지 알려면 이용자들의 관점이나 생각, 환경을 제대로 이해해야 한다. 즉, 도메인에 속하는 개념과 사건을 이해하고, 문제 해결에 유용한 것을 뽑아낸 지식을 소프트웨어에 반영하는 과정은 소프트웨어 개발에 꼭 필요한 과정이다. 중략...
즉, 도메인 지식에 기반한 개발 방법이란
소프트웨어가 사용될 분야(도메인)의 지식에 초점을 맞추고, 주의 깊은 관찰을 통해 알게 된 지식을 소프트웨어에 제대로 표현하는 개발 방법이다. 사실, 간단한 시스템은 굳이 도메인 주도 설계가 필요 없을지도 모른다. 도메인 주도 설계는, 복잡한 도메인에 관련된 문제를 해결하는 것이며,
도메인 복잡성을 풀어나가기 위한 다양한 도구, 기술 들의 모음이다.
도메인 모델은 도메인 개념을 추상화시킨 지식이다.
도메인 모델링은 도메인 개념을 추상화하는 작업을 의미한다.
이커머스 시스템 에서는 '상품' 개념이 포함는데, 의료 시스템에서도 동일한 이름의 '상품'이라는 개념이 포함된다. 하지만, '상품'이라는 이름은 같지만, 개념을 추상화하면 어떻게 다를까?
"이커머스 시스템" 에서의 '상품' : 고객이 주문하는 물건이며, 셀러가 배송한다.
"의료 시스템" 에서의 '상품' : 건강 검진 패키지 종류(취업용 검진, 부모님 검진 등)이며, 직접 방문해서 검진을 받는 상품이다.
도메인 모델은 개념을 추상화한 지식일 뿐이며, 동작하는 실체가 아니다.
도메인 객체는 도메인 모델을 소프트웨어에서 동작할 수 있도록 개발자가 구현한 것이다.
도메인 주도 설계는 이해하기 쉽지 않은 주제이다. 도메인 주도 설계는 사실 접근 자체가 복잡한 것은 아니다. 복잡한 소프트웨어에 사용하는 수준 높은 기술들을 모은 것이다. 도메인 주도 설계를 하는 개발자는 일반적으로 관련 서적을 몇 번 정도 다시 읽어본다.[1] 이번 스터디를 통해서 도메인 주도 설계를 완벽하게 이해할 수 있다고 생각하지 않는다. 많은 시행착오가 있을 것이며, 실무에서 도메인 주도 설계를 도입하는 것은 큰 도전이 될 것이다.
소프트웨어 개발을 하다보면 어떤 문제들이 발생할까? 필자가 오래전에 근무했던 회사에서 신규 플랫폼을 구축하고 있었다. 필자는 중간에 합류했는데, 초기 설계 단계에는 참여하지 못하였고, 필자가 합류하기 전에 이미 1년동안 설계가 진행된 상태였다. 필자는, 이미 정해진 설계에 맞게 일부 모듈의 피쳐를 개발하는 역할이었다. 당시 프로젝트는 몇가지 문제가 있었다.
- 너무 신기술 중심적인 설계를 반복하였다.
- 특정 인프라 솔루션에 논의가 집중되었다. (예: 쿠버네티스, 도커 등)
- 도메인 전문가와 사전 협의가 제대로 되지 않았다.
- 실제 구체적인 비즈니스 요구사항이 아닌, 개발자의 상상으로 프로젝트가 진행되고 있었다.
사실, 쿠버네티스 같은 기술은 나중에 고민해도 좋을 것 같다. 도메인 지식(문제, 요구사항)을 먼저 확실하게 정리한 후 시작해도 늦지 않았다. 결과적으로, 해당 프로젝트는 잘 안되었지만, 그때로 다시 돌아가서, 도메인 전문가와 협업을 잘했으면 결과가 좋았을까?? 라는 생각을 해보면, 그래도 잘 안됐을 것 같기는 하다...
어쨋든, 어떻게하면 소프트웨어를 효과적으로 잘 설계할 수 있을까? 이 고민에 대한 해결책으로 "도메인 주도 설계"가 조금이나마 힌트를 줄 수 있을 것으로 기대한다. 사실, 위에서도 말했지만, 도메인 주도 설계는 특정한 한가지 기술을 의미하지 않는다. "도메인 주도 설계" 는 여러가지 기술을 모아놓은 방법론이며, 궁긍적으로는 "복잡한 도메인의 문제를 해결하며, 좋은 소프트웨어를 만들기 위함"이라고 생각한다.
도메인 주도 설계는 크게 두가지 카테고리로 나눌 수 있다.
- 전략적 설계
- 전술적 설계
전략적 설계는 개발자가 소프트웨어를 구현하기 전에, 비즈니스상 전략적으로 중요한 것을 구분하고 찾는 과정이다. 이런, 전략적 설계는, 시스템에 적용되는 모델링과 설계를 결정하는 과정으로, 전체 프로젝트에 큰 영향을 준다. 최근에는, 전략적 설계를 마이크로서비스 아키텍처와 연관지어서 설계하는 경우가 많다. 하지만, 이 글에서는 "마이크로서비스 아키텍처"에 대해서는 나중에 생각하기로 하고, 도메인 주도 설계의 기본 가치에 대해서 먼저 고민해보겠다. 최근에 나온 서적을 보니, 도메인 주도 설계 와 마이크로서비스 아키텍처를 적용한 괜찮은 도서가 나왔는데, MSA 개념까지 섞어놔서 어려울 수는 있다. 사실, 도메인 주도 설계를 한두권의 책으로 완벽하게 이해할 수 있는건 불가능에 가깝다고 생각한다.
일단, 도메인 주도 설계만 고민해보겠다.
바운디드 컨텍스트는 의미적으로 동일한 컨텍스트의 범위를 표현한다. [1] 규모가 큰 프로젝트는 다수의 모델이 공존하며, 서로 다른 모델은 서로 다른 컨텍스트에 적용된다. [3] 무슨 말인지 이해가 되지 않으니, 샘플 사례로 이해해보자.
참고로... 샘플 사례는 필자가 경험해보지 못한 질병관리 시스템 도메인인데, 매우 생소한 주제를 일부러 선택하였다. 거의 모든 책에서 다루고 있는 "주문 시스템" 에 대한 사례는 너무 흔하고, 필자는 커머스 도메인을 이미 이해하고 있다. 전혀 모르는 도메인을 선택해서, 처음부터 설계한다는 마음으로 해당 사례를 선택하였다. 이 글을 읽는 개발자는 필자의 샘플을 참고하지 말고, 각자 다른 주제로 선택해서 고민해보길 바란다.
질병관리 시스템을 검토해보자. 질병관리 시스템에 포함되는 모델(개념)을 나열해보자. 바운디드 컨텍스트가 적용되기 전 모습니다.
사실, 필자가 아직 도메인 주도 설계를 스터디 하는 중이라서, 바운디드 컨텍스트에 대해서 명확하게 설명할 자신이 없다.
필자가 설명하기 어려우니.. 에릭 에반스의 "도메인 주도 설계" 를 인용하겠다.
개별적인 모델을 기반으로 작성된 코드가 한데 섞이면 많은 버그가 발생하고 신뢰성이 떨이지며 이해하기 힘든 소프트웨어가 만들어진다. 아울러 팀 구성원 간의 의사소통이 혼란스러워진다. 중략... 모델이 적용되는 컨텍스트를 명시적으로 정의하라. 컨텍스트의 경계를 팀 조직, 애플리케이션의 특정 부분에서의 사용법, 코드 기반이나 데이터베이스 스키마와 같은 물리적인 형태의 관점에서 명시적으로 설정하라. 이 경계 내에서는 모델을 엄격하게 일관된 상태로 유지하고 경계 바깥의 이슈 때문에 초점이 흐려지거나 혼란스러워져서는 안된다.[3]
필자의 개인적인 생각에 의해서, 위 사례를 3개의 바운디드 컨텍스트로 나누었다.
- 선별진료 바운디드 컨텍스트
- 역학조사 바운디드 컨텍스트
- 병상배정 바운디드 컨텍스트
위 과정은, 개발자 혼자 진행하는 과정이 아니다. 도메인 전문가와 함께한다. 이벤트 스토밍 같은 기법을 사용하기도 한다.
사람은 언어를 사용해서 소통한다. 도메인 전문가와 개발자가 서로 다른 언어를 사용한다면, 심각한 커뮤니케이션 미스가 발생할 것이다. 그래서, 전략적 설계 시 보편적으로 사용할 언어를 정의해야 한다.
필자는 샘플 사례를 아래와 같이 정의하였다.
"역학조사 바운디드 컨텍스트"의 보편언어
확진자 : Confirmed Case : 코로나 검사에서 양성이 나온 사람
밀접접촉자 : Close Contact : 양성 확진자에 의해 밀접하게 접촉한 사람
확진자 동선 : Information On Route : ...
보건소 : Community Health Center : ...
역학조사관 : Epidemic Intelligence Officer : 확진자 동선을 파악한 후, 밀접접촉자를 분류하는 사람
"선별 진료 바운디드 컨텍스트"의 보편언어
선별진료소 : Screening Center
바운디드 컨텍스트는 각각의 보편 언어를 갖는다. 같은 단어(언어)인 경우에도, 바운디드 컨텍스트 마다 각자 다른 의미로 사용될 수도 있다.
유비쿼터스 언어는 도메인 전문가와 개발자가 같이 협의 후 결정하지만, 도메인 전문가의 언어를 그대로 가져올 필요는 없다. 만약, EpidemicIntelligenceOfficer 라는 단어가 가독성이 떨어진다고 판단되어서, investigator 로 바꿔서 사용하고 싶다면? 도메인 전문가와 협의해서 결정하면 된다. 개발자 임의로 변경해서 사용하지 않아야 한다. 유비쿼터스 언어는 대화할때도 사용하고, 회의 할때도 사용하고, 문서에서도 사용하고, 심지어는 소프트웨어 코드에서도 사용하게 된다.
참고로 필자의 보편언어는 외교부 용어집을 참고하였다.
개별적인 "바운디드 컨텍스트"만 봐서는 전체 시스템을 이해할 수 없다. 나무가 아닌 숲을 봐야한다. 이때 사용하는 것이 바로 컨텍스트 맵, 컨텍스트 매핑이다. 다수의 바운디드 컨텍스트의 경계를 정의하는 것이 컨텍스트 매핑이며, 컨텍스트 맵은 컨텍스트 매핑을 큰 지도처럼 보여주는 다이어그램이다. 마틴 파울러의 글을 참고하자.
https://martinfowler.com/bliki/BoundedContext.html
필자는 샘플 사례에서 3개의 바운디드 컨텍스트를 정의하였다.
컨텍스트 매핑 시 두 컨텍스트의 관계는 아래와 같이 맺을 수 있다.
- 파트너쉽
- 공유 커널
- 고객-공급자
- 준수자
- 반부패 계층
- 공개 호스트 서비스
- 공표된 언어
- 각자의 길
기술적으로는 아래와 같은 시스템 통합 기술을 사용할 수 있다.
- HTTP RestAPI
- 메시징
- RPC
자세한 내용은 이 글에서는 생략한다. 전략적 설계를 글 하나로 전달하기에는 너무 방대하고 어렵다...
필자는, 도메인전문가(기획팀)에서 이미 확정해서 전달받은 요구사항을 충실히 구현하는 것에 익숙하다. 프로젝트 초반에 도메인 전문가와 같이 전략을 세워본 경험이 많지 않다. 그래서, 아마도 필자가 위 과정을 회사에서 도전한다면 수많은 시행착오가 있을 것이다. 그래도 기회가 된다면 한번쯤은 도전해보고 싶지만, 현재 회사에서는 불가능할 것 같다는 생각이다. 언젠가는 기회가 오겠지..
전술적 설계는 전략적 설계에서 정의한 도메인 모델의 세부 사항을 구현하는 과정이다. 전략적 설계를 통해서 도출한 바운디드 컨텍스트, 도메인 모델 을 기반으로 도메인 객체를 구현하고, 시스템 소프트웨어 내부 구조를 상세히 정의하며, 바운디드 컨텍스트 사이에 통신하는 서비스 인터페이스를 정의하는 등 각종 설계 패턴과 개발 관점에서의 방법들을 의미한다.
역학조사 바운디드 컨텍스트만 살펴보자. 역학조사 바운디드 컨텍스트에는 아래와 같은 도메인 개념을 포함한다.
- 역학조사관
- 보건소
- 확진자
- 확진자 동선
- 밀접접촉자
해당 모델은, 각자의 객체로 구체화 될 것이다. (엔티티 또는 값객체 로 될 것이다.) 객체는 객체를 참조하며, 다수의 객체들은 질서와 경계가 필요하다. 애그리거트는 서로 연관된 객체를 감싸는 경계를 정의한다. 필자가 설명하기 어려우니 역시 인용을 남기겠다.
에릭 에반스의 "도메인 주도 설계" 를 인용하겠다.
엔티티와 값객체를 애그리거트로 모으고 각각에 대한 경계를 정의하라. 한 엔티티를 골라 애그리거트 루트로 만들고, 애그리거트 경계 내부의 객체애 대해서는 루트를 거쳐 접근할 수 있게 하라. 애그리거트 밖의 객체는 루트만 참조할 수 있게 하라. 중략...
대충 그림으로 그려보겠다. 역학조사 바운디드 컨텍스트에서 두개의 애그리거트가 정의되었다. 각각 애그리거트 루트는 확진자, 역학조사관 으로 정의하였다.
소프트웨어 코드로 작성하면 아래와 같이 될 것이다. (나중에 변경 예정..)
일단, 필자가 아직 도메인 주도 설계에 익숙하지 않아서, 위와 같이 하는게 맞는지 확신은 없다. DDD 전문가는 제보를 해주길 바란다.
블로그 발행 후 추가 내용... 2021.05.04
다시 생각해보니 확진자의 동선은 값객체가 아닌 엔티티로 정의하는게 좋을 것 같다.
또한, 별도의 애그리거트로 분류를 하는게 좋겠다.
왜 그렇게 생각했는지는 상세한 내용은 2주차에서.... 암튼 처음 설계에서 많이 변경이 되었다....
애그리거트에 대한 상세한 스터디는 2주차에서 진행할 예정이다.
엔티티는 도메인 모델을 구현한 도메인 객체이다. 아래 필자의 샘플 소스는 확진자 엔티티이다. (값객체도 도메인 모델을 구현한 객체이지만, 몇가지 차이점이 있다. 예를 들어서, 엔티티는 식별이 가능하다.) 엔티티는 여러 속성을 갖는데, "확진자" 엔티티는 이름, 주소, 휴대폰넘버 등의 속성을 갖는다. 해당 속성은 변할 수 있다. (값 객체는 불변이다.)
더 자세한 내용은 2주차 스터디 때 아주 상세하게 얘기하겠다.
엔티티와는 다르게, 값객체는 불변이다. 필자가 정의했던 "확진자 동선" 이라는 값객체는 아래와 같이 정의할 수 있다. 불변이기 때문에, 생성자에서 초기화한 이후로 속성이 변하지 않는다.
그렇다면, 불변인 값객체를 변경하고 싶다면 어떻게 해야할까? 변경할 방법이 없다. 새로운 객체를 반들어서 바꿔치기를 하는 수 밖에 없다. 자세한 내용은 2주차 때 다시 고민해보기로 하자
도메인 서비스는 엔티티의 행동으로 구현하기에 어색함을 해결하기 위해서, 도메인의 행동을 별도의 객체로 분리해서 정의하는 방법이다. 참고로, 필자가 도메인 서비스에 대해서 아직 제대로 이해를 하지 못했다. 자세한 내용은 3주차 때 스터디하겠다.
애플리케이션은 소프트웨어에서 구현해야 하는 요구사항, 즉 유스케이스를 구현하는 서비스 객체이다.
예를 들면, 역학 조사에 대한 유스케이스를 제공하는 서비스 객체는 아래와 같다.
테스트 코드 작성에 대해서도 심각하게 고민을 해보겠다.
자세한 내용은 3주차 때 스터디하겠다.
리포지토리는 데이터 퍼시스턴스를 담당한다. 일반적으로 애그리거트에 한개의 리포지토리를 사용한다. "확진자", "역학조사관" 이라는 각각의 애그리거트에 각각 하나씩 리포지토리를 생성하였다.
도메인 엔티티는 소프트웨어가 중지 되었다고해서 사라지면 안된다. 어딘가에는 저장하고, 필요할 때 가져다가 사용해야 한다. 추상화된 리포지토리는 데이터스토어를 쉽게 교체할수 있다는 장점이 있다. 스터디에서는 추상화된 리포지토리에 대한 구현체로 RDBMS & JPA(하이버네이트) 기술을 사용할 예정이다. 비록, JPA 라는 기술이 DDD 에 완벽하게 적합하다고 생각하지는 않지만, JPA 를 사용해서 어떻게 DDD 를 잘 접목시킬수 있을지, 왜 완벽하다고 생각하지 않는지, 등 자세한 내용은 4주차 때 스터디하겠다.
도메인, 서비스, 리포지토리 는 의존성을 갖는다. 아래와 같은 아키텍처가 적합하다.
자세한 내용은 5주차 때 스터디하겠다.
자세한 내용은 6주차 때 스터디하겠다.
첫번쨰
5주차 까지는 대략 커리큘럼을 정하였지만, 전술적 설계에 해당한다. 전략적 설계에 대해서 어떻게 스터디를 해야할지 모르겠다... 어렵다.
두번째
JPA 기술에 대해서 같이 공부하는게 과연 맞는 일인가? 데이터베이스에 의존성이 없이 도메인 주도 설계를 공부하고 싶지만, 현실은 그렇게 쉽지는 않다. JPA 에 의해서 정의되는 "@Entity" 어노테이션은 과연 클린 아키텍처에 해당하는가? 딱히 대안이 떠오르지 않는다...
일단 5주차 까지는 전술적 설계에 해당하는 아래 내용을 스터디 하겠다.
2주차 - 엔티티, 값객체, 애그리거트
3주차 - 도메인 서비스, 애플리케이션 서비스
4주차 - 리포지토리 구현
5주차 - 아키텍처
6주차 이후에는, 전략적 설계(바운디드 컨텍스트, 컨텍스트 매핑 등)를 스터디할지, 전술적 설계 중 트랜잭션, 도메인 이벤트 등을 스터디 할지 고민이 필요할 것 같다. 전략적 설계에 대해서 스터디를 해보고 싶지만, 자신이 없다. 그래도, 전략적 설계를 끼어넣을 수 있다면... 스터디 중간에 억지로라도 넣어보겠다...
필자의 글에 댓글은 거의 없어서, 아마도 의견은 없을 것 같지만..
만약, "도메인 주도 설계 전문가"가 이 글을 우연히 보게 되었다면.. 꼭 의견을 해주길 바란다.
과제
- 전략적 설계, 전술적 설계 에 대해서 각자 찾아서 공부하고 정리하기
- 도메인 객체(엔티티, 값객체) 코드로 작성해보기 (필자의 사례 말고, 다른 사례로)
도메인이 생각이 안난다면, 주문 도메인으로 해보길 바란다.
최범균 님의 'ddd start' 를 추천한다.
[1] 도메인 주도 설계 핵심 (반 버논 지음, 에이콘 출판사)
[2] 도메인 주도 설계 철저 입문 (나루세 마사노부 지음, 위키북스)
[3] 도메인 주도 설계 - 소프트웨어의 복잡성을 다루는 지혜 (에릭 에반스 지음, 위키북스)