Choreography Saga

Event Sourcing, Domain driven Development 개발 및 CQRS를 지원하는 전문 Framework 중 하나인 Axon을 활용하여 Event driven한 12st Mall 을 구현하는 과정을 실습힌다. 특히, Domain 이벤트에 따라 커맨드를 Orchestration하는 프레임워크 레벨의 코드를 학습합니다.

이벤트스토밍 모델 준비

  • 아래 모델을 새 탭에서 로딩합니다. 모델 링크
  • 브라우져에 모델이 로딩되지 않으면, 우측 상단의 (사람모양) 아바타 아이콘을 클릭하여 깃헙(Github) 계정으로 로그인 후 리로드합니다.

image

  • 로딩된 모델은 우측 팔레트 영역에 스티커 목록이 나타나지 않습니다. 상단 메뉴영역에서 포크 아이콘(FORK)을 클릭해 주어진 모델을 복제합니다.
  • 우측 팔레트 영역에 스티커 목록들이 나타나는 것이 확인됩니다.

Orchestration Saga 시나리오

  • 주문 커맨드에 따라 OrderPlaced 이벤트가 생성됩니다.
  • OrderPlaced 이벤트로 Order Saga 프로세스가 구동됩니다.
  • Saga Process는 배송시작 커맨드를 호출하고 DeliveryStarted 이벤트를 생성합니다.
  • 이어, 재고차감 커맨드가 호출되고 StockDecreased 이벤트를 생성합니다.
  • 최종 주문상태를 업데이트하는 절차로 Saga Process는 종료됩니다.
  • 각 커맨드 호출시, 오류가 발생하면 이의 처리를 위해 Compensation Logic이 각각 실행됩니다.

Saga Modeling

  • 모델 상단의 Fork 메뉴를 클릭합니다.
  • 복제돤 모델을 다음과 같이 오케스트레이션 합니다.

Process Orchestration

오케스트레이션 Saga 모델링 규칙은 다음과 같습니다.

  1. 스티커를 선으로 연결합니다.
  2. 매핑 릴레이션을 클릭하여, 레이블(실행 순서)을 부여합니다.
  3. 레이블 중, '가 붙은 프로세스는 보상처리(Compensation Trx) 프로세스 입니다.
  • OrderPlaced > Order Saga, (1.start)
  • Order Saga > start delivery, (2)
  • DeliveryStarted > Order Saga, (3)
  • Order Saga > decrease stock, (4)
  • StockDecreased > Order Saga, (5)
  • Order Saga > update status, (6)
  • OrderCompleted > Order Saga, (7.end)
  • Order Saga > order cancel, (2')
  • Order Saga > cancel delivery, (4')

오케스트레이션 Saga 모델링 결과

image

Code 생성 및 내 Git Repository에 Push

  • Code Preview > Git 아이콘을 눌러 내 Repository에 Push합니다.

image

image

  • GitPod 환경에서 로딩합니다.

image

Axon Server 확인

infra > docker-compose.yml

  • Axon Server는 대시보드를 위한 8024, 메시지 gRPC를 위한 8124 포트를 사용합니다.
  • 각 서비스들의 Offset Token 관리를 위한 Token Store(MySQL)가 Lab 실행시 생성됩니다.

Orchestration Saga Code Completion

Product Service

상품 Domain 코드에 Biz 로직을 완성합니다.

  • 재고 부족시 도메인 오류 발생코드를 추가합니다.
# ProductAggregate - @DecreaseStockCommand 재고부족시 Exception 발생 
	if(this.getStock() < command.getStock()) throw new IllegalStateException("Out of Stock. !");  // 코드추가
  • product > ProductAggregate.java 내 다음 코드를 추가합니다.
# @EventSourcingHandler :: StockIncreasedEvent : 
    setStock(getStock() + event.getStock());  // 코드추가

#  @EventSourcingHandler :: StockDecreasedEvent : 
    setStock(getStock() - event.getStock());  // 코드추가

Order Service

주문 Domain 코드에 Biz 로직을 완성합니다.

  • order > OrderAggregate.java 내 다음 코드를 추가합니다.
# @EventSourcingHandler :: OrderPlacedEvent :
     setStatus("OrderPlaced"); 			// 코드추가
# @EventSourcingHandler :: OrderCompletedEvent : 
     setStatus("OrderCompleted"); 		// 코드추가
# @EventSourcingHandler :: OrderCancelledEvent :      
      setStatus("OrderCancelled");		// 코드추가

Delivery Service

배송 Domain 코드에 Biz 로직을 완성합니다.

  • delivery > DeliveryAggregate.java 내 다음 코드를 추가합니다.
# @EventSourcingHandler :: DeliveryStartedEvent :
      setStatus("DeliveryStarted"); 		// 코드추가
# @EventSourcingHandler :: DeliveryCancelledEvent : 
      setStatus("DeliveryCancelled"); 		// 코드추가

OrderSaga

주문 오케스트레이션을 수행하도록 OrderSaga 코드를 완성합니다.

1. Saga Start

  • OrderPlaced 이벤트로부터 Correlation key 설정 : 24 라인
@SagaEventHandler(associationProperty = "orderId")

2. 배송시작 Command 생성 및 호출 : 27라인

command.setOrderId(event.getOrderId());
command.setProductId(event.getProductId());
command.setQty(event.getQty());
command.setUserId(event.getUserId());

# 배송실패시, 주문취소 Compensation 처리 : OrderCancelCommand
orderCancelCommand.setOrderId(event.getOrderId());  	// 코드추가

3. DeliveryStartedEvent 이벤트로부터 Correlation key 설정

@SagaEventHandler(associationProperty = "orderId")

4. 재고차감 Command 생성 및 호출

command.setProductId(event.getProductId());
command.setStock(event.getQty());	
command.setOrderId(event.getOrderId());

# 재고차감 실패시, 배송취소 Compensation 처리 : CancelDeliveryCommand
cancelDeliveryCommand.setDeliveryId(event.getDeliveryId());	// 코드추가

5. StockDecreasedEvent 이벤트로부터 Correlation key 설정

@SagaEventHandler(associationProperty = "orderId")

6. 주문완료 Command 생성 및 호출

command.setOrderId(event.getOrderId());

7. Saga End

  • OrderCompletedEvent 이벤트로부터 Correlation key 설정
@SagaEventHandler(associationProperty = "orderId")
  • Saga Process 종료

12st Mall 테스트

  • Rest API를 활용해 생성된 Axon Saga 기반 쇼핑몰을 테스트 합니다.
  • 먼저 Common API를 빌드합니다.
cd common-api
mvn clean install
  • 각 마이크로 서비스를 실행합니다.
# new terminal
cd order
mvn clean spring-boot:run

# new terminal
cd product
mvn clean spring-boot:run

# new terminal
cd delivery
mvn clean spring-boot:run
  • Product 서비스(:8082)에 테스트용 상품을 등록합니다.
# new terminal
http POST :8082/products productName=TV stock=100
  • 등록된 상품 Id를 복사해 둡니다.

image

  • 복사한 상품 Id로 10개의 TV를 구매하는 주문을 생성합니다.
http POST :8081/orders productId=[상품 Id] productName=TV qty=10 userId=1001
  • 생성된 주문 Id를 복사해 둡니다.

image

12st Mall Saga Compensation 검증

주문 생성

  • 상품 Id로 100개의 TV를 구매하는 주문을 생성하고 주문번호를 복사해둡니다.
http POST :8081/orders productId=[상품 Id] productName=TV qty=100 userId=1001
  • 재고 개수(90)보다 많은 주문으로 상품 서비스에서 오류를 리턴합니다.
  • 주문 번호로 생성된 주문의 최종 상태와 이벤트 이력을 조회해 봅니다.
http GET :8081/orders/[주문번호]
http GET :8081/orders/[주문번호]/events
  • 배송서비스의 배송 상태를 조회해 봅니다.
http GET :8083/deliveries

[확장 미션] - 주문서비스 Debugging

  • 주문서비스를 종료합니다.
  • OrderSaga 에 SagaEventHandler별로 Break Points를 추가합니다.
  • 주문서비스를 디버그 모드로 실행합니다.
  • 새로운 주문을 생성한 다음 Debug Point를 확인하면서 Orchestration 흐름을 식별합니다.
  • 재고보다 많은 수량을 넣어 Debug Point를 확인합니다.
uEngine has registered trademarks and uses trademarks. and MSA-Ez is licensed under the GNU General Public License version 3.0.