
도메인 주도 개발 시작하기 (최범균 저) 책의 1장: 도메인 모델 시작하기, 2장: 아키텍처 개요 챕터를 읽었습니다.
1장에서는 도메인과 관련된 용어들을 설명합니다. 인상 깊었던 내용은 도메인, 엔티티, 밸류 타입에 대한 정의 부분이었습니다.
2장에서는 레이어드 아키텍처(계층 구조)를 기반으로, 전반적인 모듈 구조와 DIP 개념, 안티 패턴들과 애그리거트(aggregate)에 대해 설명합니다.
책에서 도메인이란 소프트웨어가 해결하고자 하는 문제 영역으로 정의합니다. 저는 단순히 User, Product와 같은 엔티티와 유사한 영역으로 생각했지만 책을 읽고 나니 확연한 차이점이 있다는 것을 알게 되었습니다.
온라인 서점이라는 도메인이 있을 때 해당 도메인은 결제, 상품과 같은 하위 도메인으로 나눌 수 있습니다.
이렇게 나누는 과정에서 생기는 엔티티인 Payment, Product와 같은 객체 모델 혹은 결제 상태를 반영하는 흐름은 도메인 모델이라고 정의합니다.
밸류 타입은 개념적으로 완전한 하나를 표현할 때 사용하는 것으로 정의합니다. 온라인에서 쇼핑을 할 때 우리는 배송지를 입력합니다. 배송지를 구성하는 요소는 우편 번호, 도로명 주소, 상세 주소로 이뤄지며 해당 요소 하나 하나로는 의미가 없으며 같이 다뤄질 때 의미가 있습니다.
이러한 값들을 밸류 타입으로 정의하며 밸류 타입을 수정할 때는 요소 하나 하나(우편 번호, 도로명 주소 등)를 수정하는 것이 아니라 새로운 밸류 타입을 표현하는 값을 생성하고 필드를 교체하는 방법을 주로 사용합니다.
애그리거트는 도메인 모델을 이해하는데 도움이 되는, 관련 객체를 하나로 묶은 그룹입니다. 주문이라는 애그리거트는 주문 정보, 배송지 정보, 주문자, 주문 목록, 총 결제 금액이라는 하위 모델을 가지고 있으며 이 중 주문 정보는 주문 애그리거트의 핵심이 되는 루트 엔티티입니다. 주문 애그리거트 내에서 다른 엔티티 혹은 밸류 타입에 접근할 때는 루트 엔티티를 거쳐서 접근할 수 있도록 설계해야 애그리거트의 일관성을 유지하고, 외부로부터 불필요한 내부 구현 노출을 방지할 수 있습니다.
제가 개발을 하면서 크게 고민하는 가치, 그리고 이 책에서 느낀, 일관성 있게 강조되는 개념 중 하나는 가시성입니다.
// 주문 내 한 항목을 표현한 클래스
public class OrderLine {
...
private Money price;
public OrderLine(...) {
...
}
}
보통 price (가격) 필드를 선언해야 한다면 int 타입(내지는 Integer)으로 선언합니다. 하지만 해당 타입은 가격을 표현하기엔 다소 맞지 않습니다. 정수형 타입은 음수도 표현할 수 있기 때문입니다. 이를 Money 클래스로 풀어서 사용한다면 가격에 대해 이를 Money 클래스로 풀어서 사용한다면 가격에 대해 의미 있는 제약(예: 음수 불가, 통화 단위 일관성 등)을 부여하고, 도메인 로직을 명확하게 캡슐화할 수 있습니다.
위의 예제 코드를 보면서 프로그래밍 언어는 사람이 읽기 쉬운 언어로 코드를 작성하기 위해 등장했다는 걸 다시 생각하면서 의미 있는 코드를 작성하려고 노력해야겠다는 생각을 했습니다.
상태(state)를 수정하는 메소드를 작성할 때 단순히 setState(OrderState state) 와 같이 수정할 값을 받아서 수정하도록 메소드를 구현한 경험이 있습니다. 처음엔 단순하지만 여러 요구사항이 생기면(배송 상태에 따라 수정에 제약이 생긴다거나) 메소드 내부는 상당히 복잡한 상태가 됩니다.
이렇게 단순한 setter 메소드를 설계하는 대신 completeOrder(), cancelOrder()와 같이 의미있는 메소드로 분리하게 되면 메소드를 호출하는 측에서 흐름을 다룰 수 있으며 클래스 간 책임이 명확해집니다. 또, Order 모델은 캡슐화가 되어 안정적이고 유지보수에 용이한 소프트웨어를 개발할 수 있다는 생각이 들었습니다.
깃허브에서 스프링과 관련된 프로젝트들에서 interface UserService, class UserServiceImpl와 같은 코드들을 보며 이것이 DIP를 지킨 것인가?라는 생각이 들어 interface를 만드는 것에 회의적인 생각을 가지고 있었습니다.
책을 읽으며 DIP란 단순히 interface를 만드는 것이 아니라 상위 수준 모듈이 하위 수준 모듈에 의존하는 것이 아니라, 추상화에 의존해야 한다는 것이 핵심이라는 것을 알게되었고 interface에 의존하게 되면 테스트에 용이하게 된다는 것을 깨닫게 되어 조금 더 유연한 사고를 가지게 되었습니다.
이 부분은 평소에 잘하고 있는 내용이라 생각됩니다. 유비쿼터스 언어란 모든 팀원들이 공통으로 사용하는 언어인데 회사에서 프로젝트를 진행하면서 수강권, 정규권, 정기권 등의 한 도메인 모델에 대해서 각자가 여러 용어를 사용하는 경우가 있었는데 의사소통의 오류가 생긴 경험이 있어 이를 개선하고자 용어를 명확히 정의하고 통일하려고 노력했던 기억이 났습니다.
나는 A를 말했지만 다른 사람은 B로 알아듣게 되면 의사소통에 비용이 추가되거나 엉뚱한 결과가 나오는 일이 발생할 수 있기 때문에 항상 팀 내에서 용어를 통일하기 위해 노력하자는 생각을 했습니다.
단 2개의 챕터만 읽었음에도 여태까지 제 생각의 폭이 좁았다는 점도 느꼈고 알게 모르게 잘해온 것도 있었다는 것을 느꼈습니다. 더 논리적인 사람이 되기 위해서 책에서 나온 내용들과 제 생각들을 정리하면서 꾸준히 학습해야겠다는 결심을 했습니다 !
이 글을 보신 분들께 도움이 됐길 바라며 글을 마치겠습니다. 긴 글 봐주셔서 감사합니다.
잘못된 내용이나 오타 지적 언제나 환영입니다.