Skip to main content Link Menu Expand (external link) Document Search Copy Copied

created at 2022-03-22

저는 MQ를 사용하긴 하지만, 보상 트랜젝션 설정이 현재는 체계적으로 되어있지 않습니다. 따라서 MSA의 SAGA pattern 중 Choreography 형태를 체계적으로 구현하기 이전, DDD에 대한 개념이 부족한 것 같아서 따로 정리하려고 합니다.

DDD(Domain-Driven Design)란?

  • Domain 이란 ?
    • 사전적의미는 ‘영역’, ‘집합’입니다.
    • 즉 Domain은 유사한 업무의 집합입니다. ex) 피자주문(도메인) = 주문 + 고객 + 상품 + …
    • DDD에서 말하는 Domain은 비즈니스 Domain입니다.
  • DDD 란?
    • 비즈니스 Domain 별로 나누어 설계한 아키텍처 디자인 패턴입니다.

      예를 들어 큰 하나의 도메인(피자주문 = 주문 + 고객 + 상품 + …)을 잘게 쪼갠 디자인이에요. 바로 이 패턴에 맞추어 잘게 쪼갠 서비스들을 서로 연결시켜주는 패턴이 Micro Service Architecture 인 것이죠.

  • Aggregate 란?
    • 흔히 DDD 에서 사용되는 패턴으로, 고유의 비즈니스 목적 수행을 위한 데이터 객체들의 집합입니다(여기서는 Order, Customer, Product가 피자주문이라는 도메인에서 사용되는 Aggregate 입니다). 하나의 도메인은 여러개의 Aggregate 를 가질 수 있어요. img reference : https://www.infoq.com/articles/microservices-aggregates-events-cqrs-part-1-richardson/
    • 이 패턴은 도메인에서 필요한 엔티티들을 모아서 비즈니스 목적을 수행 하는 루트 엔티티에 바인딩 하는 패턴이죠.

      예를 들어 주문서비스(도메인)에서 신규주문(비즈니스 로직)을 수행 하기 위해서는, 아래와 같은 엔티티+Value 들이 필요해요.

      1. Order(루트 엔티티 or aggregate root)
      2. OrderLineItem/DeliveryInfo/PaymentInfo(기타 엔티티)

즉 Aggregate 는 비즈니스 로직이 하나의 유닛으로 돌아가기 위해 설계된 비즈니스 로직 엔티티입니다. 따라서 Aggregate 를 설계하기 위해서는 3가지 규칙이 존재하는데요. 이를 RPO 라고 합니다.

  • Root only-Aggregate Root : Aggregate 내부의 entity 접근 시, Aggregate Root를 통해서만 해당 내부 엔티티 변경 가능.
  • Primary key 사용 : 다른 Aggregate 를 참조할때 객체자체를 레퍼런스하지 말고 객체의 primary key로 참조.

    예를 들어, Order(Aggregate-root)는 Consumer(Aggregate-root) 객체 자체를 참조하지 않고, consumerId 값으로 Consumer aggregate를 참조합니다. root가 아니더라도 마찬가지에요.

  • One to One : 한 개의 트랜잭션은 한 개의 Aggregate만 Writing.

보시면 DDD 는 Aggregate 를 사용함으로써 CQRS 를 따르도록 설계되어있습니다.

  • CQRS 란?

CQRS 는 읽기와 쓰기의 부하를 분산시키기 위한 룰로써, Command and Query Responsibility Segregation(명령과 조회의 책임 분리)을 나타냅니다. 즉, 데이터 베이스로부터의 읽기와 업데이트 작업을 분리하는 패턴을 말하죠. 쓰기와 읽기를 함께 사용한다면, Lock 이 중복되기 때문에 꽤 많은 부하가 걸리거든요. Aggregate 는 CQRS 룰과 부합하는데요. 예를 들어 Order(Aggregate-root=COMMAND)를 통해서만 WRITE 할 수 있습니다. OrderLineItem/DeliveryInfo/PaymentInfo(기타 엔티티=QUERY) 들은 READ 만 가능하죠. 이렇게 되면, READ 의 부하를 따로 관리할 수 있겠죠?

지금 까지 배운 것들을 정리해볼까요?

img

  1. DDD는 기존 모노 서버를 도메인 별로 나눕니다(OrderService/CustomerService/ProductService).
  2. 나뉜 도메인들은 Aggregate 를 각자 가지고 있으며, 그중 Aggregate-Root(Order/Customer/Product) 를 통해서만 기타 엔티티(OrderLineItem/DeliveryInfo/PaymentInfo) 들을 수정합니다.
  3. Aggregate 내 엔티티가 다른 Aggregate 의 어떤 엔티티들을 참조할 때, 해당 엔티티의 PK를 사용해서 참조합니다.

즉, 뭔가 Aggregate 는 트랜젝션을 따로 저장해서 관리하는 느낌이죠? 이러한 이벤트 형식의 트랜젝션 전파를 위해, 저는 아래와 같이 두 가지 버전의 도식도를 나타내 보았습니다.

트랜젝션 롤백 시 Single Aggregate 형태

img

multi-aggregate 형태는 이벤트로 도착하는 모든 aggregate 를 저장

트랜젝션 롤백이 필요없는 기능의 경우, 자체 재전송(멱등성 X)

img

지금 드는 생각으로는, 위의 그림은 멱등성이 고려되지 않았던 것 같아요. 따라서 Transaction ID를 추가적인 Kafka 토픽 스키마로 설정해주어야 합니다.