전자책 출간 알림 [마이크로서비스패턴 쉽게 개발하기]

티스토리 뷰

이번 장에서는 이미 개발된 주문 서비스를 테스트 하면서 대략의 전체 흐름을 이해해 보겠습니다. 

먼저 GitHub에 있는 소스를 IntelliJ 에 Import 합니다. 

Import Application

1) Fork Application 

아래 GuiHub Repository에 접근하여 자신의 Repository로 Fork합니다. 

https://github.com/happykubepia/agilemall-axon

 

2) Git Clone 

자신의 PC에 소스를 다운로드 합니다. 

이를 위해서는 Git Client가 PC에 설치되어야 합니다. 

아래 글을 참고하여 설치 합니다. 

https://git-scm.com/book/ko/v2/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Git-%EC%84%A4%EC%B9%98

설치 후 작업 디렉토리를 먼저 만듭니다. 

- Mac 
mkdir -p ~/workspace
- Windows
cd %userprofile%
mkdir workspace

작업 디렉토리로 이동 후 Git clone 합니다. Github repository주소는 Fork한 본인 걸로 바꾸세요. 

- 작업 디렉토리로 이동
Mac: cd ~/workspace
Win: cd %userprofile%/workspace 
- 소스 다운로드: repository 주소는 본인걸로 바꾸십시오. 
git clone https://github.com/happykubepia/agilemall-axon

 

3) Import Project

IntelliJ를 실행 하고 [Open]을 클릭 합니다. 

아래와 같이 나오면 잘 Import 된 겁니다. 

 

4) Run/Debug Configurations 만들기 

IntelliJ IDEA Ultimate를 사용하면 자동으로 실행 Configuration을 만들어 주나 CE버전은 Spring Boot를 지원하지 않아 수동으로 만들어야 합니다. 

- 메인메뉴의 [Run] > [Edit Configurations]를 클릭 합니다. 

-  왼쪽 위의 '+' 아이콘을 클릭하고 'Grade'을 선택 합니다. 

 

- Name과 Run 항목을 변경 합니다. 

Name에는 서비스의 이름을 지정하고 Run은 아래 그림과 같이 ':{서비스명}:bootRum'이라고 입력 합니다. 

하단에 [Apply]를 누르면 저장이 됩니다. 

  - 동일한 방식으로 나머지 4개 서비스의 실행 프로파일을 만듭니다. 

마이크로서비스 Name Run
주문 order :order:bootRun
결제 payment :payment:bootRun
배송 delivery :delivery:bootRun
재고 inventory :inventory:bootRun
레포트 report :report:bootRun

 

- 좌측 하단의 'Service'탭을 누릅니다. 아래 그림대로 'Run Configuration Type'을 클릭 후 'Gradle'을 선택 합니다. 

- 아래와 같이 서비스 리스트가 나오게 됩니다. 


서비스 실행 및 재고 정보 등록 하기 

1) 서비스 실행 

'Service'탭에서 실행할 서비스를 선택한 후 우측 상단의 'Run'아이콘을 클릭 합니다. 

또는 여러 서비스를 한꺼번에 선택하고 실행할 수도 있습니다. 

 

2) 서비스 포트 확인

각 서비스의 콘솔 메시지에서 'Tomcat started on port {포트번호}' 로그를 찾아서 각 서비스의 포트를 확인 합니다. 

################################################################################################
## You have not configured AxonIQ Console. AxonIQ Console provides out-of-the box monitoring  ##
## and management capabilities for your Axon Application, starting with it is free.           ##
## Visit https://console.axoniq.io for more information!                                      ##
## Suppress this message by setting system property disable-axoniq-console-message to true.   ##
################################################################################################

 01/17 15:29:13 - WARN --- JpaBaseConfiguration$JpaWebConfiguration spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
 01/17 15:29:13 - {생략}  Tomcat started on port 18080 (http) with context path ''

각 서비스 포트는 order-18080, inventory-18081, payment-18082, delivery-18083, report-18084 입니다. 

 

3) Swagger Page 열기 

각 서비스의 API를 테스트 할 수 있는 swagger page를 웹브라우저에서 열어 봅니다. 

참고) Swagger Page란 ?
Backend 개발자와 Frontend 개발자간 API에 대한 명확한 소통을 위해 API 명세를 확인하고 테스트까지 할 수 있는 웹 페이지입니다.

Java가 Java EE(Enterprise Edition)에서 Jakarta EE(Enterprise Edition)로 발전하면서 Swagger 표준도 바뀌었습니다. 
기존에는 io.springfox Library를 사용했는데 springfox에서 Jakarta EE를 지원 못하기 때문에
현재는 springdoc의 openapi를 사용합니다.  springdoc openapi 사용을 위해서는 아래 dependency를 추가해야 합니다.  
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.3.0'

주소는 http://localhost:{포트번호}//swagger-ui/index.html 

 

4) 재고정보 등록

Swagger page를 이용하여 테스트에 사용할 재고 정보를 등록 합니다. 

inventory 서비스의 swagger page를 엽니다.

http://localhost:18081/swagger-ui/index.html

 

'신규 제품 등록' API를 펼치고 [Try it out]버튼을 누릅니다. 

그리고 아래 예시와 같이 제품ID, 제품명, 단가, 재고량을 입력한 후 [Execute]버튼을 누릅니다. 

같은 방법으로 원하는 만큼 제품 정보를 등록 합니다. 2~3개 정도 등록하면 충분 합니다. 

DBeaver에서 제대로 등록되었는지 확인 합니다. 

SQL문장을 입력하고 왼쪽 실행 아이콘을 누르거나 CTRL-ENTER를 눌러 실행 합니다. 

 


신규 주문 테스트

새로운 주문이 들어 왔을 때 Saga패턴을 이용하여 각 서비스 DB 정합성을 맞추는 걸 테스트 해 보겠습니다. 

1) 프로세스 이해

전체적인 큰 흐름은 아래와 같습니다. 

- STEP1: order서비스에 주문 데이터 생성하고 생성 완료 Event를 Axon서버에 발송  

- STEP2: 결제 데이터 생성
  - 주문 생성 Saga가 주문 생성 Event를 Push 받은 후 결제데이터 생성 요청 Command를 Axon서버에 발송
  - payment 서비스가 결제 데이터를 생성하고 생성 완료 Event를 Axon 서버에 발송 

-  STEP3: 배송 데이터 생성
    - 주문 생성 Saga가 결제 생성 Event를 Push 받은 후 배송 데이터 생성 요청 Command를 Axon서버에 발송 
    - delivery 서비스가 배송 데이터를 생성하고 생성 완료 Event를 Axon 서버에 발송

- STEP4: 주문완료 
   - 주문 생성 Saga가 배송 생성 Event를 Puh 받은 후 주문 완료 요청 Command를 Axon 서버에 발송
   - order 서비스가 STEP1에서 생성한 주문 데이터를 찾아 주문 상태를 '완료'로 변경

- STEP5: 레포트 생성
   - 주문 생성 Saga가 조회 레포트 생성 요청 Command를 Axon 서버에 발송
   - report 서비스가 레포트 데이터를 생성하고 생성 완료 Event를 Axon 서버에 발송  

2) 테스트 

order 서비스의 swagger page를 웹브라우저에서 엽니다. 

http://localhost:18080/swagger-ui/index.html 

'신규 상품 주문 API'의 입력 데이터를 아래 예제를 참고하여 입력 합니다. 

userId는 아무거나 입력하면 되고, productId는 위에서 입력한 제품ID와 일치해야 합니다. 

paymentKind는 '10'은 카드이고, '20'은 포인트 입니다. paymentRate는 결제 수단별 지불 비율입니다. 

{
  "userId": "hiondal",
  "orderReqDetails": [
    {
      "productId": "PROD_01",
      "qty": 5
    }
  ],
  "paymentReqDetails": [
    {
      "paymentKind": "10",
      "paymentRate": 1
    }
  ]
}

아래와 같이 주문제품과 결제수단을 복수로 입력할 수도 있습니다. 

{
   "userId": "hiondal",
    "orderReqDetails": [
      {
        "productId": "PROD_01",
        "qty": 10
      },
      {
        "productId": "PROD_02",
        "qty": 5
      },
      {
        "productId": "PROD_03",
        "qty": 15
      }
    ],
    "paymentReqDetails": [
      {
        "paymentKind": "10",
        "paymentRate": 0.8
      },
      {
        "paymentKind": "20",
        "paymentRate": 0.2
      }
    ]
}

데이터를 입력한 후 [Execute]를 클릭합니다. 

IntelliJ에서 order 서비스의 console을 확인 합니다. 

각 로그는 수행일시, 로그종류(INFO), 수행class, 로그로 구성되어 있습니다. 

각 로그의 상세 내용은 이후에 개발할 때 다시 설명하니까 대략의 흐름만 파악하십시오. 

 01/17 17:25:19 - INFO --- c.agilemall.order.service.OrderService   ===== [Create Order] START Transaction =====
 01/17 17:25:19 - INFO --- c.agilemall.order.service.OrderService   ===== [Create Order] #1: <isValidInventory> =====
 01/17 17:25:19 - INFO --- c.agilemall.order.service.OrderService   [OrderService] Executing getInventory
 01/17 17:25:19 - INFO --- c.agilemall.order.service.OrderService   [OrderService] Executing <isValidInventory>
 01/17 17:25:19 - INFO --- c.agilemall.order.service.OrderService   [OrderService] Executing <getUnitPrice>
 01/17 17:25:19 - INFO --- c.agilemall.order.service.OrderService   ===== [Create Order] #2: <CreateOrderCommand> =====
 01/17 17:25:19 - INFO --- c.a.order.aggregate.OrderAggregate       [@CommandHandler] Executing <CreateOrderCommand> for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CreatedOrderEvent> for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.controller.OrderController     [@PostMapping] Executing createOrder is Finished
 01/17 17:25:19 - INFO --- c.a.order.events.OrderEventsHandler      [@EventHandler] Handle <CreatedOrderEvent> for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.saga.OrderCreatingSaga         [Saga] <CreatedOrderEvent> is received for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.saga.OrderCreatingSaga         ===== [Create Order] #3: <CreatePaymentCommand> =====
 01/17 17:25:19 - INFO --- c.a.order.saga.OrderCreatingSaga         [Saga] <CreatedPaymentEvent> is received for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.saga.OrderCreatingSaga         ===== [Create Order] #4: <CreateDeliveryCommand> =====
 01/17 17:25:19 - INFO --- c.a.order.saga.OrderCreatingSaga         [Saga] <CreatedDeliveryEvent> is received for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.saga.OrderCreatingSaga         ===== [Create Order] #5: <CompleteOrderCreateCommand> =====
 01/17 17:25:19 - INFO --- c.a.order.aggregate.OrderAggregate       [@CommandHandler] Executing <CompleteOrderCreateCommand> for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CompletedCreateOrderEvent> for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.events.OrderEventsHandler      [@EventHandler] Executing on <CompletedCreateOrderEvent> for Order Id:ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.saga.OrderCreatingSaga         [Saga] [CompletedCreateOrderEvent] is received for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.saga.OrderCreatingSaga         ===== [Create Order] Transaction is FINISHED =====
 01/17 17:25:19 - INFO --- c.a.order.service.CompensatingService    ===== START Updating Report =====
 01/17 17:25:19 - INFO --- c.a.order.queries.OrderQueryHandler      [@QueryHandler] Handle <ReportQuery> for Order Id: ORDER_076643895
 01/17 17:25:19 - INFO --- c.a.order.service.CompensatingService    ===== END Updating Report =====

 

3) 데이터 확인

DBeaver에서 테이블에 입력된 데이터를 확인 해 봅니다. 

SQL편집기에서 아래 SQL을 입력하여 데이터가 정확히 생성되었는지 확인합니다. 

select * from orderDB.orders order by order_id;
select * from orderDB.order_detail order by order_id;
select * from paymentDB.payment order by payment_id;
select * from paymentDB.payment_detail order by payment_id;
select * from deliveryDB.shipping order by delivery_id;
select * from reportDB.report order by order_datetime;

 


주문 수정 테스트

등록한 주문 데이터를 수정해 보겠습니다. 

1) 프로세스 이해

전체적인 흐름은 아래와 같습니다. 

- STEP1: order서비스 주문 데이터를 변경하고 수정 완료 Event를 Axon서버에 발송 

- STEP2: 결제 데이터 수정
  - 주문 수정 Saga가 주문 수정 Event를 Push 받은 후 결제데이터 수정 요청 Command를 Axon서버에 발송
  - payment 서비스가 결제 데이터를 수정하고 수정 완료 Event를 Axon 서버에 발송 

- STEP3: 주문수정 완료 
   - 주문 수정 Saga가 결제 수정 완료 Event를 Puh 받은 후 주문 수정 완료 요청 Command를 Axon 서버에 발송
   - order 서비스가 STEP1에서 수정한 주문 데이터를 찾아 주문 상태를 '완료'로 변경

- STEP4: 레포트 수정
   - 주문 수정 Saga가 조회 레포트 수정 요청 Command를 Axon 서버에 발송
   - report 서비스가 레포트 데이터를 수정하고 수정 완료 Event를 Axon 서버에 발송  

2) 테스트

order swagger page의 '주문 수정 API'의 입력 데이터를 아래 예시를 참고하여 입력 합니다. 

orderId는 신규 상품 주문 API의 실행 결과나 DBeaver에서 찾아 기존 생성된 주문ID를 입력합니다. 

productId도 그 주문 시 사용했던 제품ID를 입력해야 합니다. 기존에 여러 제품을 주문 했다면 수정할 제품 정보만 입력하면 됩니다.

결제 상세 정보의 paymentKind는 10번(카드) 또는 20번(포인트)이어야 하며, paymentRate의 총합은 '1'이어야 합니다.   

{
  "orderId": "ORDER_076643895",
  "orderReqDetails": [
    {
      "productId": "PROD_01",
      "qty": 10
    }
  ],
  "paymentReqDetails": [
    {
      "paymentKind": "10",
      "paymentRate": 1
    }
  ]
}

order 서비스의 console에서 대략적인 흐름을 로그로 확인 합니다. 

01/17 18:00:21 - INFO --- c.a.order.controller.OrderController     [@PutMapping '/orders'] Executing updateOrder: OrderReqUpdateDTO(orderId=ORDER_076643895, orderReqDetails=[OrderReqDetailDTO(productId=PROD_01, qty=10)], paymentReqDetails=[PaymentReqDetailDTO(paymentKind=10, paymentRate=1.0)])
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   [OrderService] Executing <updateOrder>: OrderReqUpdateDTO(orderId=ORDER_076643895, orderReqDetails=[OrderReqDetailDTO(productId=PROD_01, qty=10)], paymentReqDetails=[PaymentReqDetailDTO(paymentKind=10, paymentRate=1.0)])
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   ===== [Update Order] START Transaction =====
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   Executing <validateOrderUpdatableByProduct> for Order Id:ORDER_076643895
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   ===== [Update Order] #1: <validateOrderUpdatableByDeliveryStatus> =====
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   Executing <validateOrderUpdatableByDeliveryStatus> for Order Id:ORDER_076643895
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   ===== [Update Order] #2: <isValidInventory> =====
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   [OrderService] Executing getInventory
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   [OrderService] Executing <isValidInventory>
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   ===== [Update Order] #3: <UpdateOrderCommand> =====
 01/17 18:00:21 - INFO --- c.agilemall.order.service.OrderService   [OrderService] Executing <getUnitPrice>
 01/17 18:00:22 - WARN --- o.s.c.annotation.AnnotationTypeMapping   Support for convention-based annotation attribute overrides is deprecated and will be removed in Spring Framework 6.2. Please annotate the following attributes in @org.axonframework.modelling.command.AggregateIdentifier with appropriate @AliasFor declarations: [routingKey]
 01/17 18:00:22 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CreatedOrderEvent> for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CompletedCreateOrderEvent> for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.aggregate.OrderAggregate       [@CommandHandler] Executing <UpdateOrderCommand> for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <UpdatedOrderEvent> for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.events.OrderEventsHandler      [@EventHandler] Executing <UpdatedOrderEvent> for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.saga.OrderUpdatingSaga         [Saga] UpdatedOrderEvent is received for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.saga.OrderUpdatingSaga         ===== [Update Order] #4: <UpdatePaymentCommand> =====
 01/17 18:00:22 - INFO --- c.a.order.saga.OrderUpdatingSaga         [Saga] UpdatedPaymentEvent is received for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.saga.OrderUpdatingSaga         ===== [Update Order] #5: <CompletedOrderUpdateCommand> =====
 01/17 18:00:22 - INFO --- c.a.order.aggregate.OrderAggregate       [@CommandHandler] Executing <CompleteUpdateOrderCommand> for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CompletedUpdateOrderEvent> for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.saga.OrderUpdatingSaga         [Saga] [CompletedUpdateOrderEvent] is finished for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.saga.OrderUpdatingSaga         ===== [Updating Order] Transaction is Finished =====
 01/17 18:00:22 - INFO --- c.a.order.service.CompensatingService    ===== START Updating Report =====
 01/17 18:00:22 - INFO --- c.a.order.queries.OrderQueryHandler      [@QueryHandler] Handle <ReportQuery> for Order Id: ORDER_076643895
 01/17 18:00:22 - INFO --- c.a.order.service.CompensatingService    ===== END Updating Report =====

 

이 로그에서 우리가 중요하게 이해할게 있습니다.  Event Sourcing패턴과 CQRS패턴이 적용 되었다는 걸 확인할 수 있다는 겁니다. 

... c.agilemall.order.service.OrderService   ===== [Update Order] #3: <UpdateOrderCommand> =====
...
... c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CreatedOrderEvent> for Order Id: ORDER_076643895
... c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CompletedCreateOrderEvent> for Order Id: ORDER_076643895
... c.a.order.aggregate.OrderAggregate       [@CommandHandler] Executing <UpdateOrderCommand> for Order Id: ORDER_076643895
... c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <UpdatedOrderEvent> for Order Id: ORDER_076643895
... c.a.order.events.OrderEventsHandler      [@EventHandler] Executing <UpdatedOrderEvent> for Order Id: ORDER_076643895
...
... c.a.order.saga.OrderUpdatingSaga         ===== [Update Order] #5: <CompletedOrderUpdateCommand> =====
... c.a.order.aggregate.OrderAggregate       [@CommandHandler] Executing <CompleteUpdateOrderCommand> for Order Id: ORDER_076643895
... c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CompletedUpdateOrderEvent> for Order Id: ORDER_076643895
... c.a.order.events.OrderEventsHandler      [@EventHandler] Executing <CompletedUpdateOrderEvent> for Order Id: ORDER_076643895

- Event Sourcing 

OrderAggregate에서 <UpdatedOrderEvent>를 처리하기 전에 Event Replay를 통해 최종 데이터 상태를 계산하고 있습니다. 

Event Data State
<CreatedOrderEvent> 신규 주문 데이터. 주문상태 필드값은 'CREATED' 
<CompletedCreateOrderEvent> 신규 주문 완료 처리된 데이터. 주문상태 필드값은 'COMPLETED' 
테이블 'orders'와 'order_detail'에 저장된 값을 얻음

그 이후 Axon Server에서 push 받은 <UpdatedOrderEvent>, <CompletedUpdateOrderEvent>를 처리합니다.

Event Data State
<UpdatedOrderEvent> 주문 데이터를 수정. 주문상태 필드값은 'UPDATED'
<CompletedUpdateOrderEvent> 주문 수정 완료 처리된 데이터. 주문상태 필드값은 'COMPLETED'

 

- CQRS

EventHandler에서 <UpdatedOrderEvent>와 <CompletedUpdateOrderEvent>를 Push 받아 orderDB의 orders와 order_detail 테이블에 있는 데이터를 업데이트 합니다. 이때 orderDB는 조회를 위한 DB입니다. 

즉, Data Write 처리는 OrderAggregate에서 Event Sourcing 패턴으로 처리하고, Data Read를 위한 처리는 EventHandler에서 하는 것입니다.  이렇게 CQRS 패턴도 같이 적용 되고 있습니다. 

...
... c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <UpdatedOrderEvent> for Order Id: ORDER_076643895
... c.a.order.events.OrderEventsHandler      [@EventHandler] Executing <UpdatedOrderEvent> for Order Id: ORDER_076643895
...
... c.a.order.saga.OrderUpdatingSaga         ===== [Update Order] #5: <CompletedOrderUpdateCommand> =====
...
... c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CompletedUpdateOrderEvent> for Order Id: ORDER_076643895
... c.a.order.events.OrderEventsHandler      [@EventHandler] Executing <CompletedUpdateOrderEvent> for Order Id: ORDER_076643895

 

3) 데이터 확인

DBeaver의 SQL편집기에서 SQL문으로 데이터가 수정된 것을 확인 합니다. 


주문 삭제 테스트

마지막으로 주문 삭제 테스트를 합니다. 

1) 프로세스 이해 

전체적인 흐름은 아래 그림과 같습니다. 

- STEP1: order서비스 주문 데이터의 주문상태를 '주문취소'로 변경하고 삭제 완료 Event를 Axon서버에 발송

- STEP2: 결제 데이터 삭제
  - 주문 삭제 Saga가 주문 삭제 완료 Event를 Push 받은 후 결제 데이터 삭제 요청 Command를 Axon서버에 발송
  - payment 서비스가 결제 데이터를 삭제하고 삭제 완료 Event를 Axon 서버에 발송 

- STEP3: 배송 데이터 삭제
  - 주문 삭제 Saga가 결제 삭제 완료 Event를 Push 받은 후 배송 데이터 삭제 요청 Command를 Axon서버에 발송
  - delivery 서비스가 배송 데이터를 삭제하고 삭제 완료 Event를 Axon 서버에 발송

- STEP4: 레포트 데이터 삭제
  - 주문 삭제 Saga가 배송 데이터 삭제 완료 Event를 Push 받은 후 레포트 데이터 삭제 요청 Command를 Axon서버에 발송
  - report 서비스가 레포트 데이터를 삭제하고 삭제 완료 Event를 Axon 서버에 발송

- STEP5: 주문삭제 최종 완료 
   - 주문 삭제 Saga가 레포트 삭제 완료 Event를 Puh 받은 후 주문 삭제 최종 완료 요청 Command를 Axon 서버에 발송
   - order 서비스가 STEP1에서 배송상태만 수정한 주문 데이터를 찾아 삭제함

2) 테스트

swagger 페이지에서 주문 취소 API에 삭제하려는 주문ID입력하고 [Execute]버튼을 클릭하세요. 

서버 콘솔 로그를 보시고 대략의 처리 흐름을 이해해 보시기 바랍니다. 

01/17 22:10:37 - INFO --- c.agilemall.order.service.OrderService   ===== [Delete Order] START Transaction =====
01/17 22:10:37 - INFO --- c.agilemall.order.service.OrderService   ===== [Delete Order] #1: <validateOrderDeletableByDeliveryStatus> =====
01/17 22:10:37 - INFO --- c.agilemall.order.service.OrderService   Executing <validateOrderDeletableByDeliveryStatus> for Order Id:ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <DeleteOrderCommand> for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.events.OrderEventsHandler      [@EventHandler] Executing <DeletedOrderEvent> for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         [Saga] DeletedOrderEvent is received for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         ===== [Delete Order] #2: <DeletePaymentCommand> =====
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         [Saga] <DeletedPaymentEvent> is received for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         ===== [Delete Order] #3: <DeleteDeliveryCommand> =====
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         [Saga] <DeletedDeliveryEvent> is received for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         ===== [Delete Order] #4: <DeleteReportCommand> =====
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         [Saga] <DeletedReportEvent> is received for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         ===== [Delete Order] #5: <CompleteDeleteOrderCommand> =====
01/17 22:10:38 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CompleteDeleteOrderCommand> for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.aggregate.OrderAggregate       [@EventSourcingHandler] Executing <CompletedDeleteOrderEvent> for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.events.OrderEventsHandler      [@EventHandler] Executing <CompletedDeleteOrderEvent> for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         [Saga] [CompletedDeleteOrderEvent] is received for Order Id: ORDER_062233485
01/17 22:10:38 - INFO --- c.a.order.saga.OrderDeletingSaga         ===== [Delete Order] Transaction is Finished =====

 

3) 데이터 확인

DBeaver의 SQL편집기에서 SQL문을 실행하여 데이터가 각 서비스 DB에서 삭제 되었는지 확인해 보십시오. 


 

이상으로 주문의 생성, 수정, 삭제를 테스트 하면서 대략의 흐름을 이해 하여 보았습니다. 

실패 시의 보상처리는 소스를 수정해 보면서 해야 되서 테스트 못했는데 이후에 개발하면서 해 보겠습니다. 

다음 장에서는 마이크로서비스 패턴 개발 프레임워크인 Axon 프로임워크에 대해 실습해 보도록 하겠습니다. 


마이크로서비스 패턴 쉽게 개발하기 목차

  1. 마이크로서비스 패턴 이해: Saga, Event Sourcing, API Composition, CQRS 이해
  2. 실습환경 준비
  3. 주문 서비스 테스트 
  4. Axon Framework 이해 
    1. 코끼리 냉장고에 넣기 프로젝트 시작
    2. CommandGateway와 Event Sourcing
    3. EventGateway
    4. QueryGateay
    5. Snapshot
    6. State Stored Aggregate
    7. Event Replay로 조회DB 데이터 복구

아래 주제들은 '마이크로서비스패턴 쉽게 개발하기'라는 제 책에서 만나실 수 있습니다. 

  • 멀티 모듈 프로젝트 작성
  • 신규 주문 정상처리 프로세스 구현 
  • 배송 상태 변경 및 재고 증감 처리
  • 신규 주문 보상처리 프로세스 구현
  • 주문 수정 정상처리 프로세스 구현
  • 주문 수정 보상처리 프로세스 구현
  • 주문 삭제 정상처리 프로세스 구현
  • 주문 삭제 보상처리 프로세스 구현
  • API Composition 패턴과 CQRS 패턴

책 한번 내겠다는 평소의 꿈을 실현하기 위해 이번에 전자책을 내게 되었습니다. 

제 꿈을 응원하신다는 마음으로 전체 내용을 공유하지 않는것을 양해해 주시고 

구매까지 해 주시면 더욱 감사하겠습니다. 

https://happycloud-lee.tistory.com/notice/291

 

[마이크로서비스패턴 쉽게 개발하기] 전자책 출간

안녕하세요? 온달입니다. 그동안 몇 번 시도하다 포기했던 책내기에 드디어 성공 했습니다. 책 제목은 '마이크로서비스패턴 쉽게 개발하기'입니다. 2024년 2월 19일 부터 교보문고, Yes24, 알라딘 등

happycloud-lee.tistory.com

 

댓글

전자책 출간 알림 [마이크로서비스패턴 쉽게 개발하기]