Dapr — Structure#

Layout pattern#

Standard Go Layout (cmd/ + pkg/) with Monorepo-like service decomposition

Dapr follows the standard Go project layout: all binaries in cmd/, all library code in pkg/, proto definitions in a dedicated dapr/ directory. However, it extends this by co-locating six independent services in a single repository — each with its own cmd/<service>/ entry point and a corresponding pkg/<service>/ package. This is a controlled monorepo pattern where multiple deployable binaries (daprd, injector, operator, placement, sentry, scheduler) share common infrastructure code under pkg/ without separate module files.

Directory map#

repositories/dapr/
├── cmd/                    # Binary entry points (6 services)
│   ├── daprd/             # Main sidecar runtime (+ app/, options/, components/)
│   ├── injector/          # Kubernetes sidecar injector webhook
│   ├── operator/          # Kubernetes operator / CRD controller
│   ├── placement/         # Actor placement service
│   ├── scheduler/         # Job/workflow scheduler service
│   └── sentry/            # Certificate authority (mTLS)
├── dapr/
│   └── proto/             # Protobuf source definitions (runtime, components,
│                          #   placement, scheduler, sentry, operator, internals)
├── pkg/                   # All shared and per-service library code
│   ├── acl/               # Access control list evaluation
│   ├── actors/            # Virtual actor runtime (reminders, timers, state, routing)
│   ├── api/               # HTTP + gRPC API handlers (universal/, http/, grpc/)
│   ├── apis/              # Kubernetes CRD API types (components, resiliency, etc.)
│   ├── apphealth/         # Application health check probing
│   ├── buildinfo/         # Version / git-commit metadata
│   ├── channel/           # App channel abstractions (HTTP + gRPC transport)
│   ├── client/            # Generated Kubernetes clientset / informers / listers
│   ├── components/        # Component loader registry (per building block type)
│   ├── config/            # Configuration loading (env, modes, protocol)
│   ├── cors/              # CORS middleware
│   ├── diagnostics/       # OpenTelemetry tracing + metrics instrumentation
│   ├── encryption/        # State encryption helpers
│   ├── expr/              # Expression evaluation (e.g., routing rules)
│   ├── healthz/           # Internal health endpoint framework
│   ├── injector/          # Webhook handler logic for sidecar injection
│   ├── internal/          # Internal utilities (not for external import)
│   ├── messages/          # Structured error/status messages
│   ├── messaging/         # Service-to-service invocation (direct messaging)
│   ├── metrics/           # Prometheus metrics setup
│   ├── middleware/        # HTTP middleware chain management
│   ├── modes/             # Deployment mode constants (k8s, standalone)
│   ├── operator/          # Operator controller + CRD reconciliation logic
│   ├── outbox/            # Transactional outbox (pub/sub + state atomicity)
│   ├── placement/         # Placement server logic + consistent hashing
│   ├── ports/             # Port allocation utilities
│   ├── proto/             # Generated protobuf Go code (runtime, components, etc.)
│   ├── resiliency/        # Circuit breaker, retry, timeout policies
│   ├── responsewriter/    # HTTP response writer helpers
│   ├── retry/             # Retry policy primitives
│   ├── runtime/           # Core daprd runtime wiring (processor, hotreload, pubsub…)
│   ├── scheduler/         # Scheduler server logic + client
│   ├── scopes/            # Namespace/scope filtering
│   ├── security/          # mTLS, SPIFFE identity, token management
│   ├── sentry/            # Certificate authority server logic
│   ├── sse/               # Server-sent events support
│   ├── testing/           # Shared test helpers (grpc, logging, trace)
│   └── validation/        # Input validation utilities
├── charts/
│   └── dapr/              # Helm chart for Kubernetes deployment
├── docker/                # Dockerfiles (distroless runtime, debug, dev, Windows)
├── docs/                  # Decision records, development guides, release notes
├── grafana/               # Grafana dashboard JSON definitions
├── swagger/               # OpenAPI / Swagger spec files
├── tests/                 # Integration, e2e, perf tests + test runner
│   ├── apps/              # Test application binaries
│   ├── e2e/               # End-to-end test scenarios
│   ├── integration/       # Integration test suites
│   ├── perf/              # Performance/load tests
│   └── runner/            # Test infrastructure runner
├── tools/
│   └── proto/             # Protobuf code generation tooling
├── utils/                 # Miscellaneous top-level utilities
└── .build-tools/          # Internal build-time tooling (cmd/)

Entry points#

BinaryPathPurpose
daprdcmd/daprd/main.goMain sidecar runtime. Registers all components, exposes HTTP+gRPC API to apps, handles all building blocks (pub/sub, state, actors, secrets, bindings, workflows).
injectorcmd/injector/main.goKubernetes mutating webhook that injects the daprd sidecar container into application pods at admission time.
operatorcmd/operator/main.goKubernetes operator that reconciles Dapr CRDs (Components, Configurations, Resiliency policies, Subscriptions, HTTPEndpoints) and streams configuration to sidecars.
placementcmd/placement/main.goActor placement service that uses consistent hashing (virtual node tables) to route actor invocations to the correct daprd instance.
schedulercmd/scheduler/main.goJob scheduler service for workflow/timer scheduling, using etcd-backed cron. Added in a later release to offload scheduling from placement.
sentrycmd/sentry/main.goCertificate authority that issues SPIFFE-compatible mTLS certificates to dapr sidecars, enabling mutual TLS for service-to-service calls.

All main.go files are thin shims that delegate to cmd/<service>/app/app.go. The app.go files handle option parsing, component registration (for daprd), and service startup.

Package organization#

  • Internal packages (pkg/internal/): Small set of unexported utilities not intended for external consumption (package name internal enforces Go import restriction).

  • Public packages (pkg/): Everything under pkg/ uses descriptive package names aligned with their service or concern. While technically importable, these are not designed as a stable public library — Dapr follows sidecar-model conventions where consumers use the HTTP/gRPC API rather than importing Go packages. Notable public-ish packages:

    • pkg/actors — Virtual actor runtime with sub-packages for API, reminders, timers, state, routing
    • pkg/api — Unified API handler layer with universal/, http/, and grpc/ implementations
    • pkg/components — Per-building-block component registries (state, pubsub, bindings, secretstores, etc.)
    • pkg/resiliency — Circuit breaker + retry + timeout policy engine (with breaker/ sub-package)
    • pkg/runtime — Core daprd wiring: processor, component store, hot-reload, pub/sub subscription manager, workflow engine
    • pkg/security — mTLS and SPIFFE identity management
    • pkg/diagnostics — OpenTelemetry integration with consts and utils sub-packages
    • pkg/proto — Generated protobuf Go stubs for all internal RPC surfaces
  • Layering: The project uses a hub-and-spoke model rather than strict layered architecture:

    • pkg/runtime is the central hub that wires together all building blocks
    • pkg/components/<type> packages define loader registries (what’s available)
    • cmd/daprd/components/ registers the actual component implementations at startup via blank imports (_ "...")
    • pkg/api/{http,grpc} packages call into pkg/runtime and specialized packages
    • Per-service packages (pkg/placement, pkg/sentry, pkg/scheduler, pkg/injector, pkg/operator) are self-contained and only depended upon by their respective cmd/ binary

Build system#

  • Build tool: GNU Make (Makefile) — comprehensive, with docker.mk included for container targets
  • Key targets:
    • make build — Compiles all 6 binaries (daprd, placement, operator, injector, sentry, scheduler) into dist/<os>_<arch>/release/
    • make build-linux — Cross-compiles for Linux (used in Docker image builds)
    • make release — Build + archive (zip on Windows, tar.gz on Linux/Mac)
    • make test — Unit tests
    • make test-integration — Integration test suite
    • make lint — golangci-lint
    • make manifest-gen — Generates Helm YAML manifest
    • make modtidy-all — Tidy all go.mod files
    • Proto generation via make proto (uses tools/proto/)
  • Build tags: DAPR_SIDECAR_FLAVOR controls which components are compiled into daprd (allcomponents vs stablecomponents), enabling slimmer distributions
  • Docker: Yes, multi-stage implied (build artifacts copied into distroless gcr.io/distroless/static:nonroot images). Variants: standard, debug, dev, Mariner (CBL), Windows. The runtime images are minimal distroless containers.

Notable structural decisions#

  1. Single-repo for all control plane services: daprd, placement, sentry, operator, injector, and scheduler live in one repository. This enables atomic changes across service boundaries (e.g., adding a new proto message touches the definition, generated code, and all service implementations in a single PR). The tradeoff is a larger, more complex repo and per-service build complexity.

  2. Proto-first internal RPC: All inter-service communication (daprd↔placement, daprd↔sentry, daprd↔scheduler, daprd↔operator) is defined in proto files under dapr/proto/ with generated code in pkg/proto/. The source-of-truth proto definitions live in the repo alongside the implementations — no separate proto repo.

  3. Component registry via blank imports: The cmd/daprd/components/ directory exists solely to register component implementations via _ imports. This pattern cleanly separates “what components exist” (registration at startup) from “how to use them” (interface abstractions in pkg/components/<type>). The DAPR_SIDECAR_FLAVOR build tag controls which registration file is compiled in.

  4. pkg/api/universal/ unification layer: HTTP and gRPC APIs share business logic via a universal package rather than duplicating handler code. This is an uncommon pattern that avoids drift between the two API surfaces.

  5. Extensive fake/ and mock/ sub-packages: Key packages (pkg/actors/fake/, pkg/channel/fake/, pkg/outbox/fake/, pkg/security/fake/, pkg/runtime/mock/) contain in-package fakes used for unit testing. These are co-located with the production code rather than in a separate tests/ directory, making them easy to discover and maintain.

  6. tests/ directory at repo root for integration/e2e: End-to-end and integration tests are cleanly separated from unit tests in a top-level tests/ directory with its own runner/ infrastructure for standing up real Kubernetes clusters. The tests/apps/ directory contains full application binaries used as test subjects.