- **Multi-tenant**: Multiple clubs (tennis, cycling, etc.) share one application
- **Tenant identification**: Credential-based — user logs in, system resolves which club(s) they belong to. No subdomains or URL paths for tenant routing.
- **Multi-tenancy strategy**: RLS + EF Core global query filters (defense-in-depth)
- **Tenant resolution**: NOT subdomain-based. Tenant derived from authenticated user's JWT claims (club membership)
- **Bun runtime**: Research shows Bun has P99 latency issues with Next.js SSR in production (March 2026). Use Bun for local dev / package management, Node.js for production runtime.
- **Infrastructure**: Kustomize (simpler for MVP, no Helm complexity needed yet)
## Research Findings
- **Finbuckle.MultiTenant**: Industry-standard .NET multi-tenancy library. Supports header strategy — good fit for credential-based tenant resolution.
- **EF Core Global Query Filters**: Auto-filter all queries by tenant_id. Combined with RLS for defense-in-depth.
- **PostgreSQL RLS**: SET app.current_tenant per connection. Must reset when returning connections to pool.
- **Next.js + Bun**: Bun has 340ms P99 latency vs 120ms for Node.js (Platformatic benchmarks Jan 2026). Recommendation: Bun for dev, Node.js for prod Docker image.
- **K8s deployment**: Kustomize with base + overlays (dev/staging/prod). PostgreSQL as StatefulSet for dev, managed DB for prod.
- **Docker Compose**: Hot reload via `dotnet watch` for .NET, standard Next.js dev server.
## Decisions (Round 2)
- **Auth provider**: Keycloak — self-hosted in K8s cluster, runs alongside the app
- **Multi-club membership**: YES — a user can belong to multiple clubs, needs a club-switcher/selector after login
- **Member roles**: 4-tier — Admin, Manager, Member, Viewer (per club, since user can be Admin in one club and Member in another)
- **Shift model**: Time-slot with sign-up — date, time range, location, description, required headcount. Members sign up or get assigned.
- **Repo structure**: Monorepo — /backend, /frontend, /infra in one repo
- **Notifications**: None for MVP — simplest approach, users check the app