updating documentation

This commit is contained in:
lila 2026-05-16 01:59:43 +02:00
parent 1ba57c7e9d
commit 7e0311683f
25 changed files with 2660 additions and 226 deletions

View file

@ -0,0 +1,156 @@
# 01 — Architecture
> **Purpose:** Give an LLM the structural context needed to navigate the codebase and understand data flow. Concatenate with 00-project-overview.md and 99-current-task.md.
> **Last updated:** 2026-05-15
> **Depends on:** 00-project-overview.md
---
## Monorepo Boundaries
```
lila/
├── apps/
│ ├── api/ — Express backend: routers, controllers, services, WS handlers
│ └── web/ — React frontend: routes, components, hooks, client state
├── packages/
│ ├── shared/ — Zod schemas, constants, derived types. THE CONTRACT.
│ └── db/ — Drizzle schema, migrations, models (termModel, lobbyModel), seeding
├── data-pipeline/ — Kaikki extraction → enrichment → sync to PostgreSQL
└── documentation/ — Human docs + ai-context/
```
**Critical rule:** `apps/api` never imports `drizzle-orm` for queries. It only calls functions exported from `packages/db`. All database code lives in `packages/db`.
---
## Layered Architecture (HTTP)
```
HTTP Request
Router — maps URL + method to controller (Express Router)
Controller — validates input (Zod safeParse), calls service, sends response
or next(error) for errorHandler middleware
Service — business logic only. No HTTP, no direct DB access.
Calls model functions from packages/db.
Model — database queries only. No business logic.
Lives in packages/db/src/models/
Database — PostgreSQL via Drizzle ORM
```
**Error flow:** Controller throws `ValidationError` (400) or `NotFoundError` (404) → caught by `errorHandler` middleware in `app.ts` → mapped to HTTP status. Unknown errors → 500.
---
## WebSocket Architecture
The WS server attaches to the same Express HTTP server. Upgrades on `/ws` path.
```
WS Connection Upgrade
Auth middleware — validates Better Auth session from cookie on upgrade
Message Router — dispatches by `type` field (Zod discriminated union)
Handler (lobby or game) — business logic, broadcasts state to room
In-memory stores (lobby game state, game session state)
```
**Message protocol:** All WS messages validated against Zod schemas in `packages/shared/src/schemas/lobby.ts` and `packages/shared/src/schemas/game.ts`. Router switches on `type` field.
**State storage:**
- Lobby membership → PostgreSQL (`lobbies`, `lobby_players` tables) — durable
- Game/room state → in-memory (`InMemoryLobbyGameStore`, `InMemoryGameSessionStore`) — ephemeral, lost on restart. Valkey migration planned.
---
## Data Flow: Singleplayer Quiz
```
POST /api/v1/game/start (GameRequestSchema)
Controller validates → Service.createGameSession
termModel.getGameTerms(filters) + termModel.getDistractors(filters)
Service shuffles options, stores session in GameSessionStore
Returns GameSession { sessionId, questions[] }
[frontend] User selects option → confirms → POST /api/v1/game/answer
Service evaluates server-side (correct answer NEVER sent to frontend)
Returns AnswerResult { isCorrect, correctOptionId, selectedOptionId }
```
**Key design:** Correct answer is stored server-side only (in GameSessionStore). Frontend only sees `optionId` (03) and `text`. Prevents cheating.
---
## Data Flow: Multiplayer Game
```
Host creates lobby → POST /api/v1/lobbies → returns room code (e.g. WOLF-42)
Players join via code → POST /api/v1/lobbies/:code/join
All players WS connect → send lobby:join with room code
Server broadcasts lobby:state (player list) to all in room
Host clicks "Start" → WS lobby:start
MultiplayerGameService generates questions, broadcasts game:question
Players submit answers via WS game:answer within 15s server timer
On all-answered or timeout → evaluate, broadcast game:answer_result
After N rounds → broadcast game:finished with final scores
```
---
## GameSessionStore Abstraction
```typescript
// packages/shared/src/schemas/game.ts (interface defined in apps/api)
interface GameSessionStore {
createSession(session: GameSession): Promise<void>;
getSession(sessionId: string): Promise<GameSession | null>;
// ...
}
```
**Current:** `InMemoryGameSessionStore` — Map-based, process memory, lost on restart.
**Planned:** `ValkeyGameSessionStore` — Redis-compatible, persists across restarts.
Same pattern for `LobbyGameStore`.
---
## Key Files by Concern
| Concern | Key Files |
| --------------- | -------------------------------------------------------------------------------------- |
| HTTP routing | `apps/api/src/routes/apiRouter.ts`, `gameRouter.ts`, `lobbyRouter.ts` |
| Controllers | `apps/api/src/controllers/gameController.ts`, `lobbyController.ts` |
| Services | `apps/api/src/services/gameService.ts`, `multiplayerGameService.ts`, `lobbyService.ts` |
| Models | `packages/db/src/models/termModel.ts`, `lobbyModel.ts` |
| WS handlers | `apps/api/src/ws/handlers/gameHandlers.ts`, `lobbyHandlers.ts` |
| WS router | `apps/api/src/ws/router.ts` |
| WS auth | `apps/api/src/ws/auth.ts` |
| Shared schemas | `packages/shared/src/schemas/game.ts`, `lobby.ts`, `auth.ts` |
| Constants | `packages/shared/src/constants.ts` |
| DB schema | `packages/db/src/db/schema.ts` |
| Auth config | `apps/api/src/lib/auth.ts` |
| Auth middleware | `apps/api/src/middleware/authMiddleware.ts` |