Skip to main content
  1. Posts/

Dapr: Cloud-Native Runtime for Simplified Microservices

sun.ao
Author
sun.ao
I’m sun.ao, a programmer passionate about technology, focusing on AI and digital transformation.
Table of Contents

Introduction
#

In microservice development, developers often face the complexity of distributed systems: service discovery, state management, messaging, resilient retries… These problems are unrelated to business logic but consume significant development time.

Dapr (Distributed Application Runtime) was created to address this. It abstracts these distributed system challenges into standardized “building blocks,” allowing developers to focus on business code rather than infrastructure.

What is Dapr?
#

Dapr is a portable, event-driven runtime that provides distributed capabilities to applications through a sidecar architecture. Simply put:

Dapr is like an “all-purpose assistant” that handles the dirty work in microservices. You just tell it “what to do,” without worrying about “how to do it.”

Core Features
#

FeatureDescription
Language AgnosticSupports Go, Java, Python, .NET, JavaScript, and any language
Environment PortableRuns locally, on Kubernetes, edge devices, anywhere
Sidecar ArchitectureNon-invasive integration, no application code changes
Modular DesignUse building blocks as needed, flexible composition
Cloud Native StandardsFollows OpenTelemetry, CloudEvents, and other standards

Architecture Overview
#

Sidecar Pattern
#

Dapr uses the Sidecar pattern, running alongside your application process:

┌─────────────────────────────────────┐
│           Application Container      │
│  ┌───────────────────────────────┐  │
│  │     Business Code (Any Lang)   │  │
│  └───────────────┬───────────────┘  │
│                  │ HTTP/gRPC        │
│  ┌───────────────▼───────────────┐  │
│  │         Dapr Sidecar          │  │
│  │   (State, Pub/Sub, Bindings)  │  │
│  └───────────────────────────────┘  │
└─────────────────────────────────────┘

Applications communicate with the Dapr Sidecar via HTTP or gRPC, while Dapr handles interaction with underlying infrastructure.

A Request Flow
#

Taking service invocation as an example:

Service A                 Dapr Sidecar A                Dapr Sidecar B             Service B
  │                            │                              │                          │
  │ HTTP POST /v1.0/invoke     │                              │                          │
  │───────────────────────────▶│                              │                          │
  │                            │ Service Discovery + LB       │                          │
  │                            │─────────────────────────────▶│                          │
  │                            │                              │  HTTP POST /api/method   │
  │                            │                              │─────────────────────────▶│
  │                            │                              │                          │
  │                            │                              │     HTTP Response        │
  │                            │                              │◀─────────────────────────│
  │                            │     mTLS Encryption          │                          │
  │                            │◀─────────────────────────────│                          │
  │     HTTP Response          │                              │                          │
  │◀───────────────────────────│                              │                          │

Core Building Blocks
#

Dapr provides multiple building blocks, each solving a distributed system problem:

1. Service Invocation
#

Problem: How do services communicate? How to handle service discovery and load balancing?

Dapr Solution: Invoke services via namespace + app ID.

# Call the checkout method of order-service
POST http://localhost:3500/v1.0/invoke/order-service/method/checkout
// Go example
client, _ := dapr.NewClient()
resp, err := client.InvokeMethod(ctx, "order-service", "checkout", "post")

Benefits:

  • Automatic service discovery
  • Built-in retries and circuit breaking
  • mTLS encryption
  • Namespace isolation support

2. State Management
#

Problem: How to store and retrieve state? How to handle concurrent conflicts?

Dapr Solution: Unified state store API supporting multiple databases.

# Save state
POST http://localhost:3500/v1.0/state/store
[
  {"key": "user-123", "value": {"name": "John", "age": 25}}
]

# Get state
GET http://localhost:3500/v1.0/state/store/user-123
// Go example
err := client.SaveState(ctx, "store", "user-123", []byte(`{"name":"John"}`))
item, err := client.GetState(ctx, "store", "user-123")

Supported Storage Backends: Redis, PostgreSQL, MongoDB, Azure Cosmos DB, AWS DynamoDB, and more.

3. Pub/Sub
#

Problem: How to implement asynchronous messaging? How to decouple services?

Dapr Solution: Declarative subscriptions supporting multiple message queues.

# Subscription configuration
apiVersion: dapr.io/v1alpha1
kind: Subscription
metadata:
  name: order-subscription
spec:
  topic: orders
  pubsubname: pubsub
  route: /orders
scopes:
  - order-service
# Publish message
POST http://localhost:3500/v1.0/publish/pubsub/orders
{"orderId": "12345", "status": "created"}

Supported Message Queues: Redis, Kafka, RabbitMQ, Azure Service Bus, AWS SNS/SQS, and more.

4. Bindings
#

Problem: How to integrate with external systems (databases, queues, APIs)?

Dapr Solution: Unified input/output interface through binding components.

# Output binding: send email
POST http://localhost:3500/v1.0/bindings/email
{
  "operation": "create",
  "data": {
    "to": "user@example.com",
    "subject": "Order Confirmation",
    "body": "Your order has been created"
  }
}

Supported Bindings: MySQL, PostgreSQL, Kafka, RabbitMQ, Twilio, SendGrid, HTTP, and 100+ components.

5. Actors
#

Problem: How to handle stateful concurrent entities?

Dapr Solution: Implements the virtual Actor model, simplifying concurrent programming.

// C# example
public interface IOrderActor : IActor
{
    Task<Order> GetOrderAsync();
    Task ProcessPaymentAsync(Payment payment);
}

public class OrderActor : Actor, IOrderActor
{
    public async Task<Order> GetOrderAsync()
    {
        var orderId = this.Id.GetId();
        return await this.StateManager.GetStateAsync<Order>("order");
    }
}

Use Cases:

  • Game player entities
  • IoT device state management
  • Shopping carts, inventory management

6. Distributed Lock
#

Problem: How to implement mutual exclusion in distributed environments?

Dapr Solution: Provides distributed lock API.

// Go example
lock := &dapr.LockRequest{
    StoreName:  "lockstore",
    ResourceID: "order-123",
    LockOwner:  "service-a",
    ExpiryInSeconds: 60,
}

resp, err := client.Lock(ctx, lock)
if resp.Success {
    // Execute critical section
    client.Unlock(ctx, lock)
}

7. Workflow
#

Problem: How to orchestrate long-running business processes?

Dapr Solution: Provides workflow engine with state persistence.

// C# example
public class OrderWorkflow : Workflow<Order, OrderResult>
{
    public override async Task<OrderResult> RunAsync(WorkflowContext ctx, Order order)
    {
        // Step 1: Check inventory
        var inventory = await ctx.CallActivityAsync<bool>(nameof(CheckInventory), order);

        // Step 2: Process payment
        var payment = await ctx.CallActivityAsync<bool>(nameof(ProcessPayment), order);

        // Step 3: Ship order
        await ctx.CallActivityAsync(nameof(ShipOrder), order);

        return new OrderResult { Success = true };
    }
}

Quick Start
#

Local Development Environment
#

# Install Dapr CLI
winget install Dapr.Dapr  # Windows
brew install dapr/tap/dapr  # macOS

# Initialize local environment
dapr init

# Verify installation
dapr --version

Run Your First Application
#

# Start application (with Dapr sidecar)
dapr run --app-id myapp --app-port 3000 -- dotnet run

# Or use Docker Compose
docker-compose up -d

Kubernetes Deployment
#

# Install Dapr to cluster
dapr init -k

# Deploy application
kubectl apply -f deployment.yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    metadata:
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "myapp"
        dapr.io/app-port: "3000"

Component Configuration
#

Dapr configures components via YAML, achieving decoupling from infrastructure:

# Redis state store
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
    - name: redisHost
      value: localhost:6379
    - name: redisPassword
      secretKeyRef:
        name: redis-secret
        key: password
# Kafka pub/sub
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsub
spec:
  type: pubsub.kafka
  version: v1
  metadata:
    - name: brokers
      value: "kafka:9092"
    - name: consumerGroup
      value: "myapp-group"

Real-World Use Cases
#

Case 1: E-commerce Order System
#

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│ User Service │────▶│Order Service │────▶│Inventory Svc │
└──────────────┘     └──────┬───────┘     └──────────────┘
                     ┌──────▼───────┐
                     │Payment Service│
                     └──────────────┘

Dapr Building Blocks:
- Service Invocation: Inter-service communication
- State Management: Shopping cart, order status
- Pub/Sub: Order event notifications
- Workflow: Order processing flow
- Distributed Lock: Inventory deduction

Case 2: IoT Data Processing
#

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│Device Gateway│────▶│Data Processor│────▶│Storage Service│
└──────────────┘     └──────────────┘     └──────────────┘

Dapr Building Blocks:
- Bindings: MQTT input binding
- Pub/Sub: Device event broadcasting
- State Management: Device state cache
- Actors: Device entity management

Comparison with Other Solutions
#

FeatureDaprSpring CloudIstio
Language SupportAll languagesJava-firstAll languages
Architecture PatternSidecarLibrary integrationSidecar
Functional ScopeApp + Infrastructure layerApplication layerInfrastructure layer
Learning CurveMediumLowerHigher
Operational ComplexityMediumLowerHigher
State ManagementBuilt-inRequires integrationNone
Actor ModelSupportedNot supportedNot supported

Best Practices
#

1. Separate Local Development from Production
#

# Local development: Use Redis
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  metadata:
    - name: redisHost
      value: localhost:6379
# Production: Use Azure Cosmos DB
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.azure.cosmosdb
  metadata:
    - name: url
      value: https://xxx.documents.azure.com:443/
    - name: masterKey
      secretKeyRef:
        name: cosmos-secret
        key: key

2. Use Secrets Management
#

# Reference Kubernetes Secrets
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  metadata:
    - name: redisPassword
      secretKeyRef:
        name: redis-secret
        key: password
auth:
  secretStore: kubernetes

3. Observability Configuration
#

# Configure tracing sampling rate
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: tracing
spec:
  tracing:
    samplingRate: "1"
    zipkin:
      endpointAddress: "http://zipkin:9411/api/v2/spans"

Summary
#

Dapr makes distributed system development simple through building block abstraction:

Core Advantages
#

  1. Reduced Complexity: Distributed challenges become API calls
  2. Language Agnostic: Teams can use the most suitable tech stack
  3. Portability: One codebase, runs anywhere
  4. Cloud Native Standards: Follows industry standards, avoids vendor lock-in

Use Cases
#

  • Microservice architecture transformation
  • Hybrid cloud deployment
  • Event-driven architecture
  • IoT edge computing

Resources
#

If you’re building microservice applications, Dapr is worth a try! It lets you focus on business logic while Dapr handles the complexity of distributed systems.

Related articles