Dapr — Interfaces#
Interface catalog#
actors.Interface#
- Package:
github.com/dapr/dapr/pkg/actors - File:
pkg/actors/actors.go:87 - Methods:
Init(InitOptions) error Run(context.Context) error Router(context.Context) (router.Interface, error) Table(context.Context) (table.Interface, error) State(context.Context) (actorstate.Interface, error) Timers(context.Context) (timers.Interface, error) Reminders(context.Context) (reminders.Interface, error) Placement(context.Context) (placement.Interface, error) RuntimeStatus() *runtimev1pb.ActorRuntime RegisterHosted(hostconfig.Config) error UnRegisterHosted(actorTypes ...string) WaitForRegisteredHosts(ctx context.Context) error - Purpose: Defines the contract for the entire virtual actor subsystem. Consumers (the runtime and workflow engine) call through this interface, never the concrete
actorsstruct. Sub-capabilities (routing, state, placement) are exposed as sub-interfaces returned by context-aware getter methods, ensuring callers only receive them once the subsystem is initialized and ready. - Implementations:
actors.actorsstruct (the single concrete impl). Workflow engine’swfenginedepends on this interface. - Design quality: Broad (12 methods) but annotated
//nolint:interfacebloatwith deliberate intent. The design decision to return sub-interfaces rather than raw functionality is a clean “lazy-accessor” pattern — callers receiverouter.Interfaceetc. only after the subsystem is ready. The Init/Run split follows the two-phase lifecycle pattern common throughout Dapr.
channel.AppChannel#
- Package:
github.com/dapr/dapr/pkg/channel - File:
pkg/channel/channel.go:33 - Methods:
GetAppConfig(ctx context.Context, appID string) (*config.ApplicationConfig, error) InvokeMethod(ctx context.Context, req *invokev1.InvokeMethodRequest, appID string) (*invokev1.InvokeMethodResponse, error) HealthProbe(ctx context.Context) (*apphealth.Status, error) SetAppHealth(ah *apphealth.AppHealth) TriggerJob(ctx context.Context, name string, data *anypb.Any) (*invokev1.InvokeMethodResponse, error) - Purpose: Abstracts all communication from daprd to the co-located application process. Used for pub/sub delivery, service invocation callbacks, binding triggers, health checks, and job triggers. Protocol (HTTP or gRPC) is hidden behind this interface.
- Implementations:
pkg/channel/http.Channel(FastHTTP-based),pkg/channel/grpc.Channel(gRPC-based). Chosen at startup based on--app-protocolflag. - Design quality: Well-segregated, small (5 methods), cohesive. Each method represents a distinct category of app interaction. The companion
HTTPEndpointAppChannelinterface is a single-method subset for named HTTP endpoint resources, following ISP cleanly.
resiliency.Provider#
- Package:
github.com/dapr/dapr/pkg/resiliency - File:
pkg/resiliency/resiliency.go:92 - Methods:
EndpointPolicy(service string, endpoint string) *PolicyDefinition ActorPreLockPolicy(actorType string, id string) *PolicyDefinition ActorPostLockPolicy(actorType string, id string) *PolicyDefinition ComponentOutboundPolicy(name string, componentType ComponentType) *PolicyDefinition ComponentInboundPolicy(name string, componentType ComponentType) *PolicyDefinition BuiltInPolicy(name BuiltInPolicyName) *PolicyDefinition PolicyDefined(target string, policyType PolicyType) (exists bool) - Purpose: Returns
*PolicyDefinition(circuit-breaker + retry + timeout bundle) appropriate for a given call target. Callers wrap outgoing operations with the returned policy without needing to know whether a policy is configured or what its parameters are. - Implementations:
resiliency.Resiliency(full implementation backed by YAML/CRD config),resiliency.NoOp(no-op implementation for testing or when resiliency is disabled). Thevar _ = (Provider)((*Resiliency)(nil))compile-time assertion enforces interface satisfaction. - Design quality: Good ISP adherence — 7 methods, each scoped to a specific call-site category (endpoint, actor pre-lock, actor post-lock, component inbound, component outbound, built-in). The two actor variants handle the fact that resiliency policy semantics differ before vs. after acquiring an actor lock.
security.Handler#
- Package:
github.com/dapr/dapr/pkg/security - File:
pkg/security/security.go:49 - Methods:
GRPCServerOptionMTLS() grpc.ServerOption GRPCServerOptionNoClientAuth() grpc.ServerOption GRPCDialOptionMTLSUnknownTrustDomain(ns, appID string) grpc.DialOption GRPCDialOptionMTLS(spiffeid.ID) grpc.DialOption TLSServerConfigNoClientAuth() *tls.Config NetListenerID(net.Listener, spiffeid.ID) net.Listener NetDialerID(context.Context, spiffeid.ID, time.Duration) func(network, addr string) (net.Conn, error) MTLSClientConfig(spiffeid.ID) *tls.Config ControlPlaneTrustDomain() spiffeid.TrustDomain ControlPlaneNamespace() string CurrentTrustAnchors(context.Context) ([]byte, error) WithSVIDContext(context.Context) context.Context MTLSEnabled() bool ID() spiffeid.ID WatchTrustAnchors(context.Context, chan<- []byte) IdentityDir() *string - Purpose: Provides all security primitives (mTLS, SPIFFE/X.509 identity, trust anchor management) needed by components that establish network connections. A single Handler instance is passed to gRPC servers, gRPC dial operations, TCP listeners, and TCP dialers, adapting the SPIFFE SVID to each protocol’s credential type.
- Implementations: Concrete implementation in
pkg/securitybacked bydapr/kit/crypto/spiffe. The companionsecurity.Providerinterface (Run(ctx) error+Handler(ctx) (Handler, error)) acts as a factory — callers wait onHandler()until the initial SVID is fetched from sentry. - Design quality: Deliberately broad (16 methods,
//nolint:interfacebloat). The breadth reflects that all security operations are intentionally centralized into one abstraction to prevent certificate-handling code from being scattered. The separation ofProvider(lifecycle) fromHandler(operations) is clean.
wfengine.Interface#
- Package:
github.com/dapr/dapr/pkg/runtime/wfengine - File:
pkg/runtime/wfengine/wfengine.go:45 - Methods:
Run(context.Context) error RegisterGrpcServer(*grpc.Server) Client() workflows.Workflow RuntimeMetadata() *runtimev1pb.MetadataWorkflows ActivityActorType() string - Purpose: Defines the contract for the durable workflow engine. The runtime uses
RegisterGrpcServerto mount the durabletask gRPC service, andClient()to return the workflow client used byuniversal.Universalfor workflow CRUD APIs. - Implementations:
wfengine.enginebacked bydapr/durabletask-go. The engine depends onactors.Interface— workflows are built on top of the actor model. - Design quality: Small and focused (5 methods). Clean separation — the interface exposes only what the runtime’s composition root needs, not the full internal machinery of durabletask.
hotreload/loader.Interface and Loader[T]#
- Package:
github.com/dapr/dapr/pkg/runtime/hotreload/loader - File:
pkg/runtime/hotreload/loader/loader.go:27 - Methods:
// Interface Run(context.Context) error Components() Loader[compapi.Component] Subscriptions() Loader[subapi.Subscription] // Loader[T differ.Resource] (generic) List(context.Context) (*differ.LocalRemoteResources[T], error) Stream(context.Context) (*StreamConn[T], error) - Purpose: Abstracts where component configurations come from (filesystem vs. Kubernetes operator gRPC stream).
Interfaceprovides type-specificLoader[T]instances for components and subscriptions;Loader[T]provides initial listing and a streaming change channel. - Implementations: Two implementations of
Interface:loader/disk(inotify/fsnotify-based for standalone mode) andloader/operator(operator gRPC stream for k8s mode). The genericLoader[T]is implemented per-resource-type in each loader. - Design quality: Excellent use of Go generics (1.18+). The
differ.Resourceconstraint onTensures type safety across the loader/reconciler pipeline without code duplication. TheInterface/Loader[T]two-level structure cleanly separates the “what sources are available” concern from “how do you load a specific resource type” concern.
grpc.API#
- Package:
github.com/dapr/dapr/pkg/api/grpc - File:
pkg/api/grpc/grpc.go:79 - Methods:
io.Closer internalv1pb.ServiceInvocationServer // embedded runtimev1pb.DaprServer // embedded (100+ gRPC RPCs) - Purpose: Combines the internal (sidecar-to-sidecar) service invocation server with the external (app-facing) Dapr gRPC service into a single handler. Embedding the protobuf-generated server interfaces means the compiler enforces that every RPC method is implemented.
- Implementations:
grpc.apistruct, which embeds*universal.Universaland adds gRPC-specific transport handling. Theio.Closeraddition is a local Dapr convention for lifecycle management. - Design quality: The interface is primarily composed of two generated proto interfaces rather than hand-written methods — appropriate for a protocol adapter layer. The separation from
universal.Universal(which contains the actual logic) keeps the gRPC layer thin.
http.API#
- Package:
github.com/dapr/dapr/pkg/api/http - File:
pkg/api/http/http.go:63 - Methods:
APIEndpoints() []endpoints.Endpoint PublicEndpoints() []endpoints.Endpoint - Purpose: Provides the router with two sets of endpoint descriptors: the full Dapr API surface and a restricted public health/metadata subset. The
endpoints.Endpointstruct carries path, method, handler function, and middleware configuration — so the interface is really a route-table provider, not a direct handler. - Implementations:
http.apistruct, which builds endpoint lists fromuniversal.Universaloperations and HTTP-specific adapters (bindings, pubsub streaming, etc.). - Design quality: Intentionally minimal (2 methods) for testability. The actual complexity lives in the returned
[]endpoints.Endpoint, which is inspected during router setup rather than accessed through the interface.
Interface patterns#
Size distribution: Highly bimodal. Core subsystem interfaces (
actors.Interface,security.Handler) are broad (10–16 methods) and carry//nolint:interfacebloatannotations acknowledging the tradeoff. Leaf/transport interfaces (http.API,wfengine.Interface,channel.AppChannel) are small (2–5 methods). The project prefers broad interfaces at subsystem boundaries and narrow interfaces at delegation boundaries.Embedding: Protocol-level embedding is the dominant pattern:
grpc.APIembedsruntimev1pb.DaprServerandinternalv1pb.ServiceInvocationServer(proto-generated).security.Handlerdoes not embed but bundles related TLS/SPIFFE operations. Interface embedding for composition is rare.Implicit satisfaction: Interfaces are defined by consumers in the calling package, not by providers in the implementation package — Go’s standard approach. Examples:
resiliency.Provideris defined inpkg/resiliencybut consumed bypkg/actors,pkg/api/universal, etc. The compile-time assertion pattern (var _ = (Provider)((*Resiliency)(nil))) is used throughout to catch drift.Generics:
Loader[T differ.Resource]inpkg/runtime/hotreload/loaderis the clearest use of generics — parameterizes over component resource type (Component, Subscription) with adiffer.Resourceconstraint. The actors package also uses generics in internal helpers. This reflects Go 1.21+ adoption.stdlib interfaces used:
io.Closerembedded ingrpc.API;context.Contextpervasively as a first parameter on all interface methods. Noio.Reader/io.Writerat the subsystem level — the project’s boundaries are higher-level.
Key abstractions#
actors.Interface— The most architecturally load-bearing interface. Everything above the actor subsystem (universal API, workflow engine, placement) goes through this. The lazy-accessor pattern (returning sub-interfaces via context-aware getters) is a sophisticated readiness management mechanism.channel.AppChannel— The fundamental language-agnostic boundary. This is the seam where Dapr stops being Go and becomes “any language.” It hides whether the app speaks HTTP or gRPC. All pub/sub delivery, service invocation, actor callbacks, and job triggers cross through this interface.resiliency.Provider— The cross-cutting resilience abstraction. Its design — returning a*PolicyDefinitionrather than executing a policy — is a notable pattern: the caller decides when to apply the policy, not the provider. This enables composition (apply to a sub-call) and policy-free no-op injection in tests viaresiliency.NoOp.security.Handler— The SPIFFE/mTLS surface. Its broad shape reflects intentional centralization: the alternative (scatteredtls.Configconstruction) would be more dangerous than the ISP violation. The Provider/Handler split (lifecycle vs. operations) is the key design decision.hotreload/loader.Interface+Loader[T]— The dual-deployment abstraction. This interface pair is what allows the same daprd binary to run on a laptop (filesystem-watching) and in Kubernetes (operator-streaming) with zero conditional code in the core. The genericLoader[T]is among the most Go-idiomatic designs in the codebase.
Interface-driven extensibility#
Dapr uses interfaces for several distinct extensibility axes:
Component system: Component implementations (state stores, pub/sub, bindings, etc.) satisfy interfaces defined in
dapr/components-contrib— an entirely separate repository. Dapr’spkg/components/wrappers adapt these to daprd’s internal types. This is the primary extensibility path: third parties write againstcomponents-contribinterfaces, not Dapr runtime interfaces.Deployment mode:
hotreload/loader.Interfacewith disk vs. operator implementations is a clean strategy pattern for runtime deployment environment selection. Similarly,channel.AppChannelwith http vs. grpc implementations handles app protocol selection.Security backends:
security.Provider/security.Handlerabstract over sentry-issued SPIFFE SVIDs. In theory, thetrustanchorssub-interfaces fromdapr/kitallow different trust anchor sources (file, static, API).Testing: The NoOp pattern is used for resiliency (
resiliency.NoOp) and security — callers can inject no-op or mock implementations without special test framework support. Actor sub-interfaces (reminders.Interface,router.Interface, etc.) are generated into mocks viatestify/mockinpkg/actors/*/testing/packages.