본문 바로가기
Conference

ㄷㄷㄷ: Domain Driven Design과 적용 사례공유

by liony 2024. 7. 20.
ㄷㄷㄷ: Domain Driven Design과 적용 사례공유 / if(kakao)2022
카카오엔터테인먼트 테크 블로그 포스트

 

 

DDD란?

각각 기능적인 문제의 영역을 정의하는 도메인과 그 도메인을 사용하는 비즈니스 로직을 중심으로 설계하는 것을 DDD라고 한다.

특징

  • 데이터 중심이 아닌 도메인의 모델과 로직에 집중
  • 유비쿼터스 언어, 보편적 언어 사용(동일한 표현과 단어로 구성된 단일화된 언어 체계 사용. 즉, 쉽게 이야기하면 업무 용어를 통일하여 개발단 뿐만 아니라 기획, 사업단까지 단일화된 커뮤니케이션이 가능하도록 함)
  • 소프트웨어 엔티티와 도메인 간 개념 일치 (분석 모델과 설계가 다다르고 이것과 코드가 다른 구조가 아닌 함께 움직이는 모델 지향)

왜 DDD인가?

  • 반복적인 설계 수정과 테스트 자동화로 쌓아나가는 TDD
  • 비즈니스와의 협업을 중시하면서 테스트 자동화를 가져가는 BDD
  • 예시로 든 레거시 사이트에는 이미 큰 틀과 검증된 플로우가 존재. 완전히 뒤집어 엎는 것보다 필요한 기능을 구별하고 필터링하여 개선하는 점진적 향상 선택

DDD 적용에 필요했던 것들

  • DDD에서 중요한 개념 3가지
    • Bounded Context : 범위를 구분해놓은 하위 도메인 개념
      • 각 영역을 분류하여 영역 간 의존성을 줄이기 위해 일종의 경계를 구성한다. 이는 MSA 상 각각의 서비스로 나뉜다. 데이터 조회를 위해서는 서로 API를 사용하여 통신하게 하면서 각 도메인을 철저히 분리.
    • Context Map : Bounded Context 간 관계도. 전체적인 흐름 예상 가능.
    • Aggregate : 데이터 변경 단위, 일종의 라이프사이클이 같은 도메인을 한 데 모은 것.
      • Context Map을 기반으로 서비스를 구현 시, 해당 서비스가 갖고 있는 많은 객체들을 그대로 사용하는 것은 의미가 없다. 따라서 각 도메인 영역을 대표하는 도메인 객체 집합을 설계한 것이 Aggregate이다.
      • 특징
        1. 데이터 변경 단위
        2. Aggregate에 대한 접근은 Root Entity를 통해서만 가능하다.
          1. 이를 통해 개별 객체 간 상호작용보다는 객체들 사이의 관계를 좀 더 넓은 시야로 바라볼 수 있다.
          2. 제약사항을 하나의 맥락으로 관리할 수 있고 Aggregate 간 관계를 파악함으로써 비즈니스 로직에 집중할 수 있다.

DDD의 대표적인 아키텍처

  1. Layered Architecture
    • User Interface : I/O 관련 통신 변환 등을 담당하는 계층
    • Application : 도메인 객체를 직접 사용하면서 어떻게 사용할지 정의하는 usecase를 구현하는 계층
    • Domain : 비즈니스 룰 등의 도메인과 관련된 직접적인 기능을 포함하고 객체를 이루는 계층
    • Infrastructure : Repository 나 Persistence, 메시지 전송과 같은 기술적 기능 제공 계층
  2. Clean Architecture
    • External Interface : 데이터베이스나 웹 프론트엔드 프레임워크를 기반으로 한 UI
    • Interface Adapter : 데이터베이스나 파일 시스템과 같은 외부 소스에 저장하거나 내부의 Use Case를 위해 양방향으로 데이터 변환 을 수행하는 커뮤니케이터
    • Use Case : 핵심 비즈니스 로직이나 애플리케이션 로직으로 구성
    • Entity : 엔티티 또는 도메인으로 구성된, 내부 유효성 검사 혹은 모든 도메인에 적용되는 일반 논리를 가진 계층
  3. Hexagonal Architecture
    • 클린 아키텍처와 비슷한 개념을 사용하지만 port 라는 개념을 명시하고 있음. 그래서 포트와 어댑터, 포트 앤 어댑터 아키텍처라고도 한다.
    • Port : 데이터를 알맞게 변환해주는 커뮤니케이터 역할
    • Adapter : Port를 통해 도메인 로직에 접근
    • 게임을 예시로 들어 설명하자면, 조이스틱이라는 인터페이스를 통해 받아오는 인풋과 사운드나 디스플레이로 출력되는 아웃풋이 있다. 이 때 인풋을 위한 조이스틱과 아웃풋을 위한 스피커, 디스플레이는 포트에만 알맞게 꽂으면 어느 회사 것이든 사용할 수 있다. 헥사고날 아키텍처에서도 마찬가지로, 각 포트에 맞는 어댑터에서 알맞게 구현해주면 어떤 유틸리티든 가져다 쓸 수 있다.
    • 비즈니스 로직이 표현 로직이나 데이터 접근 로직에 의존하지 않는 것이 핵심이다.

Hexagonal Architecture를 선택한 이유

  • Layered Architecture는 비즈니스 로직이 커지면서 애플리케이션 레이어가 오염될 가능성이 있음.
  • Clean Architecture와 유사하지만 포트 앤 어댑터라는 비교적 더 명확한 아키텍처를 가짐.

실제 적용 시 고려한 점

  • CRUD 중 Read 를 LoadProductPort로 따로 빼고 그 외 나머지는 SaveProductPort로 분리함. 이는 CQRS 패턴을 따른 것인데, 추후에 조회 성능 작업을 위해 아마존 SQS나 RabbitMQ 등을 사용하여 Event Driven 형식으로 바꿀 계획이었다고 함.
  • DB에 저장된 데이터를 도메인에 맞게 매핑해주는 작업이 필요하므로 Mapper를 구현해야 했음. JPA를 사용했기 때문에 JpaEntity <-> DomainEntity 간 변환이 필요했음.

단점

  • 아키텍처 구현에서 생성되는 생각보다 많은 코드
    • 매퍼 클래스와 같이 도메인이 사용하기 위해 별도로 구현해야 하는 코드들이 추가로 필요
  • 각 도메인에 대한 높은 이해도 필요

장점

  • 보편적 언어 사용에 따른 빠른 커뮤니케이션
    • 개발단에서 사용했던 데이터 중심의 명칭과, ui나 앱 등 기획과 사업단에서 사용하던 명칭을 통일하여 이해도를 높임
  • 도메인 간 관계가 복잡한 경우 큰 틀에서 정리 가능(aggregate)
  • 도메인 분리에 따른 유지보수 편의성
  • 새로운 기능 및 요구 사항에 대한 유연성
  • 개발자 측면에서의 장점
    • aggregate 사용으로 인한 도메인 캡슐화 자동 적용
      • 하위 분류 도메인에 접근하기 위해서는 루트 엔티티를 통해야만 한다.
    • Loose coupling, High cohesion
      • use case 나 port 등 사용
      • 도메인 로직과 서비스 로직을 필요한 곳에 모아서 사용
    • 도메인 로직의 분리로 비즈니스 로직에 집중
    • 코드 가독성 향상

 


 

사이드 프로젝트 적용을 위해 DDD에 대해 알아보면서 실무에서는 어떤 방식으로 적용하는지 궁금했다. 그러던 중 레거시 프로젝트를 MSA로 전환하는 과정에서 DDD를 적용한 레퍼런스를 카카오 블로그와 컨퍼런스 영상으로 보게 되었다.

해당 내용을 통해 어떤 상황에서 어떤 이유로 DDD를 선택했는지, 또 어떻게 설계하고 적용했는지에 대한 과정을 나름 면밀히 볼 수 있었다.

막상 DDD를 적용한 설계를 해봐야겠다라고 생각했을 때 감이 잘 잡히지 않아 시작부터 어려운 지점이 있었는데 설계에 대한 궁금증도 어느정도 해소되고 개념적인 부분에서도 감이 좀 더 잡히는 것 같다.