- Money-service : 사용자와 밀접한 관계
(페이 지갑에 들어가는 머니를 관리하는 모듈로 머니의 단순 차/가감을 관리하며 머니의 차/가감 내역을 저장한다)- Payment-service : 외부 API와 밀접한 관계
(외부 API (금융결제원)의 요청과 응답을 관리하는 결제와 직접적으로 연결된 모듈로 결제의 성공/실패 내역을 저장합니다)
머니 금액 차감
순서를 어디에 위치해 두냐의 차이다.고민하는 부분
방법1: 요청이벤트 저장 -> 머니 차감 선 차감 -> 외부 API 통신
방법2: 요청이벤트 저장 -> 외부 API 통신 -> 머니 차감
고민 1)
MSA를 진행하면서 HTTP 통신을 최소화 할 수 있는 방법을 고민했다
1번의 방법은 money-service -> money-service -> openapi-service 순서로 외부 네트워크 통신이 2번 이루어지며
2번의 방법은 money-service -> openapi-service -> money-service 외부 네트워크 통신이 3번 이루어 진다.
그렇다면 단순히 2번의 네트워크 통신을 하는 방법1이 에러를 줄이는 방법인가?
고안 1)
task를 나눠서 적용한다.
요청이벤트를 저장하는 로직은 결제 요청시 필수로 수행되어야하는 절차인데,
이것을 비동기로 task1로 수행하고
이후 절차를 task2로 묶어서 수행하도록 한다.
결제 요청시 랜덤키를 발행하여 이 랜덤키를 가지고 외부 API 통신의 결과가 성공으로 응답을 받을 경우 요청받은 랜덤키를 통해 결제 인증을 한다.
따라서 다른 task가 실행되지만 랜덤키를 통해 결제 성공 여부를 확인할 수 있도록 하고
성공시 머니 금액을 차감한다.
고안 2)
외부 API 통신을 먼저 수행하는 이유로는 필수적으로 외부 API 통신과 응답에 따라 결정되므로 해당 로직은 외부 API에 의존적이다.
따라서 외부 API 먼저 수행하고 응답을 받음으로써 빠른 실패를 하여 이후 불필요한 로직의 수행을 줄일 수 있다.
🚩 따라서 결론적으로 방법 2를 선택한다.
머니 금액 차감을 앞에 두는 경우의 로직 순서를 보겠다.
💰 MONEY_SERVICE
|
요청
금액의 이벤트를 내역을 저장한다.💳 PAYMENT-SERVICE
|
성공/실패
이벤트 내역을 저장한다.이때 성공했을 때는 큰 문제가 없지만,
실패시 문제 발생
결제 실패 + SAGA 패턴의 에러 발생(가정)으로 롤백이 되지 않았다고 가정했을 때
사용자 입장에서 머니 금액은 차감되었으나 실제 결제는 이루어지지 않은 것으로 보인다.
이는 서비스에 대한 부정적인 경험으로 사용자의 신뢰성에 연결될 수 있는 문제고 이탈까지도 발생할 수 있다.
결제 서비스에서 가장 좋지 않은 상황으로 인지했다.
💰 MONEY_SERVICE
|
요청
금액의 이벤트를 내역을 저장한다.💳 PAYMENT-SERVICE
|
성공/실패
이벤트 내역을 저장한다. 💰 MONEY_SERVICE
|
실패할 경우 money 차감 API가 호출되지 않도록 로직을 설계하는데
머니 차감 단계가 롤백이 되지 않으면 전체 롤백이 되지 않았다는 것.
실패할 경우에는 API 호출이 한번 줄어들지만,
성공하는 경우에는 외부 API 호출이 방법1. 보다 1회 더 발생한다.
결제는 성공했는데 API 호출에 문제로 차감이 되지 않은 경우.
돈은 나갔는데 차감되지 않는 상황. 사용자 입장에서 큰 문제는 아니다.
이벤트(Async)방식으로 큐잉을 통해서 트랜잭션에 포함된 여러 작업 (각 서비스의 API call)의 결과를 게시하고 이벤트를 비동기 처리(비동기 통신)하여 다음 작업들을 진행
기본적으로 아래의 트랜잭션 모두 보상 트랜잭션을 사용한다.
- 코레오그레피: 독립적인 조율자 (ochestrator)를 두지않고 saga를 구현하는 방법 모니터링이 어렵다
- 각각의 서비스 입장에서 수행되기 때문에 전체적인 프로세스에 대한 트랜잭션을 확인하기 어렵다
- 오케스트레이션: 독립적인 조율자를 두어 하나의 saga(트랜잭션)에 대한 매니징을 담당하는 방법 모니터링이 수월
※ 보상 트랜잭션이란?
저는 처음에 보상 트랜잭션으로 롤백을 한다고 들었을 때,
기존의 커밋한 트랜잭션(Board-Service의 게시글 생성 트랜잭션)을 물리적으로 롤백한다는 의미로 이해했습니다.
그래서 어떻게 하지? 고민을 했었는데 알고 보니 트랜잭션 자체를 롤백하는게 아니라 롤백한 것처럼 만드는 것이었습니다.
그래서 보상 트랜잭션이란, 물리적 개념이 아니라 롤백된 것처럼 비즈니스적으로 롤백 처리하는 것을 의미합니다.
Saga 패턴의 예시로 주문 & 결제 도메인을 많이 사용해서, 저도 해당 예시로 예를 들어보겠습니다.
주문 생성 -> 결제 완료 순으로 Flow가 있을 때 Saga 패턴을 적용한다고 해봅시다.
이때, 주문 생성 트랜잭션이 완료 후에 결제 완료 로직에서 장애가 발생했을 때 Saga 패턴에서는 보상 트랜잭션을 실행한다고 했습니다.
일반적으로 보상 트랜잭션의 구현은 'Status'를 추가해서 해당 Status를 변경하는 것으로 구현합니다.
그래서 OrderStatus를 생성하고 주문 생성 시에 SUCCESS 상태로 변경하고,
보상 트랜잭션 비즈니스 로직을 실행하면 OrderStatus를 CANCLED 같은 실패 상태로 변경합니다.
그래서 최종적으로 SUCCESS 상태의 주문만 이후에 처리하는 느낌으로 최종적으로 롤백된 것처럼 구현할 수 있게 됩니다.