# 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
