본문 바로가기

Backend/DDD

[DDD] 도메인 주도 개발 시작하기(1)

728x90

개요

이번에 <도메인 주도 개발 시작하기> 책으로 스터디를 하게 됐다.

가볍게 한 번 읽고 실제 토이 프로젝트에 적용해 본 적이 있는데, 2회독을 하면서 이때 헷갈리고 어려웠던 부분들을 상기시켜 개념을 확실히 하는 과정을 기록하려고 한다. 스터디를 통해 이번 프로젝트에 적용해 볼 계획인데 그 과정도 같이 조금씩 기록할 것이다.

 

이번 기록은 DDD를 적용하는 첫 번째 단계에 관한 기록이다.

DDD의 핵심 개념들과 이들이 어떻게 도출되고, 이용되는지 공부해보자.

 

도메인

도메인이란 소프트웨어로 해결하고자 하는 문제 영역을 말한다.

그리고 이 도메인은 다시 하위 도메인으로 나눌 수 있다. 하위 도메인은 특정한 비즈니스 문제나 요구사항을 다룬다.

덕분에 도메인을 큰 하나의 덩어리로 보는 것이 아닌 하위 도메인으로 나누어 이해하고 설계할 수 있다.

 

하위 도메인

하위 도메인을 만드는 과정에서 개발자 뿐만 아니라 도메인 전문가와 이해 관계자들이 모여 단일화된 공통 언어를 정하고, 이를 이용해 소통한다. 그리고 이때 만들어진 공통 언어를 유비쿼터스 언어라고 하며, 이는 대화, 문서, 도메인 모델, 코드 등 모든 곳에 사용된다.

 

이 부분이 굉장히 매력적으로 느껴졌다. 그 이유는 개발자가 단순히 요구사항 텍스트를 해결하기만 하는 것이 아닌 도메인 전문가와의 소통을 통해 해당 도메인을 깊게 이해하게 되고, 주어진 요구사항을 해결하는 것이 아닌 실제 문제 영역을 해결하는 코드를 작성하게 되기 때문이다. 이를 위해선 꾸준한 소통과 도메인에 대한 이해도가 높아짐에 따라 수정되는 유비쿼터스 언어 문서화가 중요할 것 같다.

도메인 모델

기본적으로 도메인 모델은 특정 도메인을 개념적으로 표현한 것이다. 즉, 도메인을 이해하기 위한 개념 모델이라고 할 수 있다.

도메인 모델을 사용하면 여러 관계자들이 동일한 모습으로 도메인을 이해하고 도메인 지식을 공유하는 데 도움이 된다. 개념 모델이므로 꼭 정해진 틀이 있는 것이 아니라 도메인을 이해하는데 가장 적합한 방식으로 표현하면 된다.

 

도메인을 모델링할 때 기본이 되는 작업은 모델을 구성하는 핵심 구성요소, 규칙, 기능을 찾는 것이다. 그리고 이 과정은 요구사항에서 출발한다. 요구 사항을 통해 위 모델 구성 요소들을 결정한다.

 

엔티티와 밸류

도출한 모델은 크게 엔티티와 밸류로 구분할 수 있다. 이 둘의 차이를 명확하게 구분하는 것은 도메인을 구현하는 데 있어 중요하다.

엔티티

엔티티의 가장 큰 특징은 식별자를 가진다는 것이다. 식별자는 고유하기 때문에 두 엔티티 객체를 구분할 수 있는 수단이다.

밸류 타입

밸류 타입은 값으로서 사용된다. 따라서 식별자를 가지지 않고, 단순 값으로 치부된다.

때문에 밸류 타입은 불변성을 가지며, 값을 수정하고 싶다면 새롭게 인스턴스를 생성해야 한다.

차이점

위 두 모델의 가장 큰 차이점은 기능을 제공하는가 이다.

엔티티는 데이터와 함께 기능을 제공하는 객체지만, 밸류 타입은 기능을 제공하는 것이 아닌 두 개 이상의 데이터가 개념적으로 하나일 때 사용된다.

애그리거트

애그리거트는 복잡한 도메인을 이해하고 관리하기 쉽도록 만든 상위 수준의 모델이다. 이는 관련 객체를 하나의 군으로 묶어 상위 수준에서 도메인 모델 간의 관계를 파악할 수 있도록 한다. 애그리거트를 이용하면, 동일한 모델이라도 모델 간의 관계를 개별 모델 수준과 상위 수준에서 모두 이해할 수 있게 된다.

 

애그리거트는 모델을 이해하는 데 도움을 줄 뿐만 아니라 일관성을 관리하는 기준이 되고도 한다. 때문에 애그리거트에 속한 객체는 유사하거나 동일한 라이프 사이클을 갖는다. 

 

애그리거트는 경계를 가진다. 한 애그리거트에 속한 객체는 다른 애그리거트에 속하지 않는다.

이 경계를 설정할 때 기본이 되는 것이 도메인 규칙과 요구사항이다. 도메인 규칙에 따라 함께 생성되는 구성요소는 한 애그리거트에 속할 가능성이 높다.

 

애그리거트는 최대한 서로 독립적이어야 한다. 한 애그리거트가 다른 애그리거트에 의존하기 시작하면 결합도가 높아져 수정 비용이 증가한다. 때문에 한 트랜잭션에 두 개 이상의 애그리거트를 수정해야 하거나 애그리거트의 수정, 생성이 다른 애그리거트에 영향을 준다면 이벤트를 사용하는 것이 좋다.

 

애그리거트 루트

애그리거트에 속한 모든 객체가 일관된 상태를 유지하려면 애그리거트 전체를 관리할 주체가 필요하다. 이 책임을 지는 것이 바로 애그리거트의 루트 엔티티다. 루트 엔티티는 애그리거트의 대표 엔티티며, 모든 애그리거트에 속한 객체는 루트 엔티티에 직접 또는 간접적으로 속한다.

 

애그리거트 외부에서 애그리거트에 속한 객체를 변경할 수 있다면, 루트 엔티티가 역할을 수행하기 어려워질 것이다. 따라서 외부에서 애그리거트로의 접근은 루트 엔티티를 통해서만 가능하다.

리포지토리와 애그리거트

애그리거트는 개념상 한 개의 도메인 모델을 표현한다. 따라서 객체의 영속성을 처리하는 리포지토리는 애그리거트 단위로 존재한다.

리포지토리는 애그리거트를 영속화하고, 사용하기 위해 보통 다음 두 메서드를 기본으로 제공해야 한다.

  • save: 애그리거트 저장
  • findById: 애그리거트 조회

간접 참조

보통 JPA를 사용하게 되면, 연관관계를 통해 객체 간의 관계를 나타낸다.

 

[JPA] 다대일 연관관계 매핑

개요어플리케이션 내 객체는 다른 객체와 협력 관계를 통해 문제를 해결한다.해결하고자 하는 문제에 따라 객체들은 다양한 협력 관계를 가지며 이에 따라 연관관계도 다양하다.동일하게 엔티

choi-records.tistory.com

연관관계를 이용하면, 객체 그래프를 사용할 수 있게 되어 관련있는 객체를 쉽게 참조할 수 있게 된다.

 

하지만 애그리거트의 경계와 독립성 때문에 연관관계는 독이 될 수 있다. 애그리거트의 루트를 통해서가 아닌 연관관계를 통해 다른 애그리거트의 상태를 수정하게 되면, 수정 비용이 증가할 것이다. 따라서 간접 참조 즉 ID를 이용한 참조가 권장된다.

ID를 이용해 참조하게 되면 애그리거트의 독립성과 을 비교적 쉽게 유지할 수 있고, 애그리거트 간 의존을 낮출 수 있다.

마무리

제가 이해한 내용을 바탕으로 한 기록들입니다.

이에 대한 피드백, 지적은 환영입니다. 감사합니다.

728x90