| name | integrate-dapr-services |
| description | Run ABP's own abstractions (client proxies, distributed event bus, distributed lock) over a Dapr sidecar with little or no code change. USE FOR: adding the Volo.Abp.Dapr modules, invoking services through Dapr service invocation, backing IDistributedEventBus with Dapr pub/sub, using Dapr for distributed locking, securing receive endpoints with the App API token; also when you see Dapr app-ids, sidecars, or [Topic] attributes in an ABP project. DO NOT USE FOR: deciding sync-vs-async or the outbox/inbox and idempotency design itself (use design-module-and-service-communication); plain HTTP client proxies without Dapr (use consume-remote-services); non-Dapr distributed locking/caching providers (use the distributed-caching-and-locking skill).
|
| license | MIT |
ABP Dapr Integration
Dapr decouples common microservice concerns (service invocation, pub/sub, locking) into a sidecar runtime. ABP ships integration packages so its own abstractions (client proxies, IDistributedEventBus, IAbpDistributedLock) run over Dapr with little or no code change. ABP and Dapr are complementary — ABP gives the opinionated architecture, Dapr the runtime plumbing.
When to Use
- Adding the
Volo.Abp.Dapr modules to an ABP solution.
- Routing ABP client proxies through Dapr service invocation.
- Backing the distributed event bus with Dapr pub/sub (publish and/or receive).
- Using Dapr's building block for
IAbpDistributedLock.
- Securing Dapr receive endpoints with the App API token.
- Working in a project that already uses Dapr app-ids, sidecars, or
[Topic] attributes.
When Not to Use
- Deciding whether to communicate sync vs async, or designing the outbox/inbox and idempotency — use the design-module-and-service-communication skill (this skill is the Dapr transport, not the design).
- Plain HTTP client proxies without Dapr — use the consume-remote-services skill.
- Non-Dapr distributed locking / caching providers — use the distributed-caching-and-locking skill.
How it works
Packages
Add only what you need; each depends on the core Volo.Abp.Dapr:
Volo.Abp.Dapr — core package (AbpDaprModule). Provides AbpDaprOptions and IAbpDaprClientFactory. Everything else references it.
Volo.Abp.Http.Client.Dapr (AbpHttpClientDaprModule) — routes ABP's dynamic/static C# client proxies through Dapr service invocation.
Volo.Abp.EventBus.Dapr (AbpEventBusDaprModule) — distributed event bus over Dapr pub/sub. Publish only (can send, can't receive).
Volo.Abp.AspNetCore.Mvc.Dapr.EventBus (AbpAspNetCoreMvcDaprEventBusModule) — adds the subscription endpoints so an ASP.NET Core app can also receive events. Already references Volo.Abp.EventBus.Dapr, so install this one to send and receive.
Volo.Abp.DistributedLocking.Dapr (AbpDistributedLockingDaprModule) — backs IAbpDistributedLock with Dapr's lock building block.
Install a package with abp add-package Volo.Abp.Dapr (etc.), or manually add the NuGet reference plus [DependsOn(typeof(AbpDaprModule))] on your module class.
Core options and client factory
AbpDaprModule binds the Dapr section of configuration into AbpDaprOptions. All settings are optional — you usually configure nothing:
Configure<AbpDaprOptions>(options =>
{
});
"Dapr": { "HttpEndpoint": "http://localhost:3500/" }
DaprApiToken defaults from the DAPR_API_TOKEN env var and AppApiToken from APP_API_TOKEN — both set by Dapr at runtime, so they're normally auto-filled.
Use IAbpDaprClientFactory to build DaprClient / HttpClient objects that already honor these options:
public class MyService : ITransientDependency
{
private readonly IAbpDaprClientFactory _daprClientFactory;
public MyService(IAbpDaprClientFactory daprClientFactory)
=> _daprClientFactory = daprClientFactory;
public async Task DoItAsync()
{
DaprClient daprClient = await _daprClientFactory.CreateAsync();
HttpClient httpClient = await _daprClientFactory.CreateHttpClientAsync("target-app-id");
}
}
Service invocation (client proxies over Dapr)
With Volo.Abp.Http.Client.Dapr installed, point ABP's remote service base URL at the target Dapr app-id instead of a host:
{
"RemoteServices": {
"Default": { "BaseUrl": "http://dapr-httpapi/" }
}
}
Here dapr-httpapi is the server app's Dapr application id. The remote service name (Default) must match the name used in AddHttpClientProxies / AddStaticHttpClientProxies. After that, your existing client-proxy calls automatically go through Dapr's service invocation building block — no call-site changes.
Distributed event bus over Dapr pub/sub
Once the pub/sub packages are installed, use the event bus exactly as documented for distributed events — publish with IDistributedEventBus.PublishAsync, subscribe by implementing IDistributedEventHandler<TEvent>. ABP auto-registers your handlers with Dapr; no application code change to switch to Dapr as the provider.
Configure the pub/sub component name (defaults to pubsub):
Configure<AbpDaprEventBusOptions>(options =>
{
options.PubSubName = "pubsub";
});
ABP exposes two endpoints Dapr uses:
dapr/subscribe — Dapr fetches the subscription list here (ABP fills it from your handlers and any [Topic] controller actions).
api/abp/dapr/event — the unified receive endpoint; ABP dispatches to the right handler by topic.
ABP calls MapSubscribeHandler internally — don't call it yourself. Add app.UseCloudEvents() if you want CloudEvents support.
Two Dapr-specific caveats:
- Dynamic (string-based) events are not supported on the Dapr provider — Dapr needs topic subscriptions declared at startup and can't add them at runtime. Calling
Subscribe(string, ...) on it throws AbpException.
- Publishing directly via
DaprClient.PublishEventAsync bypasses ABP features like the outbox/inbox pattern; prefer IDistributedEventBus to keep them.
Distributed locking with Dapr
Volo.Abp.DistributedLocking.Dapr makes IAbpDistributedLock use Dapr's lock building block. Configure the store:
Configure<AbpDistributedLockDaprOptions>(options =>
{
options.StoreName = "mystore";
});
Usage is the standard ABP lock API:
await using (var handle = await _distributedLock.TryAcquireAsync("MyLockName"))
{
if (handle != null)
{
}
}
Two differences from ABP's default lock providers:
timeout on TryAcquireAsync is ignored — Dapr doesn't wait to acquire.
- The lock auto-expires after
DefaultExpirationTimeout even if not released; there's no per-call expiration parameter.
ABP's docs note Dapr's distributed-lock building block is at Alpha stage and its API may change — for production locking, ABP still recommends the DistributedLock-library providers over Dapr.
Security (App API token)
When traffic flows through Dapr, protect your receive endpoints with the App API token. In an event-handling action, validate it:
[HttpPost("/stock-changed")]
[Topic("pubsub", "StockChanged")]
public async Task<IActionResult> OnStockChangedAsync([FromBody] StockCountChangedEto model)
{
HttpContext.ValidateDaprAppApiToken();
return Ok();
}
ValidateDaprAppApiToken() checks the dapr-api-token header (no-op if you haven't configured App API token). Outside controllers, inject IDaprAppApiTokenValidator. Enabling App API token validation is strongly recommended so arbitrary callers can't hit your subscription endpoints.
Validation
- Confirm the Dapr sidecar is running and the app-id used in
RemoteServices.BaseUrl matches the target's Dapr application id; a proxy call then reaches the target via service invocation with no call-site change.
- For pub/sub, confirm Dapr can fetch the subscription list from
dapr/subscribe and that published events arrive at api/abp/dapr/event and dispatch to the right handler.
- For locking, acquire
TryAcquireAsync("MyLockName") against the configured StoreName and verify mutual exclusion.
- Hit a receive endpoint without a valid
dapr-api-token header and confirm ValidateDaprAppApiToken() rejects it (once App API token is configured).
Common Pitfalls
Volo.Abp.EventBus.Dapr is publish-only; to also receive events install Volo.Abp.AspNetCore.Mvc.Dapr.EventBus (it references the event bus package).
- Don't call
MapSubscribeHandler yourself — ABP does it internally; add app.UseCloudEvents() only if you want CloudEvents.
- Dynamic (string-based) events are not supported over Dapr —
Subscribe(string, ...) throws AbpException, because Dapr needs subscriptions declared at startup.
- Publishing directly via
DaprClient.PublishEventAsync bypasses ABP's outbox/inbox — go through IDistributedEventBus to keep them.
- Dapr's distributed lock ignores the
TryAcquireAsync timeout (no wait) and auto-expires after DefaultExpirationTimeout with no per-call override; its building block is Alpha — prefer the DistributedLock-library providers for production.
- Enable App API token validation so arbitrary callers can't hit your subscription endpoints;
ValidateDaprAppApiToken() is a no-op until the token is configured.
See also
- ABP Dapr integration:
https://github.com/abpframework/abp/blob/rel-10.5/docs/en/framework/dapr/index.md
- Distributed Event Bus:
https://github.com/abpframework/abp/blob/rel-10.5/docs/en/framework/infrastructure/event-bus/distributed/index.md
- Distributed Locking:
https://github.com/abpframework/abp/blob/rel-10.5/docs/en/framework/infrastructure/distributed-locking.md