# Architecture — Desi Clans **Version:** 1.0 **Last updated:** 2026-04-04 --- ## 1. System context ``` ┌─────────────┐ HTTPS ┌──────────────┐ SQL ┌────────────┐ │ Browser │ ──────────────► │ FastAPI API │ ◄──────────► │ PostgreSQL │ │ (React) │ │ + static │ └────────────┘ └─────────────┘ │ media │ │ └──────┬───────┘ │ │ │ optional ▼ └──────────────────────► S3 / CDN (prod media) ``` - **Monorepo:** `frontend/`, `backend/`, `docs/`. - **Deployment unit:** API container + static frontend (CDN or same origin) + managed Postgres. - **No microservices in v1.** One API process, clear router boundaries for future split. --- ## 2. Architectural principles 1. **API-first content** — Public UI never hardcodes event copy or images. 2. **Thin client** — Business rules (inventory, check-in) live in the backend. 3. **Explicit phases** — Ship discovery + admin before native payments if needed. 4. **Boring tech** — Stack locked to PRD; no extra frameworks without ADR. 5. **Security defaults** — JWT for auth, parameterized SQL via SQLAlchemy, validated Pydantic I/O. --- ## 3. Repository layout (target) ``` DESI-CLANS/ ├── frontend/ │ ├── public/ │ ├── src/ │ │ ├── api/ # Axios clients, types mirroring API_SPEC │ │ ├── components/ # UI primitives + composites │ │ ├── hooks/ │ │ ├── layouts/ │ │ ├── pages/ # Route-level views │ │ ├── styles/ # Tailwind entry, tokens │ │ ├── utils/ # seo, dates, formatters │ │ ├── animations/ # GSAP contexts, ScrollTrigger setup │ │ ├── App.jsx │ │ └── main.jsx │ ├── index.html │ ├── vite.config.js │ ├── tailwind.config.js │ └── package.json ├── backend/ │ ├── alembic/ │ ├── app/ │ │ ├── routers/ # public, auth, admin, share, analytics, orders (future) │ │ ├── services/ # domain logic │ │ ├── models.py │ │ ├── schemas.py │ │ ├── auth.py │ │ ├── config.py │ │ ├── database.py │ │ └── main.py │ ├── scripts/ # seed, migrate helpers │ ├── tests/ │ ├── Dockerfile │ └── requirements.txt ├── docs/ └── README.md ``` --- ## 4. Backend layers | Layer | Responsibility | |-------|----------------| | **Routers** | HTTP, status codes, dependency injection, no business logic | | **Services** | Transactions, orchestration (editions, media, tickets, email) | | **Models** | SQLAlchemy ORM, enums | | **Schemas** | Pydantic request/response; never expose hashed passwords | **Cross-cutting** - `config.py` — settings from environment (see `.env.example` when added). - `auth.py` — JWT create/verify, `get_current_admin`, future `get_current_user`. - Migrations — Alembic only; avoid `create_all` in production (dev convenience may exist temporarily). --- ## 5. Frontend layers | Layer | Responsibility | |-------|----------------| | **Pages** | Data loading per route, SEO hooks, layout composition | | **Components** | Presentational; accept props, no direct Axios in leaf components | | **api/** | Base Axios instance, interceptors for admin token | | **animations/** | GSAP timelines registered once; respect `prefers-reduced-motion` | **Routing (React Router)** - Public: `/`, `/events`, `/events/:slug`, `/subscribe` (optional dedicated page) - Admin: `/admin/login`, `/admin/*` (protected) - Account (phase): `/account`, `/account/tickets/:id` - Check-in (phase): `/admin/check-in` or staff-only route **SEO** - Vite SPA: use `react-helmet-async` **only if** needed; prefer minimal approach: - Default meta in `index.html` - Per-route `document.title` + meta updates in page `useEffect` - Backend `/share/events/{slug}` HTML for crawlers (already planned in API) --- ## 6. API boundaries | Namespace | Auth | Purpose | |-----------|------|---------| | `/api/public/*` | None | Read editions, config, subscribe, outbound redirect, calendar | | `/api/auth/*` | Credentials | Admin login (attendee login added with ticketing) | | `/api/admin/*` | Admin JWT | CRUD editions, media, links, users, subscribers | | `/api/admin/analytics/*` | Admin JWT | Click summaries | | `/share/events/{slug}` | None | OG HTML for social crawlers | | `/api/orders/*` (future) | User JWT | Checkout, my tickets | | `/api/check-in/*` (future) | Staff JWT | Validate QR, mark checked in | Public reads are cache-friendly (CDN `Cache-Control` on GET editions list with short TTL). --- ## 7. Media pipeline 1. Admin uploads via multipart → service stores file. 2. **Local:** `upload_dir` served at `/media/...` 3. **Production:** S3 bucket + public or signed URLs; store `file_path` key in DB. 4. Optional thumbnails generated on upload (service layer); expose `thumb_url` in API. --- ## 8. Ticketing architecture (target) ``` EventEdition ──< TicketTier │ Order ──────────────┼──< OrderLine (tier, qty, unit_price) │ └──< Ticket (code, qr_secret, status, checked_in_at) ``` **Checkout flow (native)** 1. Client POST order intent with tier + quantity. 2. Server validates window, inventory; creates `pending` order; returns payment session (phase B). 3. Webhook confirms payment → status `paid` → issue tickets with unique codes. 4. Email service sends QR (PNG or link to ticket page). **QR validation** - Payload: `ticket_id` + HMAC signature (server secret). - Scan endpoint: verify signature, edition match, status `issued` → `checked_in`. **Outbound mode (per edition flag)** - `ticketing_mode: outbound | native` - Outbound uses existing `bookmyshow_url`, `district_url`, etc., via `/api/public/go` click tracking. --- ## 9. Security - Passwords: bcrypt via passlib. - JWT: HS256, `ACCESS_TOKEN_EXPIRE_MINUTES` configurable. - CORS: explicit origin list from env. - Rate limit: add at reverse proxy (nginx/Cloudflare) for public POST subscribe and auth login. - Admin actions audited via structured logs (user id, action, entity id). --- ## 10. Performance requirements | Area | Target | |------|--------| | Public GET editions | p95 < 300ms @ warm DB | | Image delivery | WebP/AVIF where supported; width variants | | Frontend bundle | Route-based code splitting for admin | | DB | Indexes on `slug`, `event_type`, `is_published`, `event_date` | | Animation | GSAP only on hero/key sections; disable heavy ScrollTrigger on mobile low power | --- ## 11. Observability - Health: `GET /api/public/health` - Structured JSON logs (request id, path, status, duration) - Production: disable OpenAPI UI (`openapi_url = None` when `environment=production`) --- ## 12. Coding conventions ### Python (backend) - Python 3.11+ - Type hints on public functions - `ruff` or `flake8` + `black` (pick one formatter in CI) - Router functions: max ~30 lines; delegate to services - Enums in `models.py` for DB enums; mirror in Pydantic where needed ### JavaScript (frontend) - React 18 functional components only - Named exports for components; default export for pages optional - Tailwind for styling; no CSS-in-JS library - Axios instance with `baseURL` from `import.meta.env.VITE_API_URL` - Env vars prefixed `VITE_` only for client-safe values ### Git - Conventional commits encouraged: `feat:`, `fix:`, `docs:` - One Alembic revision per schema change --- ## 13. Implementation phases ### Phase 0 — Constitution (current) Documentation only; no new application features. ### Phase 1 — Public experience + admin content - Vite + React + Tailwind + Router - Wire public API: home, list, detail, subscribe, outbound go links - Admin: login, edition CRUD, media upload - Share previews + basic SEO - **Aligns with partial backend already in repo** ### Phase 2 — Polish & performance - GSAP hero + scroll sections with reduced-motion guard - Image lazy loading, skeleton states - Accessibility pass (focus, contrast, aria labels) ### Phase 3 — Native ticketing - DB: tiers, orders, tickets - Attendee auth - Checkout + payment webhook - Ticket page + QR generation - Staff check-in UI ### Phase 4 — Hardening - E2E smoke tests, load test on read paths - CDN, backup strategy, runbooks --- ## 14. ADR policy Significant deviations (new dependency, new service, auth model change) require a short `docs/adr/NNN-title.md` before implementation. --- ## 15. Related documents - [API_SPEC.md](./API_SPEC.md) - [DATABASE.md](./DATABASE.md) - [DESIGN_SYSTEM.md](./DESIGN_SYSTEM.md)