Dapr the Swiss army knife for developers you’ve never heard of
I recently had the opportunity to implement Dapr in one of the applications that is part of a security platform my team is developing. In this story I will explain what Dapr is, where you could use it for, and what my findings are so far.
More and more developers build cloud native applications that consists of microservices. Building your (enterprise) application out of multiple microservices, brings a lot of benefits:
- The codebase of a single microservers is smaller than a whole monolithic application. This makes it easier to maintain the code with respect to the quality attributes described in ISO 25010
- Microservices can be written in different programming languages. For example, the microservices that support the everyday line-of-business processes can be written in C# and the data analytics services can be written in Python.
- Microservices can be scaled independently. If you are following the domain driven design priciples, this could mean that you could scale certain pieces of functionality in your application.
- If your microservices are developed well, the should be decoupled from each other, meaning you could release them separately. As you only have to release a single microservice, the release process is much faster and less complex.
These benefits come with a price. In order to have you microservice application working perfectly, you have to solve a couple of challenges:
- It is difficult to maintain eventual consistency. As microservices should be decoupled from each other, they should all have their own database. This means that a lot of data should be replicated between microservices. You could use a publish/subscribe pattern that automatically that sends out events when you add a record to the database of microservice X and need that record on microservice Y as well.
- Service invocation is hard as communications within you microservices network could fail. You could implement a lot of logic here that will execute all the retries etc. Aside from al the retry patterns that you need to implement, you also should take care of encryption of data in transit.
- Your microservices are logging locally and as programming languages can be different, the log is not standardised.
All of the above challenges can be solved but most likely you will need a lot of dependancies in your code in order to solve them. This will make the maintenance of your microservice more difficult and in the worst scenario can lead to “shaddow-coupling” of your microservices — the situation where you have to update a dependency in de codebase of each of your microservice in order to have the whole application working.
Dapr
According to the Dapr website, Dapr is a portable, event-driven runtime that makes it easy for enterprise developers to build resilient, stateless and stateful microservice applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks. Dapr is a solution that will make building applications with a microservice architecture much more easy.
Dapr consists of a rich set of buildingblocks that will help developers in building microservices. Applications that make use of Dapr can implement one or multiple buildingblocks; so it’s not required to implement them all.
A building block is an abstract implementation of a certain pice of functionality that can be configured centrally. For example, your application could implement the Dapr publish/subscribe building block and centrally is configured that publish/subscribe is done using RabbitMQ. If you later decide to stop using RabbitMQ and go for Azure Service Bus, you only have to update the central configuration. The code of your microservices doesn’t require any changes.
- Service to Service invocation — Resilient service-to-service invocation enables method calls, including retries, on remote services wherever they are located in the supported hosting environment.
- State Management — With state management for storing key/value pairs, long running, highly available, stateful services can be easily written alongside stateless services in your application. The state store is pluggable and can include Azure CosmosDB, Azure SQL Server, PostgreSQL, AWS DynamoDB or Redis among others.
- Publish and Subscribe — Publishing events and subscribing to topics
- Resource bindings and triggers — Resource bindings with triggers builds further on event-driven architectures for scale and resiliency by receiving and sending events to and from any external source such as databases, queues, file systems, etc.
- Actors — A pattern for stateful and stateless objects that make concurrency simple with method and state encapsulation. Dapr provides many capabilities in its actor runtime including concurrency, state, life-cycle management for actor activation/deactivation and timers and reminders to wake-up actors.
- Observability — Dapr emit metrics, logs, and traces to debug and monitor both Dapr and user applications. Dapr supports distributed tracing to easily diagnose and serve inter-service calls in production using the W3C Trace Context standard and Open Telemetry to send to different monitoring tools.
- Secrets — Dapr provides secrets management and integrates with public cloud and local secret stores to retrieve the secrets for use in application code.
How does Dapr work?
Dapr works with a sidecar architecture. This sidecar can be a process that is running next to your application or a container that gets deployed in your kubernetes cluster.
The sidecar holds the implementation of the building blocks and is accessible using HTTP/gRPC. If, for example, you would like to send out an event to the publish/subscribe mechanism of Dapr, you can execute a HTTP request to the sidecar container/process and the sidecar will make sure your event is handled correctly.
All buildingblocks can be configured. In kubernetes you could do this by deploying a component object which holds the configuration of the building block. Have a look at the documentation of the building blocks over here.
Why should you implement Dapr?
Dapr makes it easy to deal with the challenges that come with a microservice architecture. By using Dapr the most important challenges in a microservice architecture can be solved in a couple lines of code. This normally would require developers to implement dependancies, and build a lot of logic around them so they could work in a microservice architecture.
My findings (so far)
I recently implemented Dapr in one of the applications that is part of a security platform my team and I are developing. After the implementation I came to the following conclusions:
- Dapr really makes a developer life much more easy when it comes to building microservices! A lot of patterns and tools that you would normally implement are abstracted away. This allows the development team to focus more on business logic.
- Dapr helps with the local development and testing scenario’s. Local development environments can be different from productional environments. For example, the productional application runs in Microsoft Azure where it can leverage an Azure Service bus. On a local development environment the Azure Service Bus is not available. Changing the configuration of a building block makes it possible to switch out Azure Service Bus for RabbitMQ on local development environments.
- Dapr is awesome — it really is.
Links
During my implementation I found the following documentation/links to be helpfull:
- Dapr building block developer documentation: https://docs.dapr.io/developing-applications/building-blocks/
- Dapr Components Github https://github.com/dapr/components-contrib (if you can’t find all the details in the Dapr Docs, just read the code of the component you are implemting :-))
- Dapr implemented in ASP.NET code by Toon van Houte https://github.com/ToonVanhoutte/dapr-samples/blob/master/README.md
I hope this story motivates you to have a look at Dapr as it is really opened my eyes!
Happy developing!
— Jeroen Niesen