Skip to content

Latest commit

 

History

History
144 lines (119 loc) · 7.44 KB

README.md

File metadata and controls

144 lines (119 loc) · 7.44 KB

Dapr Sidecar (Dev)

Cross-cutting infrastructure runtime per ADR-0038. Three building blocks are adopted; everything else is out of scope per ADR-0038 non-goals.

Building block Backend (dev) Component file Application interface
Pub/Sub Kafka (kafka:9092) components/pubsub-kafka.yaml IEventBus
State store Valkey (valkey:6379, RESP protocol) components/statestore-redis.yaml ICacheService
Secret store Vault (http://vault:8200, dev mode) components/secretstore-vault.yaml ISecretProvider

Phase ownership (per phase-02a § Shared Kernel and § Dapr Building Blocks, and phase-02b):

  • Phase 02a declares all three interfaces in LearnStack.SharedKernel with default in-process implementations — InProcessEventBus, InMemoryCacheService, and ConfigurationSecretProvider. All three exist today and are wired at the composition root. Those defaults are the only implementations registered, in every DeploymentMode.
  • Phase 11 ships the Dapr-backed implementations (DaprEventBus, DaprCacheService, DaprSecretProvider) in LearnStack.Infrastructure, on the triggers written in ADR-0035. Until then this compose stack runs Dapr, Kafka, Valkey and Vault for local familiarity only — no backend code path reaches them, and backend/Directory.Packages.props declares no client library for any of the four.
  • P02b-2 Step 3 implements the API-host OutboxProcessor, with both independent review rounds complete. It is the only sanctioned production caller of IEventBus.PublishAsync per ADR-0010 Amendment 1. Modules consume ICacheService and ISecretProvider from 02a and enqueue through IOutbox; they never publish through IEventBus directly.

Service invocation, workflow, bindings, actors, configuration, and distributed lock are not adopted; if a future need appears the gate is a new ADR. The state store's actorStateStore flag is therefore pinned to "false" — see the comment at the top of components/statestore-redis.yaml.

Sidecar topology

Dev compose runs one sidecar bound to the learnstack-api app id:

graph LR
    app["<b>.NET API</b><br/>dotnet run → host:5080<br/>(workstation, outside compose network)"]
    daprd["<b>daprd</b> (compose service: dapr-sidecar-api)<br/>-app-id learnstack-api<br/>-app-port 5080<br/>-app-channel-address host.docker.internal<br/>-dapr-http-port 3500<br/>-dapr-grpc-port 50001<br/>-placement-host-address dapr-placement:50005<br/>-resources-path /components<br/>-config /config/dapr-config.yaml"]
    daprd -- "inbound subscription delivery<br/>via host.docker.internal:5080" --> app
    app -- "outbound publish / get-secret / get-state<br/>via localhost:3500 (HTTP) / :50001 (gRPC)" --> daprd
Loading

Text fallback for non-Mermaid renderers:

  • App — dotnet run on the developer's workstation, listening on host:5080 (outside the compose network).
  • daprd — compose service dapr-sidecar-api, run with flags: -app-id learnstack-api, -app-port 5080, -app-channel-address host.docker.internal, -dapr-http-port 3500, -dapr-grpc-port 50001, -placement-host-address dapr-placement:50005, -resources-path /components, -config /config/dapr-config.yaml.
  • daprd → app — inbound subscription deliveries reach the workstation via host.docker.internal:5080.
  • app → daprd — outbound publish / state / secret calls hit localhost:3500 (HTTP) or localhost:50001 (gRPC).

The .NET host runs outside the container network during active dev (developers dotnet run from their workstation). -app-channel-address host.docker.internal is what makes inbound subscription deliveries reach the host — daprd's default of 127.0.0.1 would resolve to inside the sidecar container, and every subscription would silently fail to deliver. The extra_hosts: host.docker.internal:host-gateway YAML anchor in infra/compose/dev.yml maps the alias on Linux (Docker Desktop does it automatically).

The placement service (dapr-placement) is required even though actors are out of scope — daprd won't start without it.

Application access pattern

Per ADR-0038 + Standards 20, modules never import Dapr.Client. They consume:

public interface IEventBus { Task PublishAsync(IntegrationEventEnvelope envelope, CancellationToken ct); }
public interface ICacheService { Task<T?> GetAsync<T>(string key, CancellationToken ct); /* … */ }
public interface ISecretProvider { Task<string> GetSecretAsync(string key, CancellationToken ct); /* … */ }

DaprEventBus, DaprCacheService, DaprSecretProvider are the only Dapr-aware types in the codebase; they live in LearnStack.Infrastructure and ship in Phase 11 on ADR-0035's triggers. Architecture tests Dapr_SDK_Types_NotImportedOutsideInfrastructure, Modules_DoNotReference_DaprPackage, and ICacheService_Is_OnlyCacheAbstraction keep this honest.

Dev credentials

Surface Credential
Vault root token learnstack-dev-root-token
Kafka auth none (authType: none, disableTls: true)
Valkey password (empty)

All dev-only. Production wires Vault with AppRole / Kubernetes auth and loads the token through Dapr's secretKeyRef indirection so the literal token never appears in the component YAML.

Vault token — single source of truth

As of Phase 01 packet 7 (DX), the Vault root token lives in one place: VAULT_ROOT_TOKEN in the repo-root .env.example (copied to .env per workstation). The chain uses Dapr's secretKeyRef + local-env-secret-store indirection (a {{env.VAR}} template would be silently substituted with the literal — Dapr does not support that syntax in component metadata):

  1. .env — developer's actual value (gitignored).
  2. infra/compose/dev.yml — both the vault service (boots -dev mode with that token) and the dapr-sidecar-api service (passes it to daprd's process env) read ${VAULT_ROOT_TOKEN:-learnstack-dev-root-token}.
  3. infra/dapr/components/secretstore-envvar.yaml registers a secretstores.local.env component named envvar-secrets (loaded first by daprd because no other component depends on it).
  4. infra/dapr/components/secretstore-vault.yaml declares auth.secretStore: envvar-secrets and a vaultToken metadata entry resolved via secretKeyRef: { name: VAULT_ROOT_TOKEN, key: VAULT_ROOT_TOKEN }. Dapr pulls the literal from the process env at component-load time.

Changing .env therefore updates every consumer at the next docker compose up. There is no two-file edit risk and no literal token in any committed YAML metadata field.

What does NOT live here

  • The IEventBus / ICacheService / ISecretProvider implementations — the interfaces and default adapters shipped in Phase 02a; Dapr-backed adapters belong to Phase 11 (see § Phase ownership above).
  • Outbox dispatcher (OutboxProcessor polling + dispatch) — Phase 02b.
  • Shared transport-owned inbox_messages table + IInboxGuard — P02b-3.
  • Production Vault setup (HA mode, auto-unseal, AppRole policies) — Phase 11.
  • Additional sidecars for the Hub overlay (separate app id, separate consumer group) — Phase 02c.