Sarıkaya Dev Logo

Building Resilient .NET 8 Microservices with Dapr Sidecars

Mahmut Sarıkaya 4 min read 7 Views 0
Building Resilient .NET 8 Microservices with Dapr Sidecars

Why Resilience Matters in .NET 8 Microservices

Enterprises that migrated 40% of their workloads to microservices between 2022 and 2024 reported a 30% reduction in mean time to recovery, but only when the services were built with explicit resilience patterns. .NET 8 introduces native support for minimal APIs, improved async streams, and built‑in health‑check middleware, yet the real challenge lies in coordinating dozens of independently deployable units while preserving data consistency and fault tolerance.

Integrating Dapr Sidecars into a .NET 8 Project

Dapr (Distributed Application Runtime) runs as a sidecar process next to each service container. The sidecar exposes HTTP and gRPC endpoints that your .NET code can call without embedding third‑party SDKs directly. To add Dapr, include the Microsoft.Dapr.Client NuGet package and enable the sidecar in your Docker compose file.

docker compose up -d --build
# The Dapr CLI injects a sidecar for each service defined with "dapr: true"

In Program.cs, register the Dapr client as a singleton so that all controllers share the same connection.

using Dapr.Client;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<DaprClient>(sp => new DaprClientBuilder().Build());
var app = builder.Build();
app.MapGet("/health", () => Results.Ok("OK"));
app.Run();

Stateful Pub/Sub with Dapr and Redis

Dapr abstracts the underlying broker, allowing you to switch from Redis to Azure Service Bus with a single component change. Define a pub/sub component in YAML and reference it by name in your C# code.

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsubredis
spec:
  type: pubsub.redis
  version: v1
  metadata:
  - name: redisHost
    value: "redis:6379"

Publishing an event is a one‑liner:

await daprClient.PublishEventAsync("pubsubredis", "order.created", new { OrderId = 1234, Amount = 99.95 });

Subscribing is handled by decorating an endpoint with the Dapr subscription attribute. Dapr guarantees at‑least‑once delivery, and the sidecar retries automatically if your service returns a non‑2xx status.

[Topic("pubsubredis", "order.created")]
public async Task<IResult> HandleOrderCreated(OrderDto order)
{
    // Business logic here
    return Results.Ok();
}

Secure Secret Management Across Environments

Hard‑coding connection strings is a recipe for breaches. Dapr’s secret store component lets you fetch secrets from Azure Key Vault, HashiCorp Vault, or Kubernetes secrets without changing application code.

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: secretstorekv
spec:
  type: secretstores.kubernetes
  version: v1
  metadata:
  - name: namespace
    value: "default"

Retrieve a secret at runtime:

var secret = await daprClient.GetSecretAsync("secretstorekv", "DbConnectionString");
var connectionString = secret["value"];

Because the secret request travels through the sidecar, the application never touches the underlying vault API, reducing the attack surface.

Observability: Tracing, Metrics and Logs

Dapr automatically emits OpenTelemetry spans for every inbound and outbound call. Pair the sidecar with a Jaeger or Zipkin collector, and enable the .NET 8 built‑in logging provider to correlate logs with trace IDs.

docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
  -p 16686:16686 -p 9411:9411 jaegertracing/all-in-one:1.53

In your appsettings.json, turn on OpenTelemetry exporters:

{
  "Logging": { "LogLevel": { "Default": "Information" } },
  "OpenTelemetry": {
    "Tracing": { "Exporter": "jaeger", "JaegerEndpoint": "http://localhost:14268/api/traces" }
  }
}

The sidecar also publishes Prometheus metrics on /v1.0/metrics, which Grafana can scrape to visualize request latency, retry counts, and state store hit ratios.

Deploying the Full Stack with Docker Compose

A minimal compose file wires together the .NET API, Redis, Dapr sidecars, and Jaeger. The example below runs everything on a single developer laptop, but the same definitions can be promoted to Kubernetes with minor adjustments.

version: "3.9"
services:
  api:
    build: .
    ports:
      - "5000:8080"
    environment:
      - DAPR_HTTP_PORT=3500
    dapr:
      app-id: order-service
      app-port: 8080
      config: "dapr-config.yaml"
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
  jaeger:
    image: jaegertracing/all-in-one:1.53
    ports:
      - "16686:16686"
      - "9411:9411"

Run docker compose up -d and verify the health endpoint at http://localhost:5000/health. The Dapr dashboard (available at http://localhost:3500) will show registered components, subscriptions, and real‑time metrics.

Conclusion

Combining .NET 8’s modern language features with Dapr sidecars gives you a production‑ready foundation for stateful pub/sub, secret management, and end‑to‑end observability. By externalizing cross‑cutting concerns, developers can focus on business logic, while the sidecar handles retries, circuit breaking, and telemetry. The result is a microservice ecosystem that scales horizontally, recovers quickly from failures, and complies with security best practices.

Sources

Microsoft Docs – .NET 8 Release Notes; Dapr Documentation – Components Overview; Azure Architecture Center – Designing Resilient Microservices

Author: Mahmut Sarıkaya — sarikayadev.com

Tags: #dotnet 8 #dapr #microservices #pub/sub #state management
Share:
M

Written by

Mahmut Sarıkaya

Software Developer

Comments

No comments yet. Be the first to share your thoughts!

Leave a Comment

2 + 1 =