Data Flow
How data moves through Danvas
Understanding data flow is critical for debugging and feature development. This diagram shows the primary paths for authenticated requests through Danvas.
System Data Flow
flowchart TD
subgraph Auth["Authentication"]
CLERK[Clerk Auth<br/>OAuth/SSO/Webhooks]
end
subgraph Frontend["Frontend Layer"]
APP[apps/app<br/>Next.js 16]
end
subgraph Backend["Backend Packages"]
AUTH[@repo/auth<br/>User provisioning<br/>Access control]
DB[@repo/database<br/>Drizzle ORM<br/>PostgreSQL]
ANALYTICS[@repo/telemetry<br/>PostHog client<br/>External API]
SLACK[@repo/slack<br/>Slack API<br/>Block Kit]
NOTIF[@repo/notifications<br/>Web Push]
end
subgraph External["External Services"]
NEON[(Neon PostgreSQL)]
TOAST[Toast POS API]
SLACK_API[Slack API]
PUSH[Web Push Service]
POSTHOG[PostHog]
end
CLERK --> AUTH
AUTH --> DB
DB --> NEON
APP --> AUTH
APP --> DB
APP --> ANALYTICS
ANALYTICS --> TOAST
APP --> SLACK
SLACK --> SLACK_API
APP --> NOTIF
NOTIF --> PUSH
APP --> POSTHOGRequest Lifecycle
1. Authenticated Page Load
sequenceDiagram
participant User
participant NextJS
participant Auth
participant DB
User->>NextJS: Request protected page
NextJS->>Auth: getUserAuth()
Auth->>CLERK: Verify session
Auth->>DB: Lookup/create user
Auth-->>NextJS: User object
NextJS->>DB: Query filtered by teamId
NextJS-->>User: Rendered page2. Server Action Mutation
sequenceDiagram
participant User
participant Action
participant Auth
participant DB
participant Audit
participant Slack
User->>Action: Submit form
Action->>Auth: getActionContext("admin")
Auth-->>Action: { auth: { userId, teamId, role, locationIds } } (or redirect /403)
Action->>DB: Begin transaction
Action->>DB: Write data
Action->>Audit: logAudit() (same tx)
Action->>DB: Commit
Action->>Slack: Send notification
Action-->>User: Success response
logAudit()must run inside the same database transaction as the mutation. Calling it aftertx.commit()leaves a window where a crash drops the audit record silently.
3. Webhook Processing
sequenceDiagram
participant Service
participant API
participant Webhook
participant Idem
participant Auth
participant DB
Service->>API: POST /webhooks/clerk (svix headers)
API->>Idem: claimIdempotencyKey({ source, eventType, key })
Idem-->>API: true (first time) | false (duplicate)
alt first delivery
API->>Webhook: verify() signature
API->>Auth: provisionUser()
Auth->>DB: Create/update user
else duplicate
API-->>Service: 200 (already processed)
end
API-->>Service: { received: true }The claimIdempotencyKey step is the only place that decides whether the event body gets processed. Signature verification and provisioning both happen inside the "first delivery" branch.
Key Patterns
Multi-Tenant Isolation
All queries filter by teamId, the local tenant identifier:
// Always include team filter
const posts = await db.query.posts.findMany({
where: eq(posts.teamId, user.teamId)
});Circuit Breaker
Legacy HTTP clients in @repo/telemetry (labor, schedule, employee export) use circuit breakers when calling external reporting APIs:
- Threshold: 5 failures
- Timeout: 60 seconds
- States: Closed → Open → Half-open → Closed
Intelligence → Analytics dashboards (tips, team performance, upgrades) read Neon fact tables and app marts directly — not through those HTTP clients. See Analytics & Tips Integration.
Cache Strategies
- Next.js cache:
cacheLife/cacheTag(Next.js 16) - TanStack Query: Client cache for analytics server actions and API routes
- Cron invalidation: Tag-based revalidation after sync completes (Live Freshness)
Related Files
| File | Purpose |
|---|---|
packages/auth/get-user-auth.ts | Auth flow implementation |
packages/database/src/schema/ | Database schema |
packages/notifications/notification-worker.ts | Durable notification fanout worker |
apps/api/app/api/webhooks/ | Webhook handlers (see apps/api/README.md for full machine-surface inventory) |