Designing multi-service architectures is something that takes careful planning. Not only do you have to ensure that services are exchanging data they understand, but when you go to production, everything has to scale and remain secure.
Let’s imagine a scenario: You’d like to propose a new distributed technology and pattern to others in your organization using a locally runnable demo built with .NET Aspire.
Producing something like this while keeping all the pieces easy to assemble is often a lot of work. In these scenarios, it’s natural to want prebuilt tools to speed things along and avoid any risks from hand-rolling your own distributed patterns.
Dapr and Aspire - better together
If you’re already familiar with Aspire, then you might not realize that you have an easy path to trying out and adopting Dapr by using the Aspire Dapr NuGet package.
We covered some of the basics about Aspire in a blog post last year. Since that blog post, many developers have integrated Dapr with their Aspire based projects to gain capabilities beyond application definition and local orchestration.
While Aspire helps you work and collaborate by giving you great onboarding and a consistently reproducible environment, it leaves many important details up to you. Details that, if not addressed early on, can result in reliability and scaling issues that require last-minute and often challenging design changes.
Inspiring discussion
With adoption of the integration NuGet growing, and as maintainers of Dapr, we’d like to go beyond the basics and engage with the community around the Dapr and Aspire developer experience.
To accomplish this, we’ve prepared a demo project for you to play with: https://github.com/diagrid-labs/dapr-aspire-pub-sub-demo

The solution features two different pubsub brokers and an application that takes messages arriving from one and publishes them to the other. The main goal of this solution is to inspire discussion around the integration and offer an easy way to try out Dapr with Aspire.
Once you clone and run the repository, take some time to explore the code. The sample application demonstrates:
- How Dapr and Aspire can work together to simplify distributed application development
 - Dapr's publish/subscribe pattern and its ability to work with multiple message brokers
 - How to configure Dapr components within an Aspire application
 
An important note: A prerequisite for running this project is to ensure that you have installed the Dapr CLI and initialized Dapr prior to running your Aspire project.
The sample sets up Eclipse Mosquitto (mqtt) and RabbitMQ (amqp) message brokers along with a listener project that gets started with its own Dapr sidecar process.
Messages that get published to the Mosquitto broker using Dapr pubsub are consumed and modified by a worker which then publishes the result to RabbitMQ also using Dapr pubsub.
(The Dapr pubsub building block is just one of Dapr’s many distributed system abstractions that you might already be familiar with.)
Let’s take a look at some of the code in the AppHost Program.cs.
var mqtt = builder
    .AddContainer("mqtt", "eclipse-mosquitto")
    .WithBindMount(Path.Join("resources", "mosquitto", "config"), "/mosquitto/config")
    .WithVolume("dapr-aspire-demo-mqtt", "/mosquitto/data")
    .WithEndpoint(1883, 1883)
;Here, Aspire is used to configure an Eclipse Mosquitto MQTT broker. Two configuration files are bound, and the service is made visible on port `1883`:
var amqp = builder
    .AddRabbitMQ("amqp", amqpUsername, amqpPassword, port: 5672)
    .WithBindMount(Path.Join("resources", "rabbitmq", "definitions.json"), "/opt/definitions.json")
    .WithBindMount(Path.Join("resources", "rabbitmq", "rabbitmq.config"),
"/etc/rabbitmq/rabbitmq.config")
    .WithManagementPlugin()
;Similarly, we set up our RabbitMQ broker, along with some configuration that makes our demo a little more convenient.
Next, let’s have a look at our event receiver Aspire setup.
var eventReceiver = builder
    .AddProject<EventReceiver>("event-receiver")
    .WithDaprSidecar(new DaprSidecarOptions
    {
        LogLevel = "debug",
        ResourcesPaths = [ Path.Join(executingPath, "resources", "dapr") ],
    })
    .WaitFor(mqtt)
    .WaitFor(amqp)
;
This registers the event receiver, which is what processes our messages. It’s also where Aspire is instructed to run a daprd process using the WithDaprSidecar method. This adds Dapr environment variables, which get picked up by the Dapr .NET SDK while also ensuring that the Dapr process itself is started with the right arguments.
If you’re already familiar with Aspire, one thing you might notice is that the Aspire definition for the listener project doesn’t have any WithReference calls to connect it with the brokers.
This is intentional, as part of leveraging Dapr is to handle communication between dependencies through its sidecar-based design.

To test this application, you can trigger periodic messages using the message-sender command to enqueue a message for processing. The message sender custom command - discussed below - uses the Dapr executable directly to enqueue messages.

The event receiver project takes incoming MQTT messages and modifies them. This is where the business logic lives in the event receiver. We also make sure to decorate our minimal API endpoint to tell the Dapr messaging SDK which pubsub to use.
host.MapPost("demo", async (
    [FromServices] DaprClient daprClient,
    [FromBody] string message
) =>
{
    var response = message switch
    {
        "Beat..." => $"Heart {message}",
        _ => $"Received: {message}",
    };
    await daprClient.PublishEventAsync("amqp-pubsub", "demo", response);
    return TypedResults.Ok();
}).WithTopic("mqtt-pubsub", "demo");

Once processing is complete, a new message is created and sent to the AMQP-backed pubsub (RabbitMQ).
Observing the messages
Use the following steps to observe the messages as they arrive in RabbitMQ:
- Trigger a message using the custom publish message command in our Aspire project
 

- Use the link provided in Aspire to navigate to your RabbitMQ dashboard. Don’t worry if your port is different. Aspire manages them for us.
 

- At the RabbitMQ login screen, use `demo` for both the username and the password.
 

- Finally, go to the “Queues and Streams” area in RabbitMQ and click “Get Message(s)” to see the message we sent.
 

Share your thoughts
This demo offers just enough to see Dapr and Aspire working seamlessly together, and gives you an opportunity to try out the experience.
There’s a lot we’d like to learn from the community. Please share your experience with the current Dapr Aspire integration:
- Questions around the relationship between Dapr and Aspire, as well as how you can use them together effectively
 - Any comments or notes from trying out the demo project
 - Anything you’d like to see improved
 - General questions around Dapr and Aspire project architecture
 
To do this, join us in the #dotnet-aspire channel on the Dapr Discord Server where we have an active community talking about using Aspire and Dapr.
Finally, if you’re still learning Dapr and want more resources, head over to Dapr University, a free cloud-based sandbox environment to learn about the Dapr basics, Dapr Workflow, and Dapr Agents.





