Easy Understanding

(1) DDD란 무엇인가 - 데이터 중심 개발과 도메인 중심 개발 본문

Study

(1) DDD란 무엇인가 - 데이터 중심 개발과 도메인 중심 개발

appleg1226 2022. 2. 6. 16:08

참고로 스터디는 다음의 책을 통해서 진행했다.

 

DDD 간단 소개

우선 DDD(Domain-Driven Design)에 대해서 간단하게 소개를 하자면 다음과 같다.

 

  1. DDD는 단순한 코딩/아키텍처 구성 방법이 아니다. 즉 패턴과 설계와 아키텍처를 모조리 합친 그 무언가 개발의 새로운 패러다임이다. 
  2. DDD는 기존의 설계 방식과 개발 방식을 새로운 관점으로 보게 해준다.

개발을 하면서 부딪히는 문제는 여러 가지가 있지만 크게 두 가지로 나눌 수 있다.

 

  1. 사용자(고객, 회사 등)에서 제시하는 복잡하고 자주 변화하는 요구사항
  2. 성능이나 협업에서 발생하는 기술적인 어려움

DDD는 보통 첫 번째 원인 해결에 초점을 맞춘 방법론이다.

기획자(도메인 전문가)들의 요구사항을 개발 언어로 변환하는 '비효율'

최대한 '효율'적으로 바꾸려는 통찰이 집약된 방법론이다.

 

그러나 DDD는 그 필요성이 크지 않은 상황이라면 도입하는 것에 너무 큰 비용이 든다.

학습해야 할 것도 많고, 거쳐야 할 과정도 복잡하기 때문이다.

 

그러므로 DDD는 복잡한 비즈니스 상황에서 문제를 해결하기 위해 사용할 수 있는 방법론이라고 말할 수 있겠다.

 

'Implementing DDD'의 저자 반 버논은 DDD 도입이 필요하지 않은 경우에 대해서는 다음과 같이 말하고 있다.

당신의 애플리케이션이 완전히 데이터 중심(data-centric)이며 순수한 CRUD 솔루션에 정말 잘 맞아서, 모든 동작이 간단한 데이터베이스 쿼리를 통해 기본적인 생성, 읽기, 갱신, 삭제 등을 수행할 뿐이라면 DDD가 필요하지 않다. 당신의 팀은 그저 예쁜 데이터베이스 테이블 편집기만 있으면 된다. ...... 당신이 단순한 데이터베이스 개발 도구를 사용해 솔루션을 만들 수 있다면, DDD에 회사의 시간과 돈을 낭비하지 말라.

 


DDD 적용 과정

DDD가 복잡하다고 말하는 이유는, 실제로 도입을 하려면 다음의 과정들에 전부 익숙해져야 하기 때문이다.

 

  1. DDD에서 말하는 도메인/서브도메인/바운디드컨텍스트를 구분하고 정의하기
  2. 이를 바탕으로 Aggregate/Entity/Value Object/Repository 등을 구현하기
  3. 이에 적합한 아키텍처를 결정하고 개발하기 
  4. 그리고 팀 구성원이 모두 이에 대한 합의를 이루어야 하고, DDD에 대한 지식들이 학습되어 있어야 한다.

DDD는 마냥 쉽게 도입을 결정할 수 있는 부분이 아니다.

아마도 팀에 DDD를 사용하자고 설득하려면 적지 않은 노력이 들 것이다.

 


도메인 주도설계와 개발 방법론의 관계

하지만 에릭 에반스의 'DDD' 저작 이후로 시대가 지나면서 다음과 같은 기술들과 아키텍처가 화두에 오르게 되었다. 

MSA, Hexagonal Architecture, CQRS Patterns, Event-Driven, Event-Sourcing

 

신기하게도 DDD는 위와 같은 패턴/아키텍처들과 조합되었을 때 많은 시너지가 나는 방법론이다.

반 버논의 'Implementing DDD' 에서는 실제로 위의 방법론들에 대한 대략적인 소개와

DDD에서 활용 가능성에 대해서 소개해 준다.

 

최근 핫하다고 불리는 방법론들과의 통합도 잘 이루어질 뿐더러,

비즈니스의 복잡성 문제까지도 해결해줄 수 있는 방법이라니...

 

이 정도라면, 딱히 필요하지 않더라도 한 번은 공부해 볼만하지 않을까 싶다.

아마 DDD로 넘어갈 생각이 없더라도 DDD를 통해서 얻을 수 있는 인사이트는 무조건 늘어날 것이다.

 


DDD = 데이터 중심 설계에서 벗어나는 것

나는 지난 프로젝트(좌석 예약 프로젝트)를 진행할 때 다음과 같은 과정을 거쳤다.

 

1. DB를 설계한다. 

User, Seat, Reservation, Review, Favorite
Reservation은 User와 Seat을 참조한다.
Review는 Reservation을 참조한다.
Favorite은 User와 Seat을 참조한다.

 

2. 이를 토대로 바로 JAVA 패키지를 구성한다.

com.example.seat-reservation.controller: UserController.java, SeatController.java, ...
com.example.seat-reservation.service: UserService.java, SeatService.java, ...
com.example.seat-reservation.domain: User.java, Seat.java, ...
com.example.seat-reservation.repository: UserRepository.java, SeatRepository.java, ...
com.example.seat-reservation.SeatReservationApplication.java

 

3. Service에서는 CRUD 로직을 감싼 메서드들을 만든다. 아니면 JPA Repository를 바로 사용한다.

user: createUser(), login(), changePassword()
seat: createSeat(), getSeat(), getSeats(), updateSeat(), deleteSeat()
reservation: createReservation(), getReservation(), deleteReservation(), modifyReservation()

 

4. Controller 또는 Service에서 이를 조합하여 API를 제공한다.

 

 

이러한 패턴은 굉장히 일반적이고 효율적인 방식이다. 

아마 대부분은 이런 식으로 Spring을 학습했을 것이며, 계속 사용하며 익숙해졌을 것이다.

 

DDD가 지향하는 아키텍처는 위와 크게 다르지는 않다.

하지만 우리가 생각하는 이런 생각의 개념들을 완전히 바꾸지 않으면 DDD를 적용할 수 없다.

 

DDD에서는

 

1. 우선 도메인 / 서브도메인 / 바운디드 컨텍스트 를 정의하고 설계도를 그린다.

 

2. 이를 토대로 Entity, Value Object, Aggregate를 도출해낸다.

 

3. 또한 이를 토대로 패키지 구조를 구성한다.

 

4. 어떤 아키텍처를 도입할 것인지 결정하고 개발한다.(Layered, Hexagonal 등)

 

여기서는 정확하게 각 단계를 설명하지 않지만,

확실한 건 DDD 도입을 위해서는 공부하고 익숙해 져야 할 것들이 많다는 것이다.

 


 

기존 코드 VS DDD

데이터 중심의 코드에서는 보통 다음의 패턴으로 개발이 많이 이루어진다.

public void createReservation(User user, Seat seat, LocalDateTime startTime, LocalDateTime endTime){
    Reservation reservation = new Reservation();
    reservation.setUserId(user.id);
    reservation.setSeatId(seat.id);
    reservation.setStartTime(startTime);
    reservtiion.setEndTime(endTime);
    this.reservationRepository.save(reservation);
}

위의 코드는 getter와 setter를 통해서 비즈니스 로직을 처리하고 있다.

 

그러나 DDD에서는 위와 같은 코드를 'Anemic Domain Model(무기력증에 걸린 도메인 모델)'이라고까지 부르면서 경계한다.

DDD에서는 위와는 다르게 Entity에 직접 영향을 미치는 코드를 도메인 영역이라는 부분으로 감춰버린다.

그럼으로서 코드의 흐름을 이전과 다르게 더 직관적으로 읽을 수 있도록 도와주기도 한다.

public void makeReservation(User user, Seat seat, LocalDateTime startTime, LocalDateTime endTime){
    this.reservationCommandService
        .makeReservation(user, seat, startTime, endTime);
}

 

만약에 예약 시작 시간을 변경해야 할 필요가 있다면 기존에는 다음의 방법을 사용했을 것이다.

reservation.setStartTime(start);

하지만 DDD에서는 아래와 같은 인터페이스를 두고 시작 시간을 변경하고자 할 것이다.

public interface Reservation{
    public void changeStartTimeTo(LocalDateTime start);
}

reservation.changeStartTimeTo(start);

 

뭔가 다른 개발자가 보기에도 아래와 같은 방식이 보기에 더 이해가 잘 될 것 같다.

즉 DDD를 적용하면 코드에 대한 가독성과, 로직의 흐름을 직관적으로 파악하는 데에 도움이 된다.


사실 DDD는 객체지향의 끝판왕이라는 생각이 든다.

우리는 예전에 객체지향을 공부할 때 마치 '실제로 존재하는 객체'가 어떤 행위를 하는 것처럼 구성하는 방법론이라고 까지 배웠다.

 

DDD는 실제로 일어나야 하는 과정을 자세하게 설명해 주는 데에 능한 방법론이다.

도메인 지식이 적은 사람이 코드를 봤을 때,

메서드 이름만으로 무엇을 하는지 바로 알 수 있을 정도가 되도록 하는 것이 DDD 지향적인 코드의 장점이다.