The seven layers
- Runtime route
POST /v1/core/track— auth (api key | write key), Zod envelope, Upstash rate limit, permission core.events.write. NO business logic. Returns 202 {event_id} in <100 ms p95. - Queue publication — QueuePort.publish(‘core.ingest’, envelope{job_id, tenant_id, correlation_id, data}). Adapter bound in core module composition root (Vercel Queues at F1).
- Processor (worker) — idempotency gate (core.processed_jobs by job_id; tracked_events idempotency_key as second fence). Steps in ONE logical flow: identity resolution/upsert → append tracked_events (partitioned; occurred_at predicate discipline) → incremental segment eval (diff → entered/exited) → rules engine match (compiled-rules cache per tenant).
- Domain writes — effects execute via loyalty domain services (e.g., award_points → ledger transaction + lot + balance projection SAME transaction).
- Outbox + audit — every state change emits events + audit_log rows with correlation_id/causation_id (ADR-017 pattern).
- Consumers — notification dispatch (resolveChannels → per-channel rail jobs), webhooks out (HMAC), cache invalidation, metering.
- Tests & budgets — unit: resolveChannels, DSL eval, rules dedupe; integration: full pipeline with duplicate job replay (must no-op); load: k6 track p95 <100 ms, end-to-end effect visibility <5 s p95 in cert. All budgets are DoD items.