본문 바로가기
⛓️ 생각정리

DDD 사용해보자.

by 파랭이가 룰루랄라 2023. 12. 8.

지금까지 개발을 하면서 DDD를 잘 적용하기 위해 노력했고, 그 결과로 몇가지 경우의 수를 추려낼 수 있었다. 이번 글을 통해서 DDD에 대해 간략하게 설명하고, 어떤 경우에 DDD를 고려해볼 수 있는지 설명하겠다.

 

우선, DDD란 무엇인지 간략하게 알아보자. DDD는 도메인 주도 설계(Domain-Driven Design)의 약어로, 소프트웨어 개발에서 복잡한 도메인을 이해하고 그에 맞게 시스템을 구축하는 설계 방법론이다. 이는 Eric Evans가 2003년에 발표한 "도메인 주도 설계"라는 책에서 소개되었다. 더 자세한 내용은 관련 책을 보면서 학습하는 것을 추천한다.

DDD를 몰라도 어렵지 않게 DDD에 대해 접할 수 있다. 그만큼 여러 의견이 존재하겠지만 주관적으로 DDD를 사용하는 이유는 아래와 같다.

  • 유닛 테스트로 기능을 테스트한다.
  • 중복 코드를 줄일 수 있다.

유닛 테스트로 기능을 테스트한다.

복잡한 서비스 레이어의 함수를 테스트하다 보면 test fixture 생성과 함수 mocking을 위해 수십 줄의 given절 코드를 준비해야한다. (테스트를 위한 준비를 위한 준비...)

이에 반해 DDD를 적용한 도메인 테스트는 service 레이어를 Mocking 하지 않기 때문에 더 작은 단위, 적은 given절의 코드로 테스트를 작성할 수 있다.

중복 코드를 줄일 수 있다.

예를 들어 어떤 객체를 다른 객체로 변환해야 하는 코드가 있다고 할 때. 예를 들어 피카츄 -> 라이츄로 변환해야 하는 로직이 있다고 할 때. 변환이 필요할 때마다 서비스 레이어에 다음과 같은 코드를 추가할 것이다.

val 피카츄1 = 피카츄()
val 라이츄1 = 라이츄(피카츄1.param1, 피카츄1.param2, 피카츄1.param3, name = "라이츄"...)

이런 변환 코드가 10곳에만 있어도 변경이 있을 때마다 화가 날 수 있다.

중복 코드를 줄이기 위해 DDD를 고려하는 경우

연관된 객체로의 변환

위에 언급한 것처럼 결이 비슷한 객체로 변환하는 경우이다. 추가적인 예시로 상품, 주문 상품, 주문이라는 관계가 있다고 할 때. 상품과 주문 상품은 결이 비슷하다고 하면 아래와 같이 toOrderProduct 함수를 추가하여 DDD를 사용해볼 수 있다.

val product = Product()
val orderProduct = product.toOrderProduct()

객체의 값 순환

버스 노선을 관계형 데이터베이스에 저장하고 있다고 할 때 노선은 여러 정류장을 가지고 있다. 이때, 마지막 정류장 또는 첫번째 정류장을 찾아야하는 코드가 반복된다고 하면 이때도 DDD를 적용해볼 수 있다.

// DDD 적용 전
val route = Route(...)
val departureStation = route.stations.first()
val arrivalStation = route.stations.last()

// DDD 적용 후
val route = Route(...)
val departureStation = route.getDepartureStation()
val arrivalStation = route.getArrivalStation()

매번 순환을 하면서 첫번째 정류장과 마지막 정류장을 찾는 것이 아니라 route 클래스 내부에 getDepartureStation, getArrivalStation을 구현하여 의미도 명확하게 하고, 코드의 중복도 제거할 수 있다.

객체의 상태 판단

버스의 노선이 특정 요일에는 운행을 하지 않고, 이런 정보가 Entity 클래스 내부에 저장되어 있다면 운행 여부를 확인하기 위한 코드가 프로젝트 코드 전반에 퍼져있을 수 있다. 이런 경우에도 DDD를 적용하면 코드의 중복을 제거할 수 있다.

// DDD 적용 전
val nowDayOfWeeks = LocalDate.now().dayOfWeek
val route = Route(...)
if (route.dayOfWeeks.contains(nowDayOfWeeks)) {
    println("오늘 운행함")
} else {
    println("오늘 운행 안함")
}

// DDD 적용 후
val nowDayOfWeeks = LocalDate.now().dayOfWeek
val route = Route(...)
if (route.isRunToday(nowDayOfWeeks)) {
    println("오늘 운행함")
} else {
    println("오늘 운행 안함")
}

위에 구현한 것처럼 매번 route에 저장된 dayOfWeeks를 순환하는 것이 아니라 isRunToday() 함수를 생성하여 오늘 날짜에 대해 운행 여부를 판단하는 것을 명확하게 함과 동시에 중복 코드를 줄일 수 있다.

결론

테스트 용이성과 중복 코드를 줄 일 수 있기 때문에 DDD는 강력하다. 그렇다고 무턱대고, "이거 객체 상태 판단하는 거니까 적용해야지"라고 생각한다면 1천줄짜리 거대한 도메인 클래스를 마주하게 될 것 이다. 항상 느끼지만 소프트웨어에는 정답이 존재하지 않는다. 본인이 놓인 상황을 직시하고, 판단하여 읽기 좋은 코드(1달 뒤에 봐도 의도를 알 수 있는 코드), 유지 보수가 가능한 코드를 만들면 좋을 것 같다.

'⛓️ 생각정리' 카테고리의 다른 글

테스트 코드가 없다면?  (1) 2024.01.11
Yes man이 되지 말자  (0) 2024.01.09
고객 만족과 지속적 배포  (0) 2023.11.05
2023년 1분기 회고  (0) 2023.03.31
2022년 회고 "학생에서 주니어 개발자로"  (0) 2022.12.27

댓글