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 인 것이죠.
- 비즈니스 Domain 별로 나누어 설계한 아키텍처 디자인 패턴입니다.
- Aggregate 란?
- 흔히 DDD 에서 사용되는 패턴으로, 고유의 비즈니스 목적 수행을 위한 데이터 객체들의 집합입니다(여기서는 Order, Customer, Product가 피자주문이라는 도메인에서 사용되는 Aggregate 입니다). 하나의 도메인은 여러개의 Aggregate 를 가질 수 있어요. reference : https://www.infoq.com/articles/microservices-aggregates-events-cqrs-part-1-richardson/
- 이 패턴은 도메인에서 필요한 엔티티들을 모아서 비즈니스 목적을 수행 하는 루트 엔티티에 바인딩 하는 패턴이죠.
예를 들어 주문서비스(도메인)에서 신규주문(비즈니스 로직)을 수행 하기 위해서는, 아래와 같은 엔티티+Value 들이 필요해요.
- Order(루트 엔티티 or aggregate root)
- 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 의 부하를 따로 관리할 수 있겠죠?
지금 까지 배운 것들을 정리해볼까요?
- DDD는 기존 모노 서버를 도메인 별로 나눕니다(OrderService/CustomerService/ProductService).
- 나뉜 도메인들은 Aggregate 를 각자 가지고 있으며, 그중 Aggregate-Root(Order/Customer/Product) 를 통해서만 기타 엔티티(OrderLineItem/DeliveryInfo/PaymentInfo) 들을 수정합니다.
- Aggregate 내 엔티티가 다른 Aggregate 의 어떤 엔티티들을 참조할 때, 해당 엔티티의 PK를 사용해서 참조합니다.
즉, 뭔가 Aggregate 는 트랜젝션을 따로 저장해서 관리하는 느낌이죠? 이러한 이벤트 형식의 트랜젝션 전파를 위해, 저는 아래와 같이 두 가지 버전의 도식도를 나타내 보았습니다.
트랜젝션 롤백 시 Single Aggregate 형태
multi-aggregate 형태는 이벤트로 도착하는 모든 aggregate 를 저장
트랜젝션 롤백이 필요없는 기능의 경우, 자체 재전송(멱등성 X)
지금 드는 생각으로는, 위의 그림은 멱등성이 고려되지 않았던 것 같아요. 따라서 Transaction ID를 추가적인 Kafka 토픽 스키마로 설정해주어야 합니다.