https://www.youtube.com/watch?v=sLG5n_pXWK0&t=39s
DDD 그렇게 하는 거 아닌데 — 완벽한 설계보다 중요한 것
최근 우아콘의 「DDD 그렇게 하는 거 아닌데」 영상을 보면서 DDD에 대해 다시 생각하게 되었다.
DDD라는 단어를 처음 들으면 흔히 이런 생각을 하게 된다.
'처음부터 바운디드 컨텍스트를 잘 나눠야 하나?'
'도메인 모델을 완벽하게 설계해야 하나?'
나 역시 DDD를 처음 접했을 때는 객체지향 설계, 풍부한 도메인 모델, 엔티티 책임 분리 같은 코드 구조에 더 관심이 갔다.
하지만 이번 영상을 보면서 느낀 것은, DDD는 단순히 코드를 예쁘게 짜는 방법이 아니라는 점이다.
DDD는 결국 비즈니스 문제를 더 잘 이해하고, 그 복잡성을 소프트웨어 안에서 다루기 위한 방법에 가깝다.
완벽한 설계는 존재하지 않는다
연사는 완벽한 설계를 인정하지 않는 것부터 완벽한 설계의 시작이라고 한다.
어떻게 처음부터 완벽하고 풍부한 도메인 모델을 만들어낼 수 있을까?
일단 구현을 통해 충분한 도메인 지식을 얻으려 노력하며,
getter/setter도 사용하면서 점진적으로 리팩토링해서 더 나은 모델로 개선을 하는 것이다.
점차 도메인 자체가 빈약한 모델에서 풍부한 모델로 나아가야 한다.
DDD와 OOP가 얼추 비슷하고, OOP는 실제 객체 즉 도메인 기반이긴 하지만
두 개념은 분명 다르다. DDD는 조금 더 넓은 범위에서 문제를 이해하고 풀어나가는 과정이라고 생각한다.
유비쿼터스 언어는 이름 짓기 문제가 아니다
실제 프로젝트를 진행하며 각자 다른 파트 간 서로 비슷한 의미로 사용하고 있다고 생각하는 단어를
서로 다르게 말하고 있을 수도 있다. 이때 필요한 것이 유비쿼터스 언어다.
유비쿼터스 언어는 단순히 개발자가 변수 네이밍을 편하게 하기 위한 도구가 아니고,
개발에 참여하는 모든 직군이 같은 언어로 대화가 이뤄지게 하는 도구이다.
그래서 용어사전을 만드는 것이 중요하다.
바운디드 컨텍스트 안에서 꼭 유지해야하는 것은 아니며 명사/동사/ 모두 기록하고, 한/영 함께 기록한다.
용어사전을 살아있는 문서로 만들기 위해 프로젝트 README.md 같은 곳에서 관리하고,
용어 또한 코드 리뷰의 대상이라고 한다! 더 의미 있는 용어를 지속적으로 찾기 위해 노력해야한다.
인수 테스트는 고객의 언어로 시스템을 설명한다
DDD에서 인상 깊었던 부분 중 하나는 인수 테스트다.
인수 테스트는 개발자만을 위한 테스트가 아니다.
고객, 개발자, 테스터가 함께 소프트웨어가 어떻게 동작해야 하는지 정의하는 테스트다.
중요한 점은 테스트가 내부 구현이 아니라 고객의 관점에서 작성된다는 것이다.
예를 들어 자동 채점 기능이 있다고 해보자.
Feature: 자동 채점
Scenario: 과제 제출물을 제출할 수 있는 특정 과제에 대한 과제 제출물이 있는 경우
Given 특정 과제 제출물이 있는 경우
When 해당 과제 제출물의 예제 테스트를 실행하면
Then 마지막 커밋에 대한 자동 채점 기록을 확인할 수 있고 자동 채점이 저장된다
이런 식으로 작성하면 개발자는 단순히 “채점 API를 만든다”가 아니라, 사용자가 어떤 상황에서 어떤 결과를 기대하는지 이해할 수 있다.
반대로 예외 상황도 고객의 관점에서 정의할 수 있다.
Scenario: 특정 과제 제출물에 대한 마지막 커밋이 없어 예외가 발생하는 경우
Given 특정 과제 제출물이 있는 경우
When 해당 과제 제출물의 예제 테스트를 실행하면
Then 예외가 발생하고 자동 채점이 저장되지 않는다
이렇게 작성하면 테스트는 단순 검증 도구가 아니라, 도메인 지식을 공유하는 문서가 된다.
좋은 인수 테스트는 개발자에게 '무엇을 구현해야 하는지'뿐만 아니라 '왜 그렇게 동작해야 하는지'까지 알려준다.
도메인 지식은 개발자의 책임이기도 하다
영상에서 가장 기억에 남았던 말 중 하나는 다음과 같다.
내 월급은 비즈니스에서 나온다.
개발자는 코드를 작성하는 사람이지만, 결국 그 코드가 가치를 가지는 이유는 비즈니스 문제를 해결하기 때문이다.
소프트웨어로 해결하고자 하는 영역을 비즈니스 도메인이라고 한다.
쇼핑몰을 만든다면 도메인은 전자상거래이고, 그 안에는 상품, 전시, 주문, 결제, 정산 같은 하위 도메인이 존재한다.
이때 개발자가 전자상거래에 대한 이해 없이 단순히 API만 구현한다면, 사용자가 무엇을 원하고 어떤 문제가 중요한지 알기 어렵다.
도메인 지식을 쌓기 위해서는 이런 질문을 계속 던져야 한다.
우리는 어떤 종류의 비즈니스를 하고 있는가?
사용자는 어떤 문제를 해결하고 싶어 하는가?
선두 업체와 경쟁 업체는 어떤 기능을 제공하는가?
우리 서비스가 다른 서비스와 차별화되는 지점은 무엇인가?
한정된 자원을 어디에 집중해야 하는가?
이 질문들은 단순히 기획자나 PO만의 질문이 아니다.
개발자 역시 이 질문을 이해해야 부족하거나 지나치지 않은 솔루션을 선택할 수 있다.
기술적으로 멋진 구조를 만드는 것도 중요하지만, 그 구조가 비즈니스 요구에 비해 과하거나 부족하다면 좋은 설계라고 보기 어렵다.
하위 도메인과 집중해야 할 영역
하나의 비즈니스 도메인 안에는 여러 하위 도메인이 존재한다.
전자상거래를 예로 들면 상품, 전시, 주문, 결제, 정산, 배송 등이 각각 하위 도메인이 될 수 있다.
하지만 모든 하위 도메인이 똑같이 중요한 것은 아니다.
어떤 영역은 우리 서비스의 핵심 경쟁력이고, 어떤 영역은 외부 시스템을 활용해도 충분할 수 있다.
예를 들어 결제가 서비스의 차별점이 아니라면 외부 PG사를 사용하는 것이 더 나을 수 있다.
반대로 추천, 검색, 정산 구조가 서비스의 핵심 경쟁력이라면 그 부분에는 더 많은 자원을 투자해야 한다.
핵심 하위 도메인은 고정된 것이 아니다.
비즈니스 상황, 시장 환경, 사용자의 요구에 따라 계속 바뀔 수 있다.
그래서 개발자는 “어떻게 구현할까?”만 고민하는 것이 아니라, “어디에 집중해야 할까?”도 함께 고민해야 한다.
바운디드 컨텍스트는 문제를 나누는 방식이다
DDD에서 자주 등장하는 개념이 바운디드 컨텍스트다.
바운디드 컨텍스트는 단순히 패키지를 나누거나 서비스를 분리하는 개념이 아니다.
같은 용어가 같은 의미로 사용되는 경계를 정하는 것이다.
같은 “주문”이라는 단어도 결제 컨텍스트에서는 결제 대상일 수 있고, 배송 컨텍스트에서는 배송 준비의 기준이 될 수 있다.
정산 컨텍스트에서는 매출 계산의 근거가 될 수도 있다. 같은 이름을 사용한다고 해서 항상 같은 의미는 아니다.
따라서 경계를 나누는 기준은 기술 구조만이 아니라 도메인 언어와 비즈니스 의미가 되어야 한다.
팀이 이미 분리되어 있다면 시스템 구조도 그 조직 구조의 영향을 받는다. 이것이 콘웨이의 법칙이다.
반대로 원하는 시스템 구조가 있다면 조직 구조를 그에 맞게 설계할 수도 있다. 이것이 역콘웨이의 법칙이다.
결국 바운디드 컨텍스트는 “마이크로서비스로 나눌 것인가?”의 문제가 아니라, 문제를 어떤 단위로 이해하고 해결할 것인가의 문제다.
조직 간 문제 해결은 기술만으로 되지 않는다
컨텍스트를 나누면 자연스럽게 조직 간 협업 문제가 생긴다.
API를 어떻게 설계할지 함께 논의할 수도 있고, 한 조직이 일방적으로 결정할 수도 있다.
때로는 강력한 중앙 통제가 발생하기도 한다. 이 과정은 기술적인 문제이면서 동시에 정치적인 문제다.
개발자는 흔히 기술적으로 올바른 구조를 만들면 문제가 해결될 것이라고 생각한다.
하지만 실제 프로젝트에서는 각 팀의 책임, 일정, 우선순위, 이해관계가 모두 다르다.
그래서 좋은 설계는 코드 안에서만 만들어지지 않는다. 회의, 문서, API 논의, 용어 정의, 책임 범위 조율 속에서 함께 만들어진다.
DDD가 개발자만의 방법론이 아닌 이유도 여기에 있다.
은총알은 없다
브라이언 커니핸은 복잡성을 제어하는 것이 컴퓨터 프로그래밍의 본질이라고 했다.
프레드 브룩스 역시 소프트웨어 개발에는 은총알이 없다고 말했다.
DDD도 마찬가지다. DDD를 적용한다고 해서 모든 문제가 해결되지는 않는다.
바운디드 컨텍스트를 나눈다고 해서 복잡성이 사라지는 것도 아니고,
풍부한 도메인 모델을 만든다고 해서 비즈니스 이해가 자동으로 생기는 것도 아니다.
중요한 것은 본질적 복잡성과 우발적 복잡성을 구별하는 것이다.
비즈니스 자체가 가진 복잡성은 피할 수 없다.
하지만 용어가 정리되지 않아 생기는 혼란, 외부 시스템 모델이 내부 도메인을 오염시키는 문제,
모든 책임이 하나의 객체나 하나의 서비스에 몰리는 문제는 줄일 수 있다.
DDD는 이런 우발적 복잡성을 줄이고, 본질적 복잡성을 더 잘 다루기 위한 도구라고 볼 수 있다.
마무리
좋은 DDD는 “DDD에서는 이렇게 해야 한다”로 시작하지 않는다.
좋은 DDD는 프로젝트가 실제로 겪고 있는 문제에서 시작한다.
실제 프로젝트 진행하면서 도메인 주도 개발, DDD 등 개념을 맛이라도 보기 위해
엔티티에서 자기 자신의 책임을 지는 로직을 많이 구현해보았다.
막상 40분짜리 영상에도 내가 모르는 부분들이 너무나 많았다.
또한 주로 개발자끼리, 적어도 어느정도 베이스 지식이 있는 이들과 소통했기에
용어사전이라는 개념이 항상 필요가 없었는데 상상해보니 정말 소통에 필수적이라는 생각이 들었다.
DDD는 개발 방법론을 넘어 모두가 같은 문제를 바라보는 과정의 조율 도구라는 것이다.
프로젝트 과정에서 변화에 맞서 싸우기보다 변화에 대응할 수 있는 구조를 만들고 있는가?
DDD는 정답지가 아니라, 이런 질문을 계속 던지게 해주는 도구라고 느껴지게 된 계기이며,
앞으로 DDD 관련한 공부도 꾸준히 필요할 것 같다.