System Architecture & Tech Stack
The stack is locked and should not be relitigated mid-project.
| Layer | Choice | Why |
|---|---|---|
| Frontend | React + TypeScript + Tailwind CSS | Reusable components for independent, frequently-updating UI pieces (map, leaderboard, editor, contest room); type safety for complex nested data models. |
| Code Editor | Monaco Editor | Same engine as VS Code; strong syntax highlighting, multi-language support, and paste-event interception APIs needed for anti-cheating. |
| Backend | Node.js + NestJS | Opinionated, modular controller/service/module architecture; built-in WebSocket + guard (auth) support; same language as frontend. |
| Database | PostgreSQL | Strongly relational data (User, Problem, Submission, Territory, Contest); ACID transactions needed for territory assignment and daily-limit checks. |
| Real-time | WebSockets via NestJS Gateway | Push-based updates for contest status, timers, and leaderboard/map changes; per-contest rooms give clean isolation between simultaneous matches. |
| Cache / Queue | Redis + BullMQ | Submission queueing at a controlled rate to the judge; leaderboard caching; fast daily-limit counters. |
| Code Execution | Piston (self-hosted) | Lightweight, container-based multi-language sandboxed execution; chosen after Judge0 proved unstable/crash-prone in dev, including on Windows. |
| Deployment | Docker + Docker Compose | Consistent dev/prod environment per service; can extend into Kubernetes later if needed. |
| Auth | JWT + institutional email domain restriction | Stateless auth that integrates easily with WebSocket connections; signup gated to @thapar.edu-pattern emails. |
High-Level Architecture
Client (React) ↕ NestJS API (REST + WebSocket Gateway) ↕ PostgreSQL (primary data) + Redis (cache/queue) ↕ Piston workers (isolated execution)
The backend never executes student code directly — it only ever queues a job to Piston and reads back a verdict.
Data Flow
Submission Flow
- Student writes code in Monaco and clicks Submit.
- Frontend sends
code + language + problemIdtoPOST /submissions. - Backend validates input, creates a
Submissionrow (status: queued), and pushes a job to the Redis/BullMQ submission queue. - A Piston worker picks up the job, runs the code against the problem's test cases inside an isolated container, and returns per-test results.
- Backend writes
TestResultrows and updatesSubmissionstatus (AC/WA/TLE/RE). - On AC, the Scoring Service computes a Performance Score and (if under the daily limit) triggers territory assignment.
- Backend pushes a WebSocket event to the student's client with the verdict; if in an active contest, also broadcasts opponent status to the contest room.
Contest Flow
- Student A sends
POST /challengeswith the target territory owner and (optionally) a problem set. - Student B is notified (via socket if online, else on next dashboard load) and accepts via
POST /challenges/:id/accept. - A
Contestand twoContestParticipantrows are created; both clients join a WebSocket room for that contest. - Each submission during the contest window is scored and broadcast as a status update (never the opponent's code).
- On contest end (time limit or both submit), the Contest Scoring Service determines a winner and updates
TerritoryOwnership.
Scalability Plan
| Phase | Scale Target | Key Techniques |
|---|---|---|
| Phase 1 | Hundreds of concurrent users (initial rollout) | Single backend instance, single Postgres, single Redis, small Piston worker pool. |
| Phase 2 | Thousands of concurrent users | Horizontal backend scaling behind a load balancer; Postgres read replicas; Redis-backed leaderboard cache; independently scaled Piston worker pool; WebSocket gateway scaled with a Redis adapter. |
| Phase 3 (future) | Multi-institution (out of current scope) | Tenant-aware schema or separate deployments per institution; CDN for static assets; Kubernetes replacing Compose. |