Erick Kurniawan

Microsoft Certified Trainer, Microsoft MVP for +15y on Developer Technologies

Transaksi Terdistribusi pada .NET Microsevices dengan Saga Pattern (Part 3)

Pada Part 2, kita membahas choreography-based saga, yaitu pendekatan dimana tiap service saling bereaksi terhadap event tanpa adanya pengendali pusat (orkestrator). Model itu cocok untuk arsitektur event-driven, tetapi ketika workflow menjadi semakin rumit, semakin sulit pula menelusuri state, cabang alur, retry, timeout, dan kompensasi yang tersebar di banyak service. Untuk workflow yang lebih kompleks, model orchestration sering lebih cocok karena ada satu komponen pusat yang menangani koordinasi transaksi, menyimpan atau menafsirkan state tiap task, dan memicu kompensasi saat terjadi kegagalan. Azure bahkan memiliki contoh referensi resmi untuk orchestration-based saga berbasis Azure Durable Functions dalam arsitektur serverless.

Pada artikel ini kita akan memahami bagaimana cara kerja orchestration-based saga, mengapa pendekatan ini lebih mudah diaudit dan di-debug dibanding model choreography, serta bagaimana memetakan konsep tersebut ke .NET dan Azure Durable Functions. Model orchestration mempunyai centralized controller atau orchestrator yang menentukan urutan langkah, memberi tahu participant operasi apa yang harus dilakukan, lalu menangani failure recovery dengan compensating transactions.

Mengapa Menggunakan Model Orchestration?

Salah satu tantangan terbesar pada choreography adalah ketika logika bisnis bertambah kompleks dan tersebar. Service A menerbitkan event, service B merespons, service C menerbitkan kompensasi, service D memperbarui status, dan ketika alurnya mulai bercabang, ada timeout, atau ada kebutuhan audit trail yang kuat, tracing end-to-end bisa menjadi sulit. Model choreography sendiri cocok untuk workflow yang sederhana, tetapi dapat menjadi membingungkan ketika anda menambah langkah atau service yang baru. Sebaliknya, orchestration memberikan pemisahan tanggung jawab yang lebih jelas karena alur bisnis dikendalikan dari satu tempat terpusat.

Dalam konteks implementasi di layanan Azure, pendekatan ini menjadi sangat menarik karena Durable Functions dirancang untuk workflow stateful dan long-running process. Pada dokumentasi penerapan saga di layanan Azure menekankan bahwa solusi tersebut bertujuan menyederhanakan pengalaman developer dengan memanfaatkan model pemrograman Azure Functions dan Durable Functions sehingga developer dapat fokus pada business logic peserta saga, sementara detail seperti state management, checkpointing, restart saat gagal, retry, timeout, idempotency, dan observability didukung oleh arsitektur yang dirancang untuk itu.

Apa itu Orchestration-Based Saga?

Pada orchestration-based saga, ada satu komponen pusat yaitu orchestrator yang memegang flow dari awal sampai akhir. Ketika saga dimulai, orchestrator akan memutuskan langkah pertama yang harus dijalankan, menunggu hasilnya, lalu menentukan langkah berikutnya. Jika ada kegagalan, orchestrator akan memanggil kompensasi untuk membatalkan efek bisnis dari langkah-langkah yang sudah sukses.

Secara praktis, ini berarti urutan bisnis tidak lagi “terbentuk” oleh event yang saling berantai, melainkan ditulis dengan lebih eksplisit di satu tempat. Bagi tim yang besar, ini membuat workflow lebih mudah dipahami, sebagai contoh jika ingin tahu apa yang terjadi saat checkout, cukup lihat orchestrator. Anda tidak perlu menelusuri banyak handler di banyak service hanya untuk memahami happy path dan failure path.

Contoh Checkout Order dengan Model Orchestrator

Kita gunakan kembali skenario checkout order dari Part 1 dan Part 2, tetapi kali ini alurnya dikendalikan oleh orchestrator. Secara garis besar, flow-nya menjadi seperti ini:

  1. Client mengirim request untuk membuat order.
  2. Orchestrator membuat atau memulai state saga, lalu memanggil langkah Create Order.
  3. Jika sukses, orchestrator memanggil Process Payment.
  4. Jika sukses, orchestrator memanggil Reserve Inventory.
  5. Jika sukses, orchestrator memanggil Schedule Shipping.
  6. Jika semua langkah sukses, orchestrator menandai saga sebagai selesai.
  7. Jika salah satu langkah gagal, orchestrator memanggil kompensasi seperti Refund Payment dan Cancel Order sesuai urutan bisnis yang dirancang.

Pendekatan ini sangat cocok untuk skenario yang kompleks, timeout, branch, dan proses yang panjang, karena satu komponen pusat dapat memegang state machine bisnis secara eksplisit.

Gambar diagram orchestration diatas menggambarkan central controller yang mengelola transaksi dan failure recovery.

Penerapan pada Layanan Azure

Pada implementasi Azure, orchestration saga menggunakan komponen berikut:

  • Azure Durable Functions sebagai Saga Orchestrator yang menyimpan state workflow dan mengoordinasikan langkah-langkah saga.
  • Azure Functions sebagai participant/activity yang menjalankan local transaction dan business logic domain.
  • Event Hubs sebagai platform streaming/messaging di referensi arsitektur tersebut.
  • Cosmos DB untuk penyimpanan data dan juga tracking/observability workflow pada sample tersebut.
  • Retry, Circuit Breaker, timeout handling, idempotency, dan observability sebagai concern penting yang dirancang langsung di solusi referensi.

Dari sisi .NET, ini sangat cocok karena Durable Functions memiliki model pemrograman yang memungkinkan developer menulis orchestrator dan activity dalam C#, sehingga pola saga dapat diterjemahkan menjadi workflow yang eksplisit dan terstruktur.

Contoh Membuat Orchestrator Object di Saga

Sebelum menulis orchestrator, kita biasanya menyiapkan objek input yang dibutuhkan untuk menjalankan satu instance saga. Misalnya:

public record CreateOrderSagaRequest(
Guid OrderId,
Guid CustomerId,
decimal TotalAmount
);

Objek ini digunakan untuk membantu menjaga kontrak masuk ke orchestrator tetap sederhana dan jelas. Dalam implementasi nyata, anda dapat menambahkan detail produk, alamat pengiriman, metadata tenant, atau correlation ID untuk kebutuhan tracing.

Contoh Orchestrator di .NET (Durable Functions)

Berikut ilustrasi contoh orchestrator saga dalam .NET menggunakan layanan Durable Functions pada Azure. Contoh ini adalah representasi sederhana dari cara orchestration biasanya ditulis, yaitu dengan memanggil langkah satu per satu, dan jika ada kegagalan, jalankan kompensasi yang diperlukan.

using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
public static class OrderSagaOrchestrator
{
[Function(nameof(OrderSagaOrchestrator))]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var request = context.GetInput<CreateOrderSagaRequest>();
try
{
await context.CallActivityAsync(nameof(CreateOrderActivity), request);
await context.CallActivityAsync(nameof(ProcessPaymentActivity), request);
await context.CallActivityAsync(nameof(ReserveInventoryActivity), request);
await context.CallActivityAsync(nameof(ScheduleShippingActivity), request);
}
catch
{
// Compensation flow
await context.CallActivityAsync(nameof(RefundPaymentActivity), request);
await context.CallActivityAsync(nameof(CancelOrderActivity), request);
throw;
}
}
}

Orchestrator digunakan untuk menangani urutan transaksi, menyimpan dan menafsirkan state task, lalu memicu kompensasi ketika failure terjadi. Azure sample juga menegaskan bahwa Durable Functions membantu mengurangi overhead pengelolaan stateful workflow, checkpointing, dan restart saat gagal, sehingga pola seperti di atas menjadi realistis untuk workflow di cloud.

Activity Function: satu langkah, satu tanggung jawab

Pada orchestration saga, setiap participant sebaiknya memiliki tanggung jawab yang jelas dan sempit. Misalnya CreateOrderActivity hanya bertugas membuat order, ProcessPaymentActivity hanya memproses pembayaran, dan seterusnya. Ini sejalan dengan prinsip local transaction dalam Saga Pattern, di mana tiap langkah menyelesaikan pekerjaannya secara atomik di boundary-nya sendiri. Local transaction menyelesaikan pekerjaannya dalam satu service, memperbarui database service tersebut, lalu menginisiasi langkah berikutnya melalui message atau keputusan orchestrator.

Berikut contoh activity sederhana:

public static class OrderActivities
{
[Function(nameof(CreateOrderActivity))]
public static async Task CreateOrderActivity(
[ActivityTrigger] CreateOrderSagaRequest request,
FunctionContext executionContext)
{
// Simpan order status = Pending
await Task.CompletedTask;
}
[Function(nameof(ProcessPaymentActivity))]
public static async Task ProcessPaymentActivity(
[ActivityTrigger] CreateOrderSagaRequest request,
FunctionContext executionContext)
{
// Authorize / capture payment
await Task.CompletedTask;
}
[Function(nameof(ReserveInventoryActivity))]
public static async Task ReserveInventoryActivity(
[ActivityTrigger] CreateOrderSagaRequest request,
FunctionContext executionContext)
{
// Reserve stock
await Task.CompletedTask;
}
[Function(nameof(ScheduleShippingActivity))]
public static async Task ScheduleShippingActivity(
[ActivityTrigger] CreateOrderSagaRequest request,
FunctionContext executionContext)
{
// Create shipment
await Task.CompletedTask;
}
}

Pemisahan seperti ini membuat tiap langkah lebih mudah diuji, diganti, dan dipahami secara independen. Desain ini membantu developer fokus pada business logic dari saga participants (services) tanpa harus menulis sendiri seluruh mekanisme workflow state dan restart.

Diagram diatas dapat menggambarkan alur orchestration: orchestrator mengevaluasi hasil setiap langkah, lalu memutuskan apakah alur diteruskan atau dikompensasi. Orchestrator bertugas mengelola seluruh transaksi dan recovery-nya. Compensating Transaction Pattern menjelaskan bahwa kegagalan di satu langkah perlu ditangani dengan aksi pembatalan dan dapat dipulihkan bila kompensasi sendiri ikut gagal.

Keuntungan orchestration saga

Pendekatan orchestration menawarkan sejumlah kelebihan yang sangat terasa ketika workflow mulai kompleks:

  1. Flow bisnis lebih eksplisit dan mudah dipahami. Orchestration lebih cocok untuk workflow yang kompleks karena tanggung jawab koordinasi terpusat.
  2. Menghindari cyclic dependency antar participant. Karena orchestrator yang mengatur alur, participant tidak perlu saling mengetahui terlalu banyak.
  3. Service logic lebih bersih. Participant cukup fokus pada local transaction-nya sendiri, sedangkan workflow end-to-end ada di orchestrator, hal ini yang disebut sebagai sebagai clear separation of responsibilities.
  4. Auditability dan debugging lebih mudah. Observability adalah concern yang penting, dan orchestration biasanya memudahkan tracking state transaksi per instance saga karena alurnya terpusat.
  5. Cocok untuk workflow panjang, retry, timeout, dan kompensasi yang banyak cabang. Dukungan terhadap transient failure handling, timeout handling, dan workflow stateful sebagai keunggulan menggunakan Durable Functions.

Kekurangan orchestration saga

  1. Ada kompleksitas koordinasi tambahan. Orchestration membutuhkan implementasi coordination logic, sehingga desainnya tidak benar-benar “gratis”.
  2. Muncul satu titik pusat koordinasi. Walaupun bukan berarti single point of failure yang rapuh jika platform-nya dikelola dengan baik, secara arsitektur orchestrator memang menjadi pusat flow.
  3. Coupling ke orchestrator lebih tinggi. Participant mungkin sederhana, tetapi perubahan besar pada alur bisnis sering mengharuskan pembaruan di orchestrator. Ini adalah trade-off dari proses sentralisasi workflow.
  4. Tetap tidak menghilangkan semua tantangan saga. Idempotency, duplicate execution, monitoring, dan kompensasi yang bisa gagal tetap harus dirancang.

Published by

Leave a comment