Building cloud-native apps and interactions between them or their parts is very complex. You need to glue it all together—and it can be a real challenge.
The good old days when it was just an application and a database are long gone, and you face many questions. How do we ensure sending messages or making calls to services are reliable and scalable? How do we trace distributed invocations and measure performance? How do we communicate with all sorts of message buses and other services external to our application?
The quality of solutions depends greatly on the team’s skills and experience. You need to write code that works like a clock with a million nuances (failure tolerance, observability, scalability, etc.). There are so many technologies that if you need a specialized analytics database, you might not find all the necessary libraries and they will be of poor quality. Also, most frameworks focus on the service being developed, not the system as a whole, and depend greatly on chosen programming languages.
It has become clear that we need a framework for the bigger picture. The framework that lets us focus on the business logic of our services, not the infrastructure glue code we write day to day. The framework that implements best practices in this new area of cloud application development. That is independent of the programming languages chosen for the applications.
The fast pace of development in cloud-native tools and Kubernetes made this kind of framework possible.
The Distributed Application Runtime (Dapr) was created.
What is Dapr?
Incepted by Microsoft employee, Yaron Schneider, from the high-level view, Dapr is the collection of building blocks you can use in the applications. These building blocks abstract away the details of realization and provide just the standard interfaces for doing things typical to any distributed application. In addition, it resolves cross-cutting concerns of observability, service discovery, resilience, and security. This has the greatest effect when used in microservice architectures, obviously, where you have to deal with all of the concerns mentioned in each service. Consider that they can be written in different programming languages and appreciate the level of complexity before you even start implementing business code.
Dapr is designed as a sidecar running along your application and providing all of its services via the API, as shown in Figure 2. It looks very logical.
You might have heard about Service Meshes. The concept is not exactly the same but pretty close. While Service Meshes make service discovery and communication easier, Dapr workflow offers much more. Just like with service meshes (take Istio for example) each service in your application gets its own Dapr sidecar injected into the Kubernetes pod. It literally acts as a gateway to a toolkit of helpful building blocks provided by the framework. Of course, you are still free to use external resources like before. For example, to access your PostgreSQL database or write to the Redis cache. You only use what you need and that’s the strongest side of this tool. It lets you migrate existing applications gradually at your own pace. Just add that sidecar and you are one step closer to the engineering nirvana.
Even though there are SDKs for certain languages, Microsoft Dapr at its core is language-independent. You can communicate with the sidecar either via the well-documented HTTP API or prefer gRPC for greater performance. Between themselves, Dapr sidecars communicate exclusively over mTLS-protected gRPC connections. They form a network of sidecars connected to their respective applications. When an application needs to communicate with another one, it sends the request to its own Dapr sidecar. It tells the recipient’s name, the method to execute, and the data. RPC over HTTP / gRPC with bells and whistles, you got it right. The Dapr network handles the rest—discovery, reliable delivery, mTLS certification, tracing, and much more.
The communication part is just one of the possibilities that become easy and transparent, but there’s more. That’s what building blocks are for.
Building blocks with Dapr
As we mentioned, the framework feature set isn’t limited by discovery and bridging requests between services. Far from it. Each sidecar acts as the gateway to the collection of APIs that are most demanded and common across distributed applications. Things like dynamic configuration, secrets, cryptography operations, publish-subscribe queues, cron-like jobs, bi-directional bindings to external services, distributed locking, actors, and workflows.
It’s literally the library of all this functionality sitting next to your service that you can access over HTTP or gRPC. If you thought of network access performance penalties, look at it this way. All of these services (or building blocks as Dapr calls them) are distributed in their nature so one way or another the penalty would apply, be it implemented inside your service or outside of it. On the bright side, you don’t spend precious time developing these from scratch and even not integrating any language-specific libraries you may find. There’s a huge collective effort behind one piece of the software that makes it better every day. No matter which programming language you choose for your service code, these are the same building blocks, so you don’t have to reimplement and coordinate implementations across the “technology zoo”.
Many blocks use some external server / tool / cloud service for work. For example, state management requires storage (PostgreSQL, Redis, or like). The publish-subscribe block needs the message broker (RabbitMQ, Kafka, etc.). These are obviously not included with Dapr Kubernetes. It provides building block implementations for many of these that need to know where the actual database or broker is deployed. This configuration is declarative and may be handled by developer operations during the deployment process. There are hundreds of building block implementations and the list grows, thanks to extensive community effort.
Deployment
Dapr workflow is developed with Kubernetes in mind. It’s very easy to set up your Kubernetes cluster to work with distributed application runtime. Upon installation, you get the namespace with several Dapr system containers working as the registry, job scheduler, certification center (for mTLS certificates rotation), and the open telemetry traces collector – Zipkin. That’s all there is to it.
When you decide your service needs the framework, you place a couple of lines with labels into the Deployment manifest file and the sidecar will be injected into the Pod next to your service by the appropriate Dapr injector controller.
The Microsoft Dapr tooling provides the development mode where you start the service you are working on currently in your terminal as if wrapped by the Dapr sidecar. It starts your service and monitors its health. When the application finishes initialization, the framework registers itself with the locally running (in the Docker) registration service. So any other applications started in the same way can discover each other by names and interact. It’s a full simulation of the real deployment which in many circumstances may be more than enough for easy development of the application services.
The great part is that manifests written by a developer for local development are exactly the same that will be used for the real deployment to the Kubernetes cluster. Of course, care should be taken to keep secrets secure and not use “localhost” everywhere for dependencies. But otherwise, they are used as-is, which is a nice bonus.
Cons of distributed application runtime
Any coin has two sides. Here we’d like to outline what may be disappointing or render the tool completely unusable for you.
First and foremost, Dapr is for Kubernetes. If you aren’t using it, it’ll be hard to justify deploying twice as many containers. You will need to accompany every application container that you want to wire into the Dapr “network” with a Dapr sidecar container manually, tie them so that the Dapr sidecar exists only when the main container is live and healthy, etc. Nah. It’s too hard to be helpful.
If you have only one monolithic application and are not planning to split it or introduce more standalone services around it, Dapr architecture is not going to be super-helpful. One can imagine still using configuration, secrets, and bindings for external service to cut a couple of corners, but the burden of maintaining a sidecar (because you obviously are not hosting your monolithic app in a Kubernetes cluster) may not be worth the trouble.
Even if your project is a perfect fit structurally there may be some reasons to stay away. The approach is pretty innovative and radical at times (look at state management that rules out chaotic data access, for example), and adopting it may require a significant shift in how you design your applications. Not everyone will be ready to accept it and follow. One way to alleviate this pain is to introduce concepts gradually starting from the most troublesome cross-service invocations. You know your team better to feel if there’ll be any serious resistance to sabotage the project to make a decision here.
Another thing to keep in mind is even though Dapr graduated from the CNCF incubator in late 2024, it is still very young. It evolves rapidly and has great community support, but there are still bugs. For example, we found inconsistencies in how query pagination works in PostgreSQL and MongoDB which made them not interchangeable. There might be other big or small challenges along the way and if you aren’t certain your team will be able to deal with them one way or another, you may consider waiting for the framework to mature a bit more.
Conclusion
Dapr was a great discovery for us last year and we definitely see how it changes the landscape of cloud-native distributed systems development. Yes, it’s still young and occasionally we find bugs here and there, but it looks like a solid foundation for distributed applications development. And it doesn’t put you in a frame of languages and technologies.
We are using it in our pilot and pet projects. Started gradually as we suggested above and now introducing more and more features. We hope this short article sparked your interest in Dapr microservices and you continue your own journey. There will be more articles on this topic!
Looking for a team of developers experienced in cloud app development and distributed application runtime? Reach out to us and let’s work together on your next project.
* * *
Written by Mary Moore and Aleksey Gureiev