DAPR: a code-based approach with DOTNET

Satyam Pushkar
7 min readNov 15, 2021

In this article I am going to explain step by step approach to create and run a demo distributed application using DAPR with dotnet code. You can find the sample code here.

If you want to learn/understand what is DAPR, and how it works, please check this article. You can also check the below picture which is an awesome visual representation of DAPR created by Nitya Narasimhan.

Visual guide to DAPR by Nitya Narasimhan

Please follow the below mentioned steps to make your local environment ready to build and run DAPR applications in self-hosted mode.

  • The very first step is to install DAPR cli. You can follow the instructions mentioned over here. This helps in launching, running, and managing DAPR instances.
  • Then install the Docker Desktop. If running on Windows platform, make sure that docker desktop for windows is configured to use Linux containers. DAPR uses Docker Containers to provide an awesome out-of-the-box experience. Docker Desktop is not mandatory for DAPR but for the example given in this article uses it.
  • Now set up your development environment by initializing DAPR. This step installs latest DAPR binaries and container images.
  • The sample code mentioned in this article uses .NET 6.
  • Open Visual Studio Code(VSCODE). If you do not have VSCODE installed on your machine you can download and install from here.

Once you are done with setting up the local environment you can follow along. For demo purpose I have created a fraud-detection application. It’s a distributed application following microservice architecture. You can find the complete codebase here. The application consists of 3 services and 1 simulation service.

You can check the folder structure in the image on left. Start with creating folders for each of the 4 services. TransactionService, TransactionAnalyzerService and NotifierService are webapi services. TransactionSimulation is a console application which mimics to create/initiate multiple transactions. Add one more folder named ‘dapr’ which will consists of ‘components’ and ‘config’. Components folder consist of building blocks’ configuration. Add another folder called ‘Infrastructure’. This folder consists of scripts to start underline infrastructure to run this distributed system.

You can check the design of the application in the above diagram. All the main seven steps are explained in following lines:

  • Step 1: TransactionSimulation generates random transactions and uses the mqtt protocol to push these transactions. You can check the code in ‘src\TransactionSimulation\Program.cs’ where data is getting pushed.

Now you must be thinking where this data is getting pushed. For that purpose check ‘‘src\dapr\components\newtransactions.yaml’. Here you can see the ‘bindings.mqtt’ which has a topic called ‘transaction/new’, the same to which simulation service is pushing the data.

Now where is this mqtt message broker is running. You can check ‘src\Infrastructure\mosquitto’ folder where docker, config and scripts are there to start/run the broker instance. The script will run the mosquitto message broker on docker.

Next step to know is how this data is getting consumed by Transaction service. For that please check ‘src\TransactionService\Controllers\TransactionController.cs’. Transaction service is using the DAPR building block -binding(input). You can see the HttpPostAttribute which is pointing to ‘newtransaction’ and which is similar to metadata->name in ‘src\dapr\components\newtransaction.yaml’.

If you watch closely, there is nowhere code written specific to mosquitto(except for simulation service). That’s the beauty of DAPR. There is a lot of background task which is performing under the hood by DAPR.

  • Step 2: In the next step Transaction service saves the data. It uses DAPR building block -statestore. You can check the sample code at ‘src\TransactionService\Repositories\TransactionRepository.cs’.

Here you will nowhere see any specific code which specifies where data is getting saved. To know that please check the state.redis type of component configuration in ‘src\dapr\components\statestore.yaml’. You will notice the metadata->name matches the DAPR_STORE_NAME of TransactionRepository.cs.

  • Step 3: After saving the data, Transaction service calls TransactionAnalyzer service using DAPR building block -service invocation. You can check the code at ‘src\TransactionService\Proxies\TransactionAnalyzerService.cs’.

HtpClient is one way to do service invocation. We can use gRPCClient or DAPR client also. The base address of HtpClient is defined in ‘src\TransactionService\Program.cs’.

If you look closely the client is calling only it’s own sidecar and specifies the app-id of TransactionAnalyzer service. The calling sidecar then calls the TransactionAnalyzer service’s dapr sidecar and finally call goes to ‘Analyze’ action of TransactionAnalyzerService controller

  • Step 4: TransactionAnalyzer service then do some analysis. For this purpose it uses the same DAPR building block -statestore explained in step 2.
  • Step 5: After analysis if analyzer service detects that there is a fraud, it publishes the fraud event to a pub-sub broker using DAPR building block -publish & subscribe. You will be surprised to see how simple it is to publish a message/event using DAPR. Please check the code at ‘src\TransactionAnalyzerService\Controllers\TransactionAnalyzerController.cs’.

Now to understand where is it getting published, please check the pubsub.rabbitmq type of component configuration at ‘src\dapr\components\pubsub.yaml’. Now you can relate to how and where the messages are getting published. Scripts to run RabbitMQ can be find at ‘src\Infrastructure\rabbitmq’.

  • Step 6: Notifier service has subscribed to the same broker(in our case RabbitMQ). It consumes the message/event using DAPR building block -publish & subscribe. DAPR has made it very simple. All you need is to add an attribute on top of action method with proper topic details. Please check the code at ‘src\NotifierService\Controllers\NotifierController.cs’.

Please don’t forget to add ‘UseCloudEvents()’and ‘MapSubscribeHandler()’ in Programm.cs. Check ‘src\NotifierService\Program.cs’.

  • Step 7: Notifier service then creates an email format and send mail using DAPR building block -binding(output). Again DAPR has make pretty easy. You can check the code at ‘src\NotifierService\Controllers\NotifierController.cs

Till now, you must have understood that to invoke binding(sending email), there should be a component configuration. And yes, it is of type ‘bindings.smtp’ and you can find it at ‘src\dapr\components\email.yaml’. This is pointing to an instance of maildev which is used to test sending email in dev environment. The ‘src\Infrastructure\maildev’ folder contains scripts start/run it’s docker instance.

If you look at the whole application, you will not find any code specific to redis, mqtt, rabbitMQ or maildev. For example, if you want to replace redis with mongodb, all you need is to change the configuration(same is in codebase in commented out format). Similarly if you want to change RabbitMQ with Kafka, you can achieve the same by only changing the configuration. The baseline is that application code/logic is not changing while changing the underlying infrastructure. That is the beauty of DAPR (Dynamic Application Runtime).

To make the application run, please note down some important points for all the 3 web api services:

  1. Add the line ‘builder.Services.AddControllers().AddDapr();’ in programm.cs.
  2. Disable https by removing ‘app.UseHttpsRedirection()’ form programm.cs.
  3. Update ‘launchSettings.json’ with different applicationUrl as we are going to use self-hosted mode.
  4. The same port will be updated as app-port in ‘start-selfhosted.ps1’.

Follow the steps mentioned below to run the application in self-hosted mode.

  • Initialize dapr
# Run the commanddapr init
dapr --version
  • Start Infrastructure
# Change directory to ‘src\Infrastructure’ and runstart-all.ps1
  • Start services in self-hosted mode
# Change directory to ‘src\TransactionService’ and runstart-selfhosted.ps1# Change directory to ‘src\TransactionAnalyzerService’ and runstart-selfhosted.ps1# Change directory to ‘src\NotifierService’ and runstart-selfhosted.ps1# Change directory to ‘src\TransactionSimulation’ and rundotnet run
All services running logs

--

--

Satyam Pushkar

Software Engineer | Backend Specialist | System Architect | Cloud Native Apps | DOTNET, PYTHON | https://www.linkedin.com/in/satyampushkar/