# ADR 0004. Async-First Ingestion (Control Plane vs. Data Plane)

## Status
Accepted.

Tightened by spec §34 and [ADR 0015](0015-async-loyalty-sidecar.md): gameplay
must not call PlayerPal at all. Ingestion remains async-first. ACK after durable
acceptance (`202`). See [docs/ingestion-and-availability.md](../ingestion-and-availability.md).

## Context
The naive design validates, queries, calculates campaigns, updates points,
updates analytics, then responds. That puts a large amount of synchronous work
in the request path of every single activity event. At scale this is
both slow and expensive, and couples ingestion availability to the
reward engine's availability.

## Decision
Split the system into:
- **Control plane** (low frequency, session establishment, identity
  resolution, token issuance, campaign/integration config): may touch
  DB/cache directly.
- **Data plane** (high frequency, bets, rounds, transactions,
  telemetry): the ingestion request does auth + minimal envelope
  validation + durable append only, then responds 202. Everything else
  (normalization, rule evaluation, ledger updates, analytics) happens
  asynchronously off a durable event log.

Real-time and batch ingestion both normalize into **one shared event
model** feeding **one** reward engine, never two parallel engines.

## Consequences
- A reward-engine outage does not block activity acceptance.
- Ingestion API is stateless and trivially horizontally scalable.
- Requires idempotent processing downstream (see ADR 0008) since
  durability here is at-least-once, not exactly-once.
- Requires an explicit choice on ACK timing (broker-confirmed vs.
  local-append-then-forward). See the architecture brief §4.8 for the
  trade-off; not a separate ADR since it's phase-dependent, not fixed.
