Saga Pattern: Mengelola Distributed Transactions

SOFTWARE ARCHITECTURE By TryzTech Team
Saga PatternDistributed SystemsMicroservicesTransactionsArchitecture
Bagikan

Daftar Isi

Pendahuluan

Dalam monolith, checkout sering bisa berjalan dalam satu database transaction. Buat order, reserve inventory, charge payment, lalu commit semuanya bersama.

Dalam distributed system, langkah-langkah itu bisa berada di service berbeda. Order service, inventory service, payment service, dan shipping service masing-masing punya data sendiri. Satu database transaction tidak lagi mencakup seluruh workflow.

Saga pattern hadir sebagai solusi untuk mengoordinasikan workflow bisnis berdurasi panjang antar-service, tanpa memaksakan semua proses berjalan secara atomic.

Kenapa Distributed Transaction Sulit?

Distributed transaction sulit karena setiap service bisa gagal sendiri-sendiri.

LangkahKemungkinan gagal
Buat orderdatabase timeout
Reserve inventorybarang habis
Charge paymentpayment provider error
Buat shipmentshipping service unavailable

Jika payment berhasil tapi shipment gagal, sistem perlu keputusan bisnis. Apakah shipment dicoba lagi? Order dibatalkan? Payment di-refund? Perlu manual review?

Saga membuat keputusan seperti itu menjadi lebih eksplisit.

Apa yang Dilakukan Saga Pattern?

Saga memecah satu business transaction besar menjadi rangkaian local transaction yang lebih kecil.

Setiap langkah commit secara lokal. Jika langkah berikutnya gagal, saga menjalankan compensating action untuk membatalkan atau menyeimbangkan efek langkah sebelumnya.

Buat order -> Reserve stock -> Charge payment -> Buat shipment

Jika charge payment gagal:

Release stock -> Tandai order gagal

Compensation tidak selalu rollback sempurna. Ia adalah aksi bisnis untuk membuat sistem konsisten kembali.

Choreography vs Orchestration

Ada dua cara umum menerapkan saga.

StyleCara kerjaCocok untuk
Choreographyservice bereaksi terhadap event dari service lainworkflow kecil dengan dependency sederhana
Orchestrationsatu coordinator memberi instruksi ke service lainworkflow kompleks yang butuh visibility dan kontrol

Choreography terasa ringan, tapi bisa sulit dilacak. Orchestration lebih mudah diobservasi, tapi menambah komponen workflow pusat.

Contoh: Saga Checkout Order

Checkout saga dengan orchestration bisa terlihat seperti ini:

LangkahCommandSuccess eventCompensation saat gagal
1Buat orderOrderCreatedtandai order gagal
2Reserve inventoryInventoryReservedrelease inventory
3Charge paymentPaymentCapturedrefund payment
4Buat shipmentShipmentCreatedcancel shipment

Saga coordinator menyimpan progress:

order_idstatelast_step
O1001payment_capturedcharge payment
O1002failedreserve inventory
O1003completedcreate shipment

State ini penting untuk retry, debugging, dan support.

Diagram Saga Orchestration

Berikut contoh alur checkout saga dengan model orchestration. CheckoutSaga menjadi coordinator yang menentukan langkah berikutnya, menyimpan state, dan menjalankan compensation saat ada step yang gagal.

sequenceDiagram
  autonumber
  participant User
  participant Saga as CheckoutSaga
  participant Order as Order Service
  participant Inventory as Inventory Service
  participant Payment as Payment Service
  participant Shipping as Shipping Service

  User->>Saga: Place order
  Saga->>Order: Create order
  Order-->>Saga: OrderCreated
  Saga->>Inventory: Reserve inventory
  Inventory-->>Saga: InventoryReserved
  Saga->>Payment: Capture payment

  alt payment succeeds
    Payment-->>Saga: PaymentCaptured
    Saga->>Shipping: Create shipment
    Shipping-->>Saga: ShipmentCreated
    Saga-->>User: Checkout completed
  else payment fails
    Payment-->>Saga: PaymentFailed
    Saga->>Inventory: Release inventory
    Saga->>Order: Mark order failed
    Saga-->>User: Checkout failed
  end

Dalam choreography, diagramnya akan berbeda. Tidak ada coordinator pusat. Setiap service publish event, lalu service lain bereaksi terhadap event tersebut. Itu bisa lebih simpel untuk workflow kecil, tapi biasanya lebih sulit dilacak saat flow mulai bercabang.

Contoh Kode Saga Orchestrator

Contoh berikut sengaja dibuat sederhana. Tujuannya bukan menjadi production-ready framework, tapi menunjukkan bagaimana orchestrator menjalankan step berurutan dan memanggil compensation secara terbalik jika ada kegagalan.

type SagaContext = {
  orderId: string;
  userId: string;
  items: Array<{ sku: string; quantity: number }>;
  paymentId?: string;
  shipmentId?: string;
};

type SagaStep = {
  name: string;
  execute: (context: SagaContext) => Promise<void>;
  compensate: (context: SagaContext) => Promise<void>;
};

async function runSaga(context: SagaContext, steps: SagaStep[]) {
  const completedSteps: SagaStep[] = [];

  try {
    for (const step of steps) {
      await step.execute(context);
      completedSteps.push(step);
      await saveSagaState(context.orderId, step.name, "completed");
    }

    await saveSagaState(context.orderId, "checkout", "completed");
  } catch (error) {
    await saveSagaState(context.orderId, "checkout", "compensating");

    for (const step of completedSteps.reverse()) {
      await step.compensate(context);
      await saveSagaState(context.orderId, step.name, "compensated");
    }

    await saveSagaState(context.orderId, "checkout", "failed");
    throw error;
  }
}

Pemakaiannya:

const checkoutSteps: SagaStep[] = [
  {
    name: "create_order",
    execute: async (context) => {
      await orderService.createOrder(context.orderId, context.userId, context.items);
    },
    compensate: async (context) => {
      await orderService.markOrderFailed(context.orderId);
    },
  },
  {
    name: "reserve_inventory",
    execute: async (context) => {
      await inventoryService.reserve(context.orderId, context.items);
    },
    compensate: async (context) => {
      await inventoryService.release(context.orderId);
    },
  },
  {
    name: "capture_payment",
    execute: async (context) => {
      context.paymentId = await paymentService.capture(context.orderId);
    },
    compensate: async (context) => {
      if (context.paymentId) {
        await paymentService.refund(context.paymentId);
      }
    },
  },
  {
    name: "create_shipment",
    execute: async (context) => {
      context.shipmentId = await shippingService.createShipment(context.orderId);
    },
    compensate: async (context) => {
      if (context.shipmentId) {
        await shippingService.cancelShipment(context.shipmentId);
      }
    },
  },
];

await runSaga(
  {
    orderId: "O1004",
    userId: "U2001",
    items: [{ sku: "SKU-1", quantity: 2 }],
  },
  checkoutSteps,
);

Ada beberapa hal penting yang belum ditangani contoh kecil ini: retry dengan backoff, timeout per step, idempotency key, concurrency control, dead-letter queue, dan recovery kalau proses mati di tengah jalan. Di production, bagian-bagian itu biasanya ditangani oleh workflow engine atau message framework.

Compensating Action

Compensating action adalah inti desain saga.

Contohnya:

  • release reserved inventory
  • refund payment
  • cancel shipment
  • tandai order sebagai failed
  • buat support ticket untuk manual handling

Compensation yang baik harus idempotent. Jika sistem mengulang compensation, tidak boleh terjadi refund dua kali atau inventory dilepas dua kali.

Library dan Tool yang Bisa Dipakai

Kamu bisa menerapkan saga sendiri, tapi untuk workflow penting biasanya lebih aman memakai library atau workflow engine yang sudah menangani state, retry, timeout, observability, dan recovery.

Beberapa opsi yang umum:

ToolCocok untuk
Temporalworkflow durable berbasis code, cocok untuk orchestration jangka panjang
Camunda / Zeebeproses bisnis berbasis BPMN, cocok saat workflow perlu terlihat jelas oleh tim bisnis dan engineering
AWS Step Functionsorchestration serverless di ekosistem AWS
Azure Durable Functionsworkflow durable di ekosistem Azure
MassTransitsaga state machine dan routing slip di ekosistem .NET/message bus
Netflix Conductorworkflow orchestration untuk microservices

Kalau sistem masih kecil, implementasi sederhana dengan database state table dan message queue bisa cukup. Begitu workflow makin panjang, punya banyak cabang, atau butuh audit trail, workflow engine biasanya lebih masuk akal.

Hal Operasional yang Perlu Disiapkan

Saga bukan cuma design pattern. Ia butuh dukungan operasional.

Yang perlu disiapkan:

  • saga state yang durable
  • aturan retry
  • timeout handling
  • idempotency key
  • dead-letter queue atau jalur manual review
  • visibility untuk workflow yang stuck
  • ownership aturan compensation dari sisi bisnis

Tanpa observability, saga bisa gagal diam-diam dan membuat user bingung.

Kesalahan Umum

Menganggap compensation sebagai rollback teknis

Compensation adalah keputusan bisnis. Refund, cancellation, atau manual review bisa sama-sama valid tergantung domain.

Lupa idempotency

Message bisa terkirim lebih dari sekali. Setiap command dan compensation harus aman untuk diulang.

Menyembunyikan state saga

Jika support dan engineering tidak bisa melihat workflow berhenti di mana, incident akan lebih sulit diselesaikan.

Memakai saga untuk workflow sederhana

Tidak semua proses multi-step butuh kompleksitas saga. Gunakan saat workflow melewati batas service dan failure handling memang penting.

Checklist

  • Definisikan setiap local transaction dengan jelas.
  • Definisikan compensation untuk setiap langkah.
  • Buat command dan compensation idempotent.
  • Simpan saga state secara durable.
  • Tambahkan timeout dan retry policy.
  • Track correlation ID antar service.
  • Tampilkan workflow yang stuck untuk support atau operasi.
  • Tentukan kapan perlu manual intervention.
  • Test failure di setiap langkah.

FAQ

Apakah saga sama dengan two-phase commit?

Tidak. Two-phase commit mencoba membuat pekerjaan distributed menjadi atomic. Saga menerima local commit dan memakai compensation saat langkah berikutnya gagal.

Pilih choreography atau orchestration?

Gunakan choreography untuk event chain sederhana. Gunakan orchestration saat workflow punya banyak cabang, urutan kuat, atau butuh visibility pusat.

Apakah saga menjamin strong consistency?

Tidak. Saga biasanya memberi eventual consistency. Sistem bisa berada di state sementara sebelum semua langkah selesai. Untuk audit trail dan pencatatan state perubahan saga yang andal, pattern ini sering dipadukan dengan Event Sourcing dan CQRS Pattern. Selain itu, tempatkan saga handler atau orchestrator di dalam Application Layer sesuai prinsip Hexagonal Architecture agar business logic terpisah dari message broker.

Kesimpulan

Saga pattern membantu tim mengelola workflow bisnis lintas service. Pattern ini tidak menghapus failure, tapi memberi sistem cara yang jelas untuk merespons saat failure terjadi.

Desain workflow, compensation, retry, dan observability sebagai satu paket. Di situlah saga berubah dari diagram menjadi pattern yang siap production.

Lanjutkan membaca topik yang masih satu konteks.

Jangan Ketinggalan Info Terbaru

Dapatkan artikel teknologi, tips, dan insights menarik langsung ke email Kamu.