책/도메인 주도 설계 첫걸음

[도메인 주도 설계 첫걸음] Part 2. 전술적 설계

2023. 9. 3. 23:46
목차
  1. 5장) 간단한 비즈니스 로직 분석
  2. 트랜잭션 스크립트
  3. 액티브 레코드
  4. 6장) 복잡한 비즈니스 로직 다루기
  5. 도메인 모델
  6. 도메인 모델의 구현
  7. 도메인 모델의 구성요소
  8. 1) 밸류 오브젝트
  9. 2) 엔티티
  10. 3) 애그리게이트
  11. 4) 도메인 서비스
  12. 7장) 시간 차원의 모델링
  13. 이벤트 소싱 도메인 모델 패턴
  14. 이벤트 소싱 애그리게이트의 작업 단계
  15. 이벤트 소싱 도메인 모델 패턴의 장점
  16. 이벤트 소싱 도메인 모델 패턴의 단점
  17. 8장) 아키텍처 패턴
  18. 계층형 아키텍처
  19. 포트와 어댑터 아키텍처 (= 헥사고날 아키텍처, 클린 아키텍처)
  20. 의존성 역전 원칙
  21. CQRS(command-query responsibility segregation)
본문은 도메인 주도 설계 첫걸음을 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.

 

5장) 간단한 비즈니스 로직 분석

트랜잭션 스크립트

  • 프로시저를 기반으로 시스템의 비즈니스 로직을 구성하는 패턴
    • 입출력은 퍼블릭 인터페이스를 통하고 내부 구현은 프로시저로 구성된 스크립트
      • 프로시저는 간단하고 쉬운 절차지향 스크립트(procedure script)로 구현
      • 절차지향 스크립트? 작업을 순차적으로 수행하는 것을 의미(↔ 객체지향 언어)
    • 내부적으로는 데이터베이스에 직접 접근한다
  • 트랜잭션을 적용해 작업이 모두 성공하거나 실패함을 보장해야 한다
  • 단순함이 최고의 장점
    • e.g. 원천 시스템의 데이터를 변환해서 목적 시스템에 전달 (단순 DTO Query를 생각해보자)
    • 비즈니스 로직이 단순한 지원 하위 도메인에 적합
    • 어댑터 or 충돌 방지 계층에도 사용 가능
  • 비즈니스 로직이 복잡할 때는 사용하면 안 된다 (= 핵심 하위 도메인에는 사용하면 안 됨!)
    • 비즈니스 로직의 중복
    • 중복된 코드가 동기화되지 않을 때 일관성 없는 동작 발생할 수 있음

 

액티브 레코드

  • 비즈니스 로직이 단순하지만, 자료구조가 복잡할 때 사용하는 패턴
    • 데이터 접근 로직을 구현한다
    • 트랜잭션 스크립트로 시스템의 비즈니스 로직을 만든다
    • 액티브 레코드는 트랜잭션 스크립트와 달리 데이터베이스에 직접 접근하지 않는 대신, 트랜잭션 스크립트가 액티브 레코드 객체를 조작한다
  • 간단한 CRUD 데이터 접근 방법을 제공하는 자료구조
    • 비즈니스 로직이 포함될 수 있으나, CRUD 및 입력 유효성 검증과 같은 간단한 비즈니스 로직만 수행 가능
  • 액티브 레코드는 데이터베이스 스키마 매핑의 복잡성을 해소한다는 것이 특징
  • 보다 자세한 예시는 아래 문서를 참고하자
    • https://en.wikipedia.org/wiki/Active_record_pattern
    • https://guides.rubyonrails.org/active_record_basics.html

 

 

6장) 복잡한 비즈니스 로직 다루기

도메인 모델

  • 복잡한 비즈니스 로직을 다루기 위한 패턴
  • CRUD 인터페이스 대신 복잡한 상태 전환, 항상 보호해야 하는 규칙인 비즈니스 규칙과 불변성을 다룬다
  • 도메인 모델 패턴은 복잡한 비즈니스 로직을 갖는 하위 도메인에만 적용되므로 이를 핵심 하위 도메인으로 가정해도 좋다

 

도메인 모델의 구현

  • 행동(behavior)과 데이터(data)를 모두 포함하는 도메인의 객체 모델
  • DDD의 전술 패턴인 애그리게이트, 밸류 오브젝트, 도메인 이벤트, 도메인 서비스는 모두 객체 모델의 구성요소

복잡성

  • 모델에는 인프라 또는 기술적인 관심사를 피해야 한다 (e.g. 데이터베이스, 외부 API 호출 등)
  • 결국 모델은 플레인 올드 오브젝트(plain old object)가 된다 (=POJO)

유비쿼터스 언어

  • 도메인 모델의 객체가 기술적 관심사가 아닌 비즈니스 로직에 집중하게 해야 한다
  • 이로써 코드에서 유비쿼터스 언어를 사용하게 하고 도메인 전문가의 멘탈 모델을 따르게 하는 효과가 있다

 

도메인 모델의 구성요소

1) 밸류 오브젝트

  • 값에 의해 식별되는 객체
data clss Color(
    val red: Int,
    val green: Int,
    val blue: Int
)
  • 불변 객체이다
    • 필드 중 하나가 바뀌면 개념적으로 새로운 인스턴스가 생성되는 것이다
  • 밸류 오브젝트를 사용하지 않고 원시 데이터 타입에 전적으로 의존해서 비즈니스 도메인을 표현하는 것은 원시 집착 코드 징후(primitive obsession code smell)이다
    • 유효성 검사 로직이 중복됨
    • 값이 사용되기 전에 유효성 검사 로직을 호출하게 하기 어려움
    • 유지보수가 어려움
    • https://refactoring.guru/ko/smells/primitive-obsession 참고
  • 밸류 오브젝트의 장점
    • 명료성 향상 (변수의 의도가 명확해짐)
    • 값을 할당하기 전에 유효성 검사를 할 필요가 없어짐
    • 응집된 로직은 한 곳에서 구현되고 쉽게 테스트할 수 있음
    • 코드에서 비즈니스 도메인의 개념을 표현 (코드에서 유비쿼터스 언어를 사용하므로!!)
  • 사용하는 경우
    • 가능한 모든 경우에 사용하는 게 좋음
    • 다른 객체의 속성을 표현하는 도메인 요소에 밸류 오브젝트를 사용하라

2) 엔티티

  • 식별 필드에 의해 식별되는 객체
    • 식별 필드는 각 엔티티 인스턴스마다 고유해야 한다
    • 엔티티의 식별 필드의 값은 엔티티의 생애주기 내내 불변이어야 한다
class Person(
    val personId: Id, // 식별 필드
    val name: Name
)
  • 밸류 오브젝트와 반대로 불변이 아니라 변할 것으로 예상됨
  • 앞서 소개한 밸류 오브젝트는 엔티티의 속성을 설명하는 것
  • 엔티티는 단독으로 구현되지 않고, 애그리게이트 패턴의 컨텍스트에서만 구현된다

3) 애그리게이트

  • 엔티티 + α
    • 애그리게이트는 기본적으로 엔티티
    • 따라서 명시적인 식별 필드가 필요하고, 상태가 변할 것으로 예상됨
    • 그러나 단순한 엔티티 그 이상의 존재다 (데이터의 일관성을 보호한다!)
  • 데이터의 일관성을 보호하는 것이 존재 목적
    • 데이터의 일관성은 애그리게이트의 비즈니스 로직을 통해서만 애그리게이트의 상태를 변경하게 해야 강화됨
    • 애그리게이트의 퍼블릭 인터페이스는 입력값의 유효성을 검사하고 관련된 모든 비즈니스 규칙과 불변성을 강화하는 것을 담당
    • 퍼블릭 인터페이스로 노출된 상태 변경 메서드는 커맨드라고 함
      • 일반적인 퍼블릭 메서드로 만들거나 커맨드 객체를 별도로 메서드의 아규먼트로 넘겨서 구현할 수 있음
    • 외부 객체는 애그리게이트 내의 데이터 필드를 읽을 수만 있음
    • 결국 애그리거트는 일관성을 강화하는 경계가 됨
  • 트랜잭션 경계로서의 애그리게이트
    • 애그리게이트의 상태는 자신의 비즈니스 로직을 통해서만 수정될 수 있으므로 애그리게이트는 트랜잭션 경계의 역할을 함
    • 애그리게이트 상태 변경은 원자적인 단일 오퍼레이션으로 트랜잭션 처리
      • 애그리게이트의 상태가 수정되면 모든 변경이 커밋되거나 모두 롤백됨
    • 다중 애그리게이트 트랜잭션은 원칙적으로 지원되지 않는다
  • 애그리게이트간 참조
    • 애그리게이트가 너무 커지면 성능과 확장 문제가 생길 수 있음 (같은 트랜잭션 경계를 공유하므로)
    • 따라서 비즈니스 로직에 따라 강력한 일관성이 필요한 정보만 애그리게이트에 포함되면 됨
    • 애그리게이트를 가능한 작게 유지하고 강력하게 일관적으로 상태를 유지할 필요가 있는 객체만 포함
    • 애그리게이트 외부의 객체 참조는 ID를 사용한다
      • 해당 객체가 애그리게이트 경계에 속하지 않음을 명확히 하고, 각 애그리게이트가 자신의 트랜잭션 경계를 갖게 보장함
  • 애그리게이트 루트
    • 애그리게이트는 엔티티의 계층 구조를 대표한다
    • 따라서 그중 하나만 애그리게이트의 퍼블릭 인터페이스, 즉 애그리게이트 루트로 지정돼야 한다
  • 도메인 이벤트
    • 비즈니스 도메인에서 일어나는 중요한 이벤트를 설명하는 메시지
    • 이미 발생된 것이므로 과거형으로 명명
    • 비즈니스 도메인에서 일어난 일을 설명하고, 이벤트와 관련된 모든 필요한 데이터를 제공하는 것이 목적

4) 도메인 서비스

  • 비즈니스 로직을 구현한 상태가 없는 객체
  • 밸류 오브젝트에도 속하지 않거나 복수의 애그리게이트에 관련된 비즈니스 로직을 다루게 될 때 사용하는 패턴
  • 여러 애그리게이트의 데이터를 읽는 것이 필요한 계산 로직을 구현하는 것을 도와줌

 

 

7장) 시간 차원의 모델링

이벤트 소싱 도메인 모델 패턴

  • 데이터 모델에 시간 차원을 도입
  • 애그리게이트 수명주기의 모든 변경사항을 분석하는 이벤트를 유지

 

이벤트 소싱 애그리게이트의 작업 단계

  • 애그리게이트의 도메인 이벤트를 로드
  • 분석을 목적으로 프로젝션해서 상태 표현을 재구성
  • 애그리게이트 명령을 실행하여 비즈니스 로직을 수행하고, 결과적으로 새로운 도메인 이벤트를 생성
  • 새 도메인 이벤트를 이벤트 스토어에 커밋
    • 이벤트 스토어는 추가만 가능한 저장소 (수정 or 삭제 X)
    • 특정 비즈니스 엔티티에 속한 모든 이벤트를 가져오고 이벤트를 추가해야 함

 

이벤트 소싱 도메인 모델 패턴의 장점

  • 애그리게이트의 모든 과거 상태를 추적할 수 있다
  • 소싱한 이벤트를 유의미한 모델로 자유롭게 변환할 수 있다
  • 영속적인 도메인 이벤트는 애그리게이트 상태에서 발생한 모든 것에 대한 강력하게 일관된 감사 로그(audit log)를 나타낸다
  • 고급 낙관적 동시성 제어를 사용할 수 있다
    • 읽기 데이터가 기록되는 동안 다른 프로세스에 의해 덮어 쓰어지는 경우를 방지한다

 

이벤트 소싱 도메인 모델 패턴의 단점

  • 러닝 커브가 높다
  • 이벤트 소싱 모델을 변화시키는 것이 어렵다
  • 아키텍처의 복잡도가 높아진다

 

 

8장) 아키텍처 패턴

  • 여기서 소개하는 아키텍처 패턴들은 시스템 전체의 구성 원칙이나 전체 바운디드 컨텍스트를 위한 고수준의 아키텍처 패턴이 아니다
  • 바운디드 컨텍스트 내의 여러 하위 도메인에서 필요에 따라 다양한 패턴들을 적용할 수 있다 (수직적 분할)

계층형 아키텍처

  • 기본적으로 프레젠테이션 계층, (서비스 계층), 비즈니스 로직 계층, 데이터 접근 계층으로 나눠짐
  • 탑-다운 구조의 의존 관계를 지님
  • 세부 구조 (위에서부터 고수준)
    • 프레젠테이션 계층
    • 비즈니스 로직 계층
    • 데이터 접근 계층

프레젠테이션 계층 (= 사용자 인터페이스 계층)

  • 기본적으로 그래픽 인터페이스를 나타낸다 (e.g. 웹 인터페이스, 데스크톱 인터페이스)
  • 프로그램의 동작을 촉발하는 모든 동기식 또는 비동기식 수단과 같은 좀 더 광범위한 범주를 포함

서비스 계층 (= 애플리케이션 계층)

  • 계층형 아키텐처 패턴을 확정해서 서비스 계층을 추가할 수 있다
  • 논리적 경계
    • 비즈니스 로직 계층으로의 관문 역할
  • 비즈니스 로직을 오케스트레이션
    • 하부 계층을 조율하는 데 필요한 것들을 감싸서 퍼블릭 인터페이스의 메서드에 상응하는 인터페이스로 노출한다 
  • 비즈니스 로직 패턴에서 외부 조율을 해야 할 경우에만 서비스 계층이 필요하다
    • 트랜잭션 스크립트는 이미 퍼블릭 인터페이스를 통해서 비즈니스 로직이 구성되어 있다
    • 따라서 트랜잭션 스크립트 패턴에서는 서비스 계층 사용이 적절치 않고, 액티브 레코드 패턴에서는 적절하다

비즈니스 로직 계층 (= 도메인 계층 = 모델 계층)

  • 비즈니스 의사결정을 구현하는 계층
  • 액티브 레코드 또는 도메인 모델과 같은 비즈니스 로직 패턴을 이 계층에서 구현

데이터 접근 계층 (= 인프라스트럭처 계층)

  • 시스템의 데이터베이스
  • 영속성 메커니즘에 접근할 수 있는 계층
    • 단순 RDB 외에도 더 포괄적인 내용이 포함된다
    • e.g. NoSQL, 메시지 버스, 외부 API

 

포트와 어댑터 아키텍처 (= 헥사고날 아키텍처, 클린 아키텍처)

  • 계층형 아키텍처의 단점을 해결하고 좀 더 복잡한 비즈니스 로직을 구현하는 데 적합한 패턴
    • 모든 기술적 관심사로부터 비즈니스 로직을 분리하는 것이 목적
    • 도메인 모델 패턴을 사용하여 구현한 비즈니스 로직에 매우 적합하다
  • 프레젠테이션 계층과 데이터 접근 계층을 '인프라스트럭처 계층'으로 통합했다
  • 세부 구조 (위에서부터 고수준)
    • 비즈니스 로직 계층
    • 애플리케이션 계층 (= 서비스 계층 = 유스케이스 계층)
    • 인프라스트럭처 계층 (= 도메인 계층 = 핵심 계층)

의존성 역전 원칙

  • 기존 계층형 아키텍처는 의존성 역전 원칙(DIP)를 위반한다
    • 상위 수준 모듈이 하위 수준 모듈을 의존한다
  • 포트와 어댑터 아키텍처는 의존성 역전 원칙이 반영되었다
    • 비즈니스 로직 계층(상위 수준 모듈)이 인프라스트럭처 계층이 구현할 '포트'를 정의한다 (비즈니스 로직 계층 → 인터페이스 정의)
    • 인프라스트럭처 계층은 '어댑터'를 구현한다 (인프라스트럭처 계층 → 인터페이스 구현)
    • 퍼블릭 인터페이스를 위한 관문으로 애플리케이션 계층이 중간에 추가된다

https://engineering.linecorp.com/ko/blog/port-and-adapter-architecture

 

 

CQRS(command-query responsibility segregation)

  • 포트와 어댑터와 동일하게 비즈니스 로직과 인프라스트럭처 관심사에 기반한다
    • 그러나 포트와 어댑터 아키텍처와 다르게 시스템의 데이터를 관리하는 방법이 다르다
  • CQRS는 시스템 모델의 책임을 분리시키는 패턴이다
    • 도메인 모델 패턴과 이벤트 소싱 도메인 모델에 모두 적합하다
    • 또한, 인프라스트럭처 관점에선 다양한 종류의 데이터베이스의 장점을 활용할 수 있다

커맨드 실행 모델

  • 시스템의 상태를 수정하는 오퍼레이션(시스템 커맨드)을 전담으로 수행하는 단일 모델
  • 비즈니스 로직을 구현하고 규칙을 검사하며 불변성을 강화하는 데 사용됨
  • 강력한 일관성을 데이터를 표현하는 유일한 모델
    • 일관적 상태를 읽을 수 있어야 한다
    • 낙관적 동시성을 지원해야 한다

읽기 모델(프로젝션)

  • 사용자에게 데이터를 보여주거나 다른 시스템에 정보를 제공하기 위해 필요한 만큼 정의하는 모델
  • RDB의 머터리얼라이즈 뷰(materialize view)의 개념과 유사하다
  • 읽기 모델은 읽기 전용이므로 읽기 모델의 데이터를 직접 수정하는 것은 불가능하다

프로젝션의 생성 방식

  • 프로젝션을 생성하는 데엔 동기식 프로젝션과 비동기식 프로젝션이라는 두 가지 방법이 존재한다
  • 동기식 프로젝션
    • 프로젝션 엔진이 마지막에 처리한 체크포인트 이후에 추가되거나 갱신된 레코드를 조회한다
    • 조회된 데이터를 이용해서 읽기 모델을 재생성/갱신한다
    • 다시 처리 체크포인트를 기록한다
    • 정리하자면 상태 변경 -> 읽기 모델 반영 -> 체크 포인트 기록이라는 흐름으로 프로젝션을 생성한다
  • 비동기식 프로젝션
    • 커맨드 실행 모델이 모든 변경사항을 메시지 버스에 발행한다
    • 프로젝션 엔진은 이를 구독하고 읽기 모델에 반영한다
  • 가능하면 동기식 프로젝션 방식을 구현하고, 선택적으로 비동기식 프로젝션 방식을 추가하는 것을 권장한다
    • 비동기식 프로젝션 방식은 확장성과 성능적인 측면에서 유리하지만, 분산 컴퓨팅에서 문제가 발생하기 더 쉽다
    • 메시지의 순서가 꼬이거나 중복 처리가 일어나면 일관성 없는 데이터가 프로젝션 될 수 있다

모델 분리

  • CQRS에서 데이터 조회는 읽기 모델을 통해서만 이뤄지는 것이 아니다
    • 커맨드가 데이터를 반환함으로써 동작에 대한 피드백을 받을 수 있다 (e.g. 실패/성공에 대한 사유)
    • 사용자 입장에서 반환값을 다음 워크플로우에 사용할 수 있다
    • 데이터가 궁극적으로 일관성을 갖는 프로젝션의 경우에는 데이터에 대한 즉각적인 갱신은 기대할 수 없다

 

저작자표시 비영리 (새창열림)

'책 > 도메인 주도 설계 첫걸음' 카테고리의 다른 글

[도메인 주도 설계 첫걸음] Part 4. 다른 방법론 및 패턴과의 관계  (1) 2023.10.09
[도메인 주도 설계 첫걸음] Part 3. 도메인 주도 설계 적용 실무  (0) 2023.10.08
[도메인 주도 설계 첫걸음] Part 1. 비즈니스 도메인 분석하기  (0) 2023.08.27
  1. 5장) 간단한 비즈니스 로직 분석
  2. 트랜잭션 스크립트
  3. 액티브 레코드
  4. 6장) 복잡한 비즈니스 로직 다루기
  5. 도메인 모델
  6. 도메인 모델의 구현
  7. 도메인 모델의 구성요소
  8. 1) 밸류 오브젝트
  9. 2) 엔티티
  10. 3) 애그리게이트
  11. 4) 도메인 서비스
  12. 7장) 시간 차원의 모델링
  13. 이벤트 소싱 도메인 모델 패턴
  14. 이벤트 소싱 애그리게이트의 작업 단계
  15. 이벤트 소싱 도메인 모델 패턴의 장점
  16. 이벤트 소싱 도메인 모델 패턴의 단점
  17. 8장) 아키텍처 패턴
  18. 계층형 아키텍처
  19. 포트와 어댑터 아키텍처 (= 헥사고날 아키텍처, 클린 아키텍처)
  20. 의존성 역전 원칙
  21. CQRS(command-query responsibility segregation)
'책/도메인 주도 설계 첫걸음' 카테고리의 다른 글
  • [도메인 주도 설계 첫걸음] Part 4. 다른 방법론 및 패턴과의 관계
  • [도메인 주도 설계 첫걸음] Part 3. 도메인 주도 설계 적용 실무
  • [도메인 주도 설계 첫걸음] Part 1. 비즈니스 도메인 분석하기
코택
코택
코택
TaxFree
코택
전체
오늘
어제
  • 분류 전체보기 (369)
    • Spring (29)
      • Spring (18)
      • 스프링 핵심 원리 - 고급편 (11)
    • Spring Batch (4)
    • JPA (4)
    • CS (89)
      • 자료구조 (2)
      • 네트워크 (5)
      • 운영체제 (1)
      • 데이터베이스 (4)
      • SQL (7)
      • 알고리즘 이론 (4)
      • 알고리즘 문제 풀이 (66)
    • 웹 (28)
      • React.js (4)
      • Next.js (1)
      • Node.js (14)
      • FastAPI (4)
      • Django (5)
    • 프로그래밍 언어 (45)
      • Python (5)
      • Java + Kotlin (29)
      • JavaScript + TypeScript (11)
    • 테스트코드 (26)
      • ATDD, 클린 코드 with Spring (4)
      • 이규원의 현실 세상의 TDD: 안정감을 주는 코드.. (20)
    • 인프라 (6)
      • AWS (2)
      • Kubernetes (4)
    • 트러블슈팅 (25)
    • 책 (89)
      • Effective Java (54)
      • Effective Kotlin (14)
      • 도메인 주도 개발 시작하기: DDD 핵심 개념 정.. (11)
      • 웹 프로그래머를 위한 데이터베이스를 지탱하는 기술 (6)
      • 도메인 주도 설계 첫걸음 (4)
    • Git (10)
    • 회고 (5)
    • etc (8)

블로그 메뉴

  • 홈
  • 방명록
  • 관리
  • GitHub
  • LinkedIn

공지사항

  • 스킨 관련

인기 글

태그

  • 그래프 탐색
  • fastapi
  • 백준
  • 그래프
  • BOJ
  • Git
  • mysql
  • dp
  • atdd
  • Shortest Path
  • http
  • 깊이 우선 탐색
  • 파이썬
  • 브루트포스
  • 장고

최근 댓글

최근 글

hELLO · Designed By 정상우.
코택
[도메인 주도 설계 첫걸음] Part 2. 전술적 설계
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.