Introduction to Dapr and how its benefits to EDA

Harshil Kansagara
7 min readSep 4, 2023

--

Event-driven architecture (EDA) is an increasingly popular option to help companies process complex data streams in real-time. Event-driven architecture has been gaining traction in the software development world lately, and with good reason. Kindly go through this link to get more information on EDA's architectural components and benefits.

This article discusses the concepts related to Dapr, and why it’s useful.

What’s an Event-Driven Application?

The control flow of an event-driven application is determined by events. It is a decoupled architecture application that can listen to, process, and transmit events. In a typical event-driven architecture, the application is made up of various components that communicate with one another via events. Events are messages that are exchanged between application components. Events can be triggered by user interactions or external service triggers via webhook.

Key Concepts: Publishers, Subscribers, Sources and Sinks

Unlike traditional architectures, which respond to user-initiated queries and deal with data in batches that are added and changed at predefined intervals, event-driven architecture-based applications respond to events as they happen. The fundamental principles of event-driven architecture are publishers, subscribers, sources, and event consumers (sinks).

A publisher is a component responsible for capturing event data and storing it in a repository. The subscriber is responsible for consuming the event data and responding to the event. A source denotes the component where the event originates, and sinks represent the destinations where the event subscribers send data.

What’s Dapr?

Dapr, or Distributed Application Runtime, is a new way to build modern distributed applications.

Dapr addresses a major issue that exists in modern distributed applications: complexity.

Dapr greatly simplifies the plumbing behind distributed applications by using a pluggable component architecture. It acts as a dynamic glue, connecting your application to infrastructure capabilities provided by the Dapr runtime.

Consider requiring one of your services to be stateful. What would your design be? You could write your own code to target a state store like Redis Cache. However, Dapr provides state management capabilities out of the box. Your service invokes a Dapr state management building block that dynamically binds to a state store component via a Dapr component configuration YAML file. Dapr includes a number of pre-built state store components, including Redis. With this model, your service delegates state management to the Dapr runtime. There is no SDK, library, or direct reference to the underlying component in your service. You can even switch from Redis to MySQL or Cassandra without changing any code.

Dapr High Level Architecture design
Dapr High-Level Architecture Design

In the above figure at the top row, note how Dapr provides language-specific SDKs for popular development platforms.

While language-specific SDKs enhance the developer experience, Dapr is platform-agnostic. Dapr’s programming model exposes capabilities under the hood via standard HTTP/gRPC communication protocols. Any programming platform can call Dapr via its native HTTP and gRPC APIs.

The blue boxes across the centre of the figure represent the Dapr building blocks. Each exposes a distributed application capability that your application can consume.

The bottom row highlights the portability of Dapr and the diverse environments across which it can run.

Dapr Architecture

Let me give you a closer look at how Dapr works.

Building blocks

A building block encapsulates a distributed infrastructure capability. You can access the functionality through the HTTP or gRPC APIs.

Dapr building blocks

Below describes the infrastructure services provided by each block:

  1. State Management: Support contextual information for long-running stateful services.
  2. Service Invocation: Invoke direct, secure service-to-service calls using platform-independent protocols and well-known endpoints.
  3. Publish and Subscribe: Implement secure, scalable pub/sub messaging between services.
  4. Bindings: Trigger code from events raised by external resources with bi-directional communication.
  5. Observability: Monitor and measure message calls across networked services
  6. Secrets: Securely access external secret stores.
  7. Actors: Encapsulates logic and data in reusable actor objects.

Building blocks abstract the implementation of distributed application capabilities from your services.

Dapr Building Block Integration

Dapr components that provide the concrete implementation for each resource are invoked by building blocks. Your service’s code is only aware of the building block. It does not require any external SDKs or libraries; Dapr handles the plumbing for you. Each building block is self-contained. In your application, you can use one, some, or all of them. Dapr’s building blocks include industry best practices such as comprehensive observability as an added value.

Components

While building blocks expose an API to invoke distributed application capabilities, Dapr components provide concrete implementation to make it happen.

Consider, the Dapr state store component. It provides a uniform way to manage the state for CRUD operations. Each component provides the necessary implementation through a common state management interface:

type Store interface {
Init(metadata Metadata) error
Delete(req *DeleteRequest) error
BulkDelete(req []DeleteRequest) error
Get(req *GetRequest) (*GetResponse, error)
Set(req *SetRequest) error
BulkSet(req []SetRequest) error
}

Without any change to your service code, you could switch between any of the following Dapr state components:

  • AWS DynamoDB
  • Aerospike
  • Azure Blob Storage
  • Azure CosmosDB
  • Azure Table Storage
  • Cassandra
  • Cloud Firestore (Datastore mode)
  • CloudState
  • Couchbase
  • Etcd
  • HashiCorp Consul
  • Hazelcast
  • Memcached
  • MongoDB
  • PostgreSQL
  • Redis
  • RethinkDB
  • SQL Server
  • Zookeeper

Sidecar architecture

Dapr can coexist with your service by using a sidecar, which allows it to operate in its memory process or container. Sidecar provides isolation and encapsulation as they aren’t part of the service, but connected to it. This separation enables each to have its own runtime environment and be built upon different programming platforms.

Dapr Sidecar Architecture

Hosting environments

Dapr has cross-platform support and can run in many different environments. These environments include Kubernetes, a group of VMs, or edge environments such as Azure IoT Edge.

The self-hosted mode is the simplest way to get started with local development. The microservices and Dapr sidecars run in separate local processes in self-hosted mode, without the use of a container orchestrator such as Kubernetes.

Fig shows an application and Dapr hosted in two separate memory processes communication via HTTP or gRPC.

Self-hosted Dapr Sidecar

By default, Dapr installs Docker containers for Redis and Zipkin to provide default state management and observability. If you don’t want to install Docker on your local machine, you can even run Dapr in self-hosted mode without any Docker containers. However, you must install default components such as Redis for state management and pub/sub manually.

Dapr also runs in containerized environments such as Kubernetes. The figure shows Dapr running in a separate side-car container along with the application container in the same Kubernetes pod.

Kubernetes-Hosted Dapr sidecar

Dapr performance considerations

As previously mentioned, Dapr exposes a sidecar architecture to decouple your application from distributed application capabilities. Invoking a Dapr operation requires at least one out-of-process network call.

Dapr traffic patterns

Calls between Dapr sidecars are always made with gRPC, which delivers high performance and small binary payloads. In most cases, the additional overhead should be sub-millisecond.

To increase performance, developers can call the Dapr building blocks with gRPC.

Dapr and service meshes

Service mesh is another quickly evolving technology for distributed applications.

A service mesh is a configurable infrastructure layer that includes built-in capabilities to handle service-to-service communication, resiliency, load balancing, and telemetry capture. It shifts responsibility for these concerns out of the services and into the service mesh layer. Like Dapr, a service mesh also follows a sidecar architecture.

Service mesh with a sidecar

The preceding diagram depicts how messages are intercepted by a sidecar proxy that runs concurrently with each service. Each proxy can be configured with service-specific traffic rules. It comprehends messages and can route them through your services and out into the world.

As a result, the question is, “Is Dapr a service mesh?”.

While both use a sidecar architecture, each technology has a different purpose. Dapr provides distributed application features. A service mesh provides a dedicated network infrastructure layer.

The Dapr online documentation covers Dapr and service mesh integration.

Summary

This chapter introduced you to Dapr, a Distributed Application Runtime

At its core, Dapr helps reduce the inherent complexity of distributed microservice applications. It’s built upon a concept of building block APIs. Dapr building blocks expose common distributed application capabilities, such as state management, service-to-service invocation, and pub/sub messaging. Dapr components lie beneath the building blocks and provide the concrete implementation for each capability. Applications bind to various components through configuration files.

--

--