Dapr pub/sub
Dapr is a runtime that runs a sidecar process next to each service. The service does not talk to RabbitMQ, Azure Service Bus or Kafka. It calls the sidecar over local HTTP or gRPC, and the sidecar talks to the broker. Which broker that is lives in a YAML file, not in the code. Where a messaging frameworkMessaging frameworks for .NETWhat MassTransit, NServiceBus, Wolverine and CAP add on top of a raw broker client.Opens in a new tab is a library inside the process, Dapr moves the same job into a separate one.
The component file
A component names the broker and holds its connection settings. Moving the shop from RabbitMQ to another supported broker means a different type and different metadata here, while the service code stays the same. Credentials belong in a secret store rather than in this file.
apiVersion: dapr.io/v1alpha1kind: Componentmetadata: name: shop-pubsubspec: type: pubsub.rabbitmq version: v1 metadata: - name: connectionString value: "amqp://localhost:5672"Line 4 is the name the code uses. Line 6 selects the broker, here pubsub.rabbitmq.
Publishing
With the .NET SDK (Dapr.Client and Dapr.AspNetCore), publishing is one call to DaprClient. The sidecar’s HTTP form of the same call is POST /v1.0/publish/<pubsub>/<topic>. Dapr wraps the payload in a CloudEvents 1.0 envelope.
builder.Services.AddDaprClient(); app.MapPost("/orders", async (PlaceOrder cmd, DaprClient dapr, CancellationToken ct) =>{ var order = Order.Create(cmd.CustomerId, cmd.Total); // ... save the order ... await dapr.PublishEventAsync( "shop-pubsub", "orders.placed", new OrderPlaced(order.Id, order.Total), ct); return Results.Accepted($"/orders/{order.Id}");});Subscribing
The Inventory service tells the sidecar which topics it wants. MapSubscribeHandler exposes that list, and WithTopic adds this endpoint to it. The sidecar then calls the endpoint for each message. The other ways to subscribe are a declarative YAML subscription and a [Topic] attribute on a controller action.
app.UseCloudEvents();app.MapSubscribeHandler(); app.MapPost("/orders-placed", async (OrderPlaced evt, IStock stock, CancellationToken ct) =>{ await stock.ReserveAsync(evt.OrderId, ct); return Results.Ok();}).WithTopic("shop-pubsub", "orders.placed");UseCloudEvents unwraps the envelope so the handler receives an OrderPlaced. The response tells Dapr what to do: a 2xx means the message is processed, a 404 drops it, and any other status means it will be retried. Dapr offers dead-letter topics for messages that keep failing. The service is started under the sidecar with dapr run --app-id inventory --app-port 5002 dotnet run, or runs next to one in a Kubernetes pod.
The trade-offs
- Broker-agnostic code. The same publish and subscribe calls work across the supported brokers, and the same API is available from any language.
- Another process to run and watch beside every service, and another network hop for each message.
- A common API hides broker differences. Dapr guarantees at-least-once delivery. Competing consumers across instances with the same app id work with Kafka, Azure Service Bus queues, RabbitMQ and Redis Streams, and other components support fewer patterns. Features that exist in only one broker are reached through component settings, if at all.
- Publishing is still a separate call from saving the order, so the dual-write problem in the outbox noteOutbox and inboxWrite the event in the same transaction as the data, publish it from there, and deduplicate on the way in.Opens in a new tab remains, and handlers still need to be idempotentIdempotent consumersA consumer that gives the same result whether a message arrives once or five times.Opens in a new tab.
Where it fits
Dapr fits when several services, possibly in different languages, need the same messaging API and the broker may change. A team with one language and one broker gets less from the extra moving part, and a library such as MassTransit (see the walkthroughMassTransit, from contract to outboxOne event, OrderPlaced, carried from the Orders service to Inventory with MassTransit 8 on RabbitMQ.Opens in a new tab) keeps everything in one process.
Sources
- Publish and subscribe overview, Dapr Docs · accessed June 12, 2026
- How to: publish a message and subscribe to a topic, Dapr Docs · accessed June 12, 2026
- Publish and subscribe API reference, Dapr Docs · accessed June 12, 2026
- RabbitMQ pub/sub component, Dapr Docs · accessed June 12, 2026
- Troubleshoot pub/sub with the .NET SDK, Dapr Docs · accessed June 12, 2026