Konsistensi Data pada Arsitektur Terdistribusi

Pola Two-Phase Commit (2PC) tidak cocok untuk arsitektur microservices modern karena sifatnya yang blocking, latensi tinggi, dan ketergantungan pada koordinator tunggal yang membatasi skalabilitas. Sebagai gantinya, Saga Pattern memecah transaksi global menjadi serangkaian transaksi lokal yang dieksekusi berurutan pada setiap layanan terkait. Setiap langkah memperbarui database lokal dan menerbitkan pesan atau memicu langkah berikutnya.

Tantangan utama Saga adalah penanganan kegagalan: jika satu langkah gagal, sistem harus mengeksekusi compensating transactions untuk membatalkan perubahan sebelumnya secara semantik. Dua pendekatan utama untuk mengimplementasikan pola ini adalah Choreography dan Orchestration.

1. Arsitektur Dasar

Saga Berbasis Choreography

Pada pendekatan Choreography, tidak ada entitas pengendali pusat. Setiap layanan menjalankan transaksi lokalnya, lalu mempublikasikan domain event ke message broker (misalnya Apache Kafka atau RabbitMQ). Layanan lain mendengarkan event tersebut dan menjalankan logika bisnis masing-masing secara independen.

[Order Service] --(OrderCreated)--> [Message Broker]
                                          |
                                          v
[Payment Service] <-----------------------+
       |
       +--(PaymentProcessed)------> [Message Broker]
                                          |
                                          v
[Inventory Service] <---------------------+

Saga Berbasis Orchestration

Pendekatan Orchestration menggunakan koordinator terpusat (Saga Orchestrator) seperti Temporal, AWS Step Functions, Camunda, atau layanan internal kustom. Orchestrator mengeksekusi alur kerja dengan mengirimkan instruksi (commands) secara eksplisit ke partisipan via RPC atau antrean pesan, menunggu respons, lalu memutuskan langkah berikutnya.

                [Saga Orchestrator]
               /         |         \
     1. CreateOrder  2. ProcessPayment  3. ReserveStock
             v           v           v
      [Order Svc]  [Payment Svc]  [Inventory Svc]

2. Analisis Trade-off: Coupling, Siklus Dependensi, dan Kompensasi

Coupling dan Siklus Dependensi (Cyclic Dependency)

Choreography tampak menghasilkan loose coupling di level deployment karena layanan hanya bergantung pada broker. Namun, pendekatan ini menimbulkan semantic coupling tersembunyi: setiap layanan harus memahami konteks event dari layanan lain untuk menentukan kapan harus bertindak.

Ketika alur transaksi meluas hingga lebih dari 4–5 layanan, Choreography rentan terhadap cyclic dependency. Layanan A memicu Layanan B, yang memicu Layanan C, yang secara tidak sengaja mempublikasikan event yang kembali dikonsumsi Layanan A. Kondisi ini memicu infinite loop jika kontrol state lokal tidak ketat.

Orchestration memusatkan dependensi alur kerja pada koordinator. Layanan partisipan bersifat pasif; mereka mengeksekusi perintah dan mengembalikan hasil tanpa perlu mengetahui siapa konsumen berikutnya. Alur transaksi bersifat eksplisit, menghilangkan risiko dependensi siklis antar-layanan.

Penanganan Compensating Transaction

Rollback terdistribusi pada Choreography membutuhkan koordinasi event kompensasi yang kompleks. Jika Inventory Service gagal mengalokasikan stok, ia harus mempublikasikan event InventoryAllocationFailed. Layanan pembayaran dan pesanan harus mendengarkan event kegagalan tersebut untuk mengeksekusi kompensasi (refund dan pembatalan pesanan).

// Contoh penanganan kompensasi pada Choreography (Payment Service)
func handleInventoryFailed(event InventoryAllocationFailedEvent) error {
    payment, err := paymentRepo.FindByOrderID(event.OrderID)
    if err != nil {
        return err // Butuh retry logic eksplisit
    }
    return paymentGateway.Refund(payment.TransactionID)
}

Kelemahan pendekatan di atas muncul saat terdapat percabangan kondisi atau kegagalan bertingkat. Jika kompensasi pada salah satu partisipan gagal, pelacakan status akhir transaksi menjadi sangat rapuh.

Pada Orchestration, compensating transactions dikelola oleh state machine koordinator. Alur rollback didefinisikan secara deklaratif atau programatis dalam urutan terbalik dari transaksi yang berhasil:

// Contoh alur kompensasi pada Temporal (Workflow Go SDK)
func OrderSagaWorkflow(ctx workflow.Context, order OrderData) (err error) {
    var compensations []func(workflow.Context)

    defer func() {
        if err != nil {
            // Eksekusi kompensasi dengan urutan mundur (LIFO)
            for i := len(compensations) - 1; i >= 0; i-- {
                _ = compensations[i](ctx)
            }
        }
    }()

    // Step 1: Create Pending Order
    err = workflow.ExecuteActivity(ctx, CreateOrderActivity, order).Get(ctx, nil)
    if err != nil { return err }
    compensations = append(compensations, CancelOrderCompensate)

    // Step 2: Charge Payment
    err = workflow.ExecuteActivity(ctx, ProcessPaymentActivity, order).Get(ctx, nil)
    if err != nil { return err }
    compensations = append(compensations, RefundPaymentCompensate)

    // Step 3: Reserve Inventory
    err = workflow.ExecuteActivity(ctx, ReserveInventoryActivity, order).Get(ctx, nil)
    return err
}

3. Observabilitas dan Debugging

Menentukan status suatu transaksi pada Choreography memerlukan implementasi distributed tracing (misal OpenTelemetry) dengan injeksi traceparent dan correlation_id di setiap payload event. Tanpa tracing yang matang, pelacakan pesanan yang tersangkut di antrean membutuhkan agregasi log lintas sistem.

Sebaliknya, Orchestrator menyediakan visibilitas tersentralisasi bawaan. State machine mencatat riwayat eksekusi (execution history) per instans saga. Status eksekusi—apakah sedang berjalan, menunggu respons worker, gagal, atau dalam proses kompensasi—dapat dilihat secara real-time via dashboard UI orchestrator.

4. Biaya Operasional dan Overhead Infrastruktur

Pemilihan pola berdampak langsung pada kompleksitas operasional infrastruktur:

  • Choreography: Memanfaatkan message broker yang umumnya sudah ada dalam arsitektur microservices (misalnya Kafka atau RabbitMQ). Tidak ada komponen baru yang perlu di-deploy. Namun, beban operasional bergeser ke sisi pemeliharaan kode aplikasi: validasi skema event (menggunakan Schema Registry seperti Avro/Protobuf) dan penanganan dead-letter queue (DLQ).
  • Orchestration: Memerlukan runtime engine terdedikasi. Solusi self-hosted seperti Temporal atau Camunda membutuhkan deployment database backend (PostgreSQL/Cassandra/Elasticsearch), monitoring performa worker, serta konfigurasi ketersediaan tinggi (HA). Managed service seperti AWS Step Functions memangkas kompleksitas setup, tetapi membebankan biaya per transisi status (state transition) yang dapat membengkak signifikan pada throughput ribuan transaksi per detik.

5. Matriks Keputusan

Gunakan kriteria berikut untuk menentukan pendekatan Saga yang tepat:

KriteriaGunakan ChoreographyGunakan Orchestration
Jumlah Langkah / LayananRantai pendek (2–4 langkah sederhana).Rantai panjang (> 4 langkah) atau alur non-linear.
Logika Bisnis & KompensasiLinear, kompensasi jarang atau deterministik.Kompleks, memiliki percabangan, butuh retry bertahap dan kompensasi terjamin.
Struktur Organisasi TimBanyak tim otonom, kepemilikan domain terpisah tanpa tim platform sentral.Satu tim domain terpadu atau organisasi yang memiliki tim platform.
Infrastruktur yang AdaSudah mengoperasikan event streaming (Kafka) dengan SLA tinggi.Siap mengelola orchestrator (Temporal) atau memanfaatkan managed service.
Kebutuhan Audit / ComplianceAudit trail harus dibangun manual via tracing & log sink.Audit trail dan event history tersedia out-of-the-box pada engine.

Mulailah dengan Choreography jika transaksi terdistribusi Anda hanya melibatkan 2–3 langkah sederhana dan sistem Anda sudah berbasis event-driven. Beralihlah ke Orchestration ketika logika kompensasi mulai menghasilkan kode penanganan error yang mendominasi logika bisnis utama, atau saat debugging transaksi lintas layanan menghabiskan waktu investigasi tim secara signifikan.