이 프로젝트는 Temporal을 Saga Orchestrator로, Spring Cloud Netflix Eureka를 서비스 디스커버리로 사용하여 마이크로서비스 간 분산 트랜잭션을 관리하는 예제입니다.
┌─────────────────────────────────────────────────────────────────────┐
│ 클라이언트 │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ order-service (8080) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ POST /api/orders │ │
│ │ 1. 재고 확인 (Feign → inventory-service) [동기] │ │
│ │ 2. 주문 저장 (PENDING) │ │
│ │ 3. Workflow 시작 (Temporal) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│ │
│ Feign (동기) │ Workflow 시작
▼ ▼
┌───────────────────┐ ┌─────────────────────────────────────────┐
│ inventory-service │ │ Temporal Server │
│ (8083) │ │ (7233) │
└───────────────────┘ └─────────────────────────────────────────┘
│
│ Task Queue 폴링
▼
┌─────────────────────────────────────────────────────────────────────┐
│ saga-orchestrator (8082) ⭐ Orchestrator │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Temporal Worker │ │
│ │ ├─ OrderWorkflow (Saga 로직) │ │
│ │ │ ├─ Step 1: reserveStock (inventory-service) │ │
│ │ │ ├─ Step 2: processPayment (payment-service) │ │
│ │ │ └─ Step 3: completeOrder (order-service) │ │
│ │ └─ 보상 트랜잭션 │ │
│ │ ├─ releaseStock (재고 해제) │ │
│ │ └─ refundPayment (결제 환불) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ inventory │ │ payment │ │ order │
│ -service │ │ -service │ │ -service │
│ (Activity) │ │ (Activity) │ │ (Activity) │
└───────────────┘ └───────────────┘ └───────────────┘
┌─────────────────────┐
│ Eureka Server │
│ (8761) │
│ 서비스 디스커버리 │
└─────────────────────┘
▲
┌─────────────────────┼─────────────────────┐
│ │ │
order-service inventory-service payment-service
saga-orchestrator
| 구분 | 기술 |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 3.3.x |
| Build Tool | Gradle 8.x |
| Service Discovery | Spring Cloud Netflix Eureka |
| 동기 호출 | OpenFeign |
| Saga Orchestration | Temporal |
| Database | H2 (in-memory), PostgreSQL (Temporal) |
| Container | Docker, Docker Compose |
saga-example/
├── eureka-server/ # 🆕 Eureka Server (서비스 디스커버리)
│ └── src/main/java/com/example/eureka/
│ └── EurekaServerApplication.java
│
├── saga-common/ # 공유 모듈 (인터페이스, 상수)
│ └── src/main/java/com/example/saga/common/
│ ├── workflow/
│ │ ├── OrderWorkflow.java
│ │ ├── OrderServiceActivities.java
│ │ ├── PaymentServiceActivities.java
│ │ └── InventoryServiceActivities.java # 🆕
│ └── constants/
│ └── SagaConstants.java
│
├── inventory-service/ # 🆕 재고 서비스 (도메인)
│ └── src/main/java/com/example/inventory/
│ ├── InventoryServiceApplication.java
│ ├── domain/
│ │ ├── Inventory.java
│ │ └── StockReservation.java
│ ├── repository/
│ ├── service/
│ │ ├── InventoryService.java
│ │ └── InventoryServiceActivitiesImpl.java
│ ├── controller/
│ │ └── InventoryController.java
│ └── config/
│
├── order-service/ # 주문 서비스 (+ Feign Client)
│ └── src/main/java/com/example/order/
│ ├── OrderServiceApplication.java
│ ├── client/ # 🆕
│ │ ├── InventoryClient.java # Feign Client
│ │ └── InventoryClientFallback.java
│ ├── domain/
│ ├── service/
│ │ ├── OrderService.java # 재고 확인 로직 추가
│ │ └── OrderServiceActivitiesImpl.java
│ └── ...
│
├── payment-service/ # 결제 서비스
│ └── src/main/java/com/example/payment/
│ └── ...
│
├── saga-orchestrator/ # Saga Orchestrator
│ └── src/main/java/com/example/orchestrator/
│ └── workflow/
│ └── OrderWorkflowImpl.java # 재고 Saga 추가
│
├── docker-compose.yml
├── build.gradle
├── settings.gradle
└── README.md
saga-common (공유 라이브러리)
↑
┌───────────────┼───────────────┬───────────────┐
│ │ │ │
order-service inventory-service payment-service saga-orchestrator
(Workflow 시작) (Activity 구현) (Activity 구현) (Workflow 구현)
│
└──→ InventoryClient (Feign - 동기 호출)
│
▼
inventory-service
↑ Eureka 등록 ↑
eureka-server (8761)
sequenceDiagram
participant Client
participant OrderService
participant InventoryService
participant Temporal
participant Orchestrator
participant PaymentService
Client->>OrderService: POST /api/orders<br/>{productId, quantity, amount: 50}
OrderService->>InventoryService: [Feign] 재고 확인
InventoryService-->>OrderService: {available: true}
OrderService->>OrderService: 주문 저장 (PENDING)
OrderService->>Temporal: Start OrderWorkflow
OrderService-->>Client: 201 Created<br/>{id, status: PENDING}
Temporal->>Orchestrator: Task Queue 폴링
Note over Orchestrator: [Step 1] 재고 예약
Orchestrator->>InventoryService: reserveStock()
InventoryService-->>Orchestrator: true (예약 성공)
Note over Orchestrator: [Step 2] 결제 처리
Orchestrator->>PaymentService: processPayment()
PaymentService-->>Orchestrator: true (결제 성공)
Note over Orchestrator: [Step 3] 주문 완료
Orchestrator->>OrderService: completeOrder()
Orchestrator->>InventoryService: confirmStock()
Note over Orchestrator: Saga 완료 (성공)
sequenceDiagram
participant Client
participant OrderService
participant InventoryService
participant Temporal
participant Orchestrator
participant PaymentService
Client->>OrderService: POST /api/orders<br/>{productId, quantity, amount: 200}
OrderService->>InventoryService: [Feign] 재고 확인
InventoryService-->>OrderService: {available: true}
OrderService->>OrderService: 주문 저장 (PENDING)
OrderService->>Temporal: Start OrderWorkflow
OrderService-->>Client: 201 Created
Temporal->>Orchestrator: Task Queue 폴링
Note over Orchestrator: [Step 1] 재고 예약
Orchestrator->>InventoryService: reserveStock()
InventoryService-->>Orchestrator: true
Note over Orchestrator: [Step 2] 결제 처리
Orchestrator->>PaymentService: processPayment()
PaymentService-->>Orchestrator: false (잔액 부족)
Note over Orchestrator: 보상 트랜잭션 실행
Orchestrator->>InventoryService: releaseStock() (보상)
Orchestrator->>OrderService: cancelOrder()
Note over Orchestrator: Saga 완료 (결제 실패 → 취소)
- Java 21
- Docker & Docker Compose
- Gradle 8.x (또는 Gradle Wrapper 사용)
./gradlew clean build -x testdocker-compose up -d# 터미널 1: Eureka Server (먼저 실행!)
./gradlew :eureka-server:bootRun
# 터미널 2: Inventory Service
./gradlew :inventory-service:bootRun
# 터미널 3: Payment Service
./gradlew :payment-service:bootRun
# 터미널 4: Order Service
./gradlew :order-service:bootRun
# 터미널 5: Saga Orchestrator
./gradlew :saga-orchestrator:bootRun| 서비스 | 포트 | 설명 |
|---|---|---|
| eureka-server | 8761 | 서비스 디스커버리 Dashboard |
| order-service | 8080 | 주문 API |
| payment-service | 8081 | 결제 API |
| saga-orchestrator | 8082 | Saga Orchestrator (Temporal Worker) |
| inventory-service | 8083 | 재고 API |
| Temporal Web UI | 8088 | Workflow 모니터링 |
| Temporal Server | 7233 | Temporal gRPC |
docker-compose down -vcurl http://localhost:8083/api/inventorycurl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"userId": "user-1",
"productId": "PROD-001",
"quantity": 2,
"amount": 50
}'응답:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": "user-1",
"productId": "PROD-001",
"quantity": 2,
"amount": 50,
"status": "PENDING"
}curl http://localhost:8080/api/orders/{orderId}curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"userId": "user-2",
"productId": "PROD-001",
"quantity": 1,
"amount": 200
}'잠시 후 조회하면 CANCELED 상태:
{
"id": "...",
"status": "CANCELED"
}http://localhost:8761 에서 Eureka Dashboard에 접속하여:
- 등록된 서비스 목록 확인
- 각 서비스의 인스턴스 상태 확인
- 서비스 헬스 체크
- ORDER-SERVICE
- PAYMENT-SERVICE
- INVENTORY-SERVICE
- SAGA-ORCHESTRATOR
http://localhost:8088 에서 Temporal Web UI에 접속하여:
- Workflow 목록 확인: 실행 중인/완료된 Workflow 목록
- Workflow 상세 보기: 각 Activity 실행 이력, 입력/출력 값
- Timeline 보기: Saga 각 단계의 시간 흐름
order-{orderId}형태로 생성됨
- 서비스 자동 등록/해제
- 로드 밸런싱 지원
- 서비스 헬스 체크
- 주문 생성 전 재고 확인 (동기)
- 선언적 REST 클라이언트
- Fallback 처리 지원
- 3단계 Saga: 재고 예약 → 결제 → 주문 완료
- 자동 보상 트랜잭션
- 상태 영속성 및 재시도 처리
- 멱등성: 재고 예약, 결제 요청의 중복 처리 방지
- 타임아웃: Activity별 적절한 타임아웃 설정
- 재시도 정책: 서비스별 재시도 전략
- 모니터링: Prometheus, Grafana 연동
- 보안: mTLS, 인증/인가
- 로깅: 분산 추적 (Jaeger, Zipkin)