사내 DDD(Domain Driven Development) 세미나 관련해서 정리한 내용입니다. Domain 중심으로 개발을 진행하는 관점에 대한 내용이 주된 내용입니다.

도메인 모델

  • 소프트웨어로 해결할 문제 영역

  • 도메인 모델
    • 도메인의 구성요소를 개념적으로 표현

    • 정적 모델
      • 클래스, ER
    • 동적 모델
      • 커뮤니케이션, 시퀀스, 상태
  • 단순 데이터 모델
    • 단순 속성 나열
    • 반쪽짜리 모델
      • Action이 없음(?)
  • 도메인 모델에 필요한거
    • 표현력
      • 불피요한 번역 /해석이 필요하지 않도록 (코드를 보고 알아야…)
    • 기능
      • 객체 모델로 표현
    • 동적 측면 / 제약 조건 표현
      • 메소드를 잘활용하면 제약조건 표현에 좋다.(네이밍으로 표현(?))
    • 정적 : 모델링 할때 모델(도메인)에 상태와 행위를 같이 표현해야 보기 좋다.

DDD

  • 모델 구성요소
    • 기본모델
      • 엔티티, 밸류
    • 묶음
      • 에그리거트
    • 기능
      • 객체 모델
      • 서비스
    • 영속
      • 레파지 토리
  • 엔티티 중심 모델 만들기
    • 식별자 생성
    • 특정 규칙, UUID, 시스템 식별자, 일려번호 등…
  • 밸류 : 개념적으로 하나의 값
    • 주소, 수신자등의 값을 ShippingInfo라는 Value로 표현
  • 모델에 기능 넣기
    • https://wckhg89.github.io/archivers/oop
    • 메소드로 기능과 제약 표현
      • 해당 메소드 이름만 보고 모델이 어떤 일을 하는지 알기 좋다
    • How ?
      • 가능하면 public set 메소드를 쓰지말자
        • 도메인의 의도를 사라지게 하는 효과가 있다.
      • get도 웬만하면 쓰지말자
        • 도메인에 getter setter를 넣으면 절차지향적인 개발이 되기 쉽다.
      • DTO 영역 경계 간 데이터를 전달하기 위한 DTO와는 구분
    • 주의사항
      • 모든 엔티티들에게 연관관계를 맺지 말자 (연관관계 최소화)
        • n+1 problem을 야기하기가 쉽다.
    • 개발 코드와 상황에 대한 불일치를 계속해서 네이밍을 바꾸면서 발전 시켜야한다.

애그리거트

  • 복잡해지는 모델의 경계를 잘 정해야 한다.
  • 개별 모델을 상위 수준에서 적당하게 묶어주는 단위

  • 유사한 라이프 사이클을 가지는 단위 (한 애그리거트에 묶일 가능성이 높음)

  • 한 애그리거트는 다른 애그리거트에 있는 객체와 엮이지 않음
    • 주문 1 애그리거트, 주문 2 애그리거트는 엮이지 않음
  • 애그리거트 루트
    • 애그리거트의 모든 객체는 루트에 직/간접적으로 속한다.
    • 애그리거트에 속한 객체의 상태를 바꿀 수 있는것은 애그리거트 루트를 통해서만 가능하다.
  • 애그리거트의 트랜잭션
    • 범위는 자기자신의 애그리거트로 제한해야한다.
  • 애그리거트의 크기 경계
    • 상품 (1) : 리뷰 (N)
      • 상품과 리뷰는 함꼐 바뀌지 않음 (라이프 사이클이 다름)
      • 리뷰의 관리 주체(리뷰를 다는사람)와 상품의 관리 주체(관리자)가 다름

아키텍처

  • DIP (의존관계 역전의 원칙)
    • 고수준 모듈 / 저수준 모듈

    • 고수준 : 가격할인 계산 (고객정보를 구함 / 룰을 이용해서 할인 금액 구함) (더 상위 수준)

    • 저수준 : RDMS JPA / Drools로 룰을 적용 (더 저수준)

    • 의존의 방향 (고수준 -> 저수준)
      • 저수준 고수준이 섞여 있으면 저수준의 로직 변화에 고수준이 영향을 받게 된다.

      • DIP 적용으로 방지 할 수 있다.

        • 전략 패턴(?)
        • 인터페이스를 활용하여 구현체를 따로 두면 저수준의 구현이 바뀌더라도 다른 구현체로 갈아 끼워주면 된다.
        • 인터페이스의 네이밍은 고수준 관점에서의 네이밍 혹은 기능을 정의 하는게 좋다.
    • 영역 (패키지 구성(?))
      • 표현 (컨트롤러)
      • 응용 서비스
        • 사용자의 요청을 처리할 기능을 구현
        • 처리 흐름을 구현!
        • 도메인 로직은 도메인 영역에 위임

        • 도메인에 역할을 위임을 잘 할 수록 더 짧은 코드가 작성된다.
      • 도메인
      • 인프라 스트럭쳐
        • 응용서비스 혹은 도메인에서 사용하는 실제 구현
  • 응용 서비스의 입력과 출력
    • 입력
      • 필요한 파라미터만 넘기자
    • 출력
      • 조회전용 모델 (CQRS)
    • 트랜잭션
      • Transaction 단위가 된다.
    • 응용 서비스에 도메인 로직을 넣지 않기

    • 몇가지 고민거리
      • 서비스의 크기 어디까지
      • 인터페이스를 구지 가져야하나?

      • 구현의 편의성을 우선으로 생각하는 편이십니당.
  • 표현영역 (Controller)
    • 사용자에게 화면을 제공하고 사용 흐름 제어
    • 사용자 요청을 응용 서비스에 전달
    • 세션 관리
  • 인프라 스트럭쳐
    • 기반 구현 기술 제공 (실제 구현)
    • DIP 고려
  • 모듈 구성
    • 각 도메인 별로 패키지를 둔다.
      • 그 하위에 표현 / 응용 / 도메인 / 인프라 스트럭쳐 패키지를 둔다. (DDD 관점)
  • CQRS
    • 상태를 바꾸는 기능(Command) / 조회하는 기능(Query)

    • 위의 상황에 따라서 모델을 나누자

      • 주문 상태 변경 (Command Model)
      • 주문 내용 조회 (Query Model)

리파지토리, 모델 구현

  • 기본적으로 애그리거트(루트) 단위로 리파지토리 생성
    • 테이블 단위로 존재하는 것이 아니다.
  • 리포지토리는 완전한 애그리거트를 다룸
    • 로딩 시점에 애그리거트에 속한 모든 연관 객체 로딩
      • 즉 엔티티나 콜렉션에 대해 EAGER 로딩 기본으로 사용
    • 저장 시점에 애그리거트에 속한 모든 연관객체 저장
    • 삭제 시점에 애그리거트에 속한 모든 연관객체 삭제
  • 별도 테이블이라고 무조건 Entity 라고 생각하지 말자
    • 한 애그리거트에 속해 있다면 Value가 아닐지 의심해보자
      • Value라고 생각되면, JPA의 @Embeddable 어노테이션을 사용해서 애그리거트 루트 엔티티에 밸류로 포함시키자.
    • 밸류 컬렉션
      • 애그리거트 내부의 콜렉션은 주로 밸류 콜렉션
        • 엔티티일 경우는 드물다.
  • 애그리거트간의 연관
    • 애그리거트 루트를 참조한다.
    • 직접 참조보다는 ID를 통한 간접 참조를 선호
      • 편한 탐색 오용 방지 (다른 애그리거트 루트에서 다른 커스터머의 패스워드를 손쉽게 바꿀 수 있게된다.)
      • 성능에 대한 고민 제거
      • 시스템 확장시 유리

올바른 소프트웨어?

  • 사용자에게 가치를 주는 SW
    • 만드는게 어떤건지를 알아야 할텐데…
  • 도메인 중심 사고
    • 도메인 중심으로 사고하게 되면 만드는게 무엇인지 잘 이해할 가능성이 높아진다.
  • BDD (Behavior-Driven Development)

가치의 변화에 대응

  • 변화에 대응할 수 있는 역량 필요

    • 객체지향, 함수형 사고
    • 리펙토링
    • TDD
    • 기술

요즘 기술, 좋은 기술 ?

  • 상황에 맞는 기술 선택

  • 조직 상황을 고려하지 않은 기술은 변화에 빠르게 대응할 수 없게 만들어 경쟁력 낮춤