# Architecture ## Server Dual-server setup: - **server.ts** — HTTP server entry point, initializes Socket.IO - **server/app.ts** — Express app with React Router SSR handler and database context - **server/socket.ts** — Socket.IO server for real-time draft updates - **server/timer.ts** — Runs every second via `setInterval` to advance draft timers. Uses a dedicated DB connection. **Must be started** for draft functionality to work. ## Database Layer - **Schema**: `database/schema.ts` - **Context**: `database/context.ts` — DB connection injected via `AsyncLocalStorage` - **Models**: `app/models/` — all DB queries live here, organized by domain (e.g. `league.ts`, `season.ts`, `draft-pick.ts`) Always query through the model layer — never write Drizzle queries directly in route files. ## Frontend - **Routes config**: `app/routes.ts` — all routes must be explicitly registered here (React Router 7 does not auto-discover files) - **Route files**: `app/routes/` — file naming uses `.` as path separator; `$param` for dynamic segments; `_index.tsx` for index routes - **Components**: `app/components/` — UI primitives in `app/components/ui/` (ShadCN) - **Hooks**: `app/hooks/` — custom React hooks - **Lib**: `app/lib/` — pure utility functions ## Real-time (Socket.IO) Clients join a room per season. Key events: | Event | Direction | Trigger | |---|---|---| | `join-draft` | client → server | on draft page load | | `timer-update` | server → client | every second during active draft | | `pick-made` | server → client | participant drafted | | `autodraft-updated` | server → client | team enables/disables autodraft | | `team-connected` / `team-disconnected` | server → client | presence | Server emits via `getSocketIO()` from `server/socket.ts`. Clients consume via `useDraftSocket()` hook at `app/hooks/useDraftSocket.ts`. ### Adding a Socket Event 1. Define types in `server/socket.d.ts` 2. Add handler in `server/socket.ts` 3. Emit from server: `getSocketIO().to(room).emit(...)` 4. Listen in client component or hook