Orchestrating Reliable Distributed Workflows in .NET 8 with Temporal.io

Mahmut Sarıkaya 4 dk okuma 12 Görüntülenme 0
Orchestrating Reliable Distributed Workflows in .NET 8 with Temporal.io

Why distributed workflows matter in modern .NET applications

Imagine a checkout system that must coordinate inventory reservation, payment processing, and shipping across three independent microservices. If any step fails, the entire transaction should roll back without leaving orphaned records. Traditional message queues can deliver events, but they do not guarantee the end‑to‑end state consistency required for such orchestrations. Temporal.io fills that gap by providing a durable, stateful workflow engine that runs on top of .NET 8 and the C# SDK.

System requirements and quick start with Temporal.io

Temporal runs as a Docker container, so a machine with Docker 20.10+ and at least 4 GB RAM is sufficient for development. Install the .NET 8 SDK (version 8.0.100 or later) and add the Temporal C# client package:

dotnet new console -n TemporalDemo && cd TemporalDemo dotnet add package Temporalio.SDK && docker pull temporalio/auto-setup docker run -d --name temporal -p 7233:7233 temporalio/auto-setup

After the container is up, the Temporal service listens on port 7233. The next step is to define a workflow class that models the business process.

Defining a workflow with the C# SDK

A workflow is a deterministic state machine. The C# SDK uses attributes to mark the entry point and activity calls. Below is a minimal order‑fulfillment workflow that demonstrates sequential activity execution and result handling.

using Temporalio\Client; using Temporalio\Workflow; public class OrderWorkflow : WorkflowBase { [WorkflowRun] public async Task RunAsync(string orderId) { var paymentResult = await Workflow.ExecuteActivityAsync<ProcessPaymentActivity>(orderId, new ActivityOptions { StartToCloseTimeout = TimeSpan.FromSeconds(30) }); var inventoryResult = await Workflow.ExecuteActivityAsync<ReserveInventoryActivity>(orderId); var shipmentResult = await Workflow.ExecuteActivityAsync<CreateShipmentActivity>(orderId); // Combine results or raise an exception if any step fails 

The workflow code is pure C#; there is no need to write DSLs or YAML files. All state is persisted by Temporal, so if the process crashes, it resumes automatically from the last completed activity.

Implementing robust activities

Activities contain the actual business logic and can be written as regular async methods. Because they run in separate worker processes, you can scale them independently. Here is an example of a payment activity that retries on transient network errors.

public class ProcessPaymentActivity { public async Task<bool> ExecuteAsync(string orderId) { try { // Simulate external payment gateway call await Task.Delay(500); return true; } catch (HttpRequestException) { // Let Temporal handle retry according to ActivityOptions throw; } } }

Notice the absence of explicit retry loops. Temporal respects the RetryPolicy defined in ActivityOptions, automatically re‑executing the activity up to the configured limit.

Handling failures and compensation

When an activity throws an exception that exceeds the retry limit, Temporal aborts the workflow and triggers a compensation path if you have defined one. Compensation activities undo side effects, such as refunding a payment or releasing reserved inventory. The pattern mirrors the Saga design but is managed centrally by the workflow engine.

public class RefundPaymentActivity { public async Task ExecuteAsync(string orderId) { // Call payment gateway to reverse transaction await Task.Delay(200); } }

In the workflow you can catch the failure and invoke compensation:

try { await Workflow.ExecuteActivityAsync<ProcessPaymentActivity>(orderId); } catch (Exception) { await Workflow.ExecuteActivityAsync<RefundPaymentActivity>(orderId); throw; }

Microservices orchestration at scale

Temporal workers are just .NET console apps, so each microservice can host its own set of activities. Deploy three workers—PaymentWorker, InventoryWorker, ShippingWorker—each listening on the same Temporal namespace. The workflow dispatcher routes activity calls to the appropriate worker based on task queue names. This decouples services while preserving a single source of truth for the overall process.

Scaling is as simple as increasing the replica count of a worker container. Because Temporal persists state, adding or removing workers does not affect in‑flight workflows.

Observability and monitoring

Temporal emits detailed metrics via OpenTelemetry. Enable the exporter in your .NET worker configuration to push data to Prometheus or Azure Monitor:

services.AddTemporalClient(options => { options.TargetEndpoint = "localhost:7233"; options.Namespace = "default"; }).AddOpenTelemetryMetrics();

Dashboards can show workflow latency, activity success rates, and queue backlogs, helping you spot bottlenecks before they impact customers.

Conclusion

By combining .NET 8, the Temporal C# SDK, and containerized workers, developers gain a deterministic, fault‑tolerant platform for orchestrating distributed workflows. The approach eliminates ad‑hoc retry code, provides built‑in compensation, and scales seamlessly across microservices. For any system where business logic spans multiple services—order processing, IoT pipelines, or data enrichment—Temporal offers a pragmatic path to reliability without sacrificing the productivity of C# developers.

Author: Mahmut Sarıkaya — sarikayadev.com

Sources

Temporal Documentation, Microsoft .NET Documentation, DZone Temporal.io Tutorial

Etiketler: #Temporal.io #.NET 8 #C# SDK #distributed workflow #microservices orchestration
Paylaş:
M

Yazar

Mahmut Sarıkaya

yazılım Geliştirici

Yorumlar

Henüz yorum yok. İlk yorumu siz yapın!

Yorum Bırakın

6 + 7 =