Implement the Saga Pattern in Go by defining a coordinator service that orchestrates steps and compensating actions for each step. Use a Saga struct to hold the sequence of actions and their compensations, then execute them sequentially while catching errors to trigger rollbacks.
package main
import (
"fmt"
"log"
)
type Step func() error
type Compensation func() error
type SagaStep struct {
Action Step
Compensation Compensation
}
type Saga struct {
Steps []SagaStep
}
func (s *Saga) Execute() error {
for i, step := range s.Steps {
if err := step.Action(); err != nil {
log.Printf("Step %d failed, rolling back", i)
return s.Rollback(i - 1)
}
}
return nil
}
func (s *Saga) Rollback(fromIndex int) error {
for i := fromIndex; i >= 0; i-- {
if err := s.Steps[i].Compensation(); err != nil {
return fmt.Errorf("rollback step %d failed: %w", i, err)
}
}
return nil
}
func main() {
saga := &Saga{
Steps: []SagaStep{
{
Action: func() error { fmt.Println("Step 1: Create Order"); return nil },
Compensation: func() error { fmt.Println("Compensate 1: Cancel Order"); return nil },
},
{
Action: func() error { fmt.Println("Step 2: Reserve Inventory"); return fmt.Errorf("inventory unavailable") },
Compensation: func() error { fmt.Println("Compensate 2: Release Inventory"); return nil },
},
{
Action: func() error { fmt.Println("Step 3: Charge Payment"); return nil },
Compensation: func() error { fmt.Println("Compensate 3: Refund Payment"); return nil },
},
},
}
if err := saga.Execute(); err != nil {
log.Printf("Saga failed: %v", err)
}
}