Files
InvestPlay/docs/CONTEXT.md
Lefteris Notas e75f913f1c
Some checks failed
CI / Lint (push) Has been cancelled
CI / TypeCheck (push) Has been cancelled
CI / Test (push) Has been cancelled
Deploy / Docker Build & Push (map[context:. dockerfile:apps/api/Dockerfile name:api]) (push) Has been cancelled
Deploy / Docker Build & Push (map[context:. dockerfile:apps/cms/Dockerfile name:cms]) (push) Has been cancelled
Deploy / Docker Build & Push (map[context:. dockerfile:apps/web/Dockerfile name:web]) (push) Has been cancelled
CI / Build (push) Has been cancelled
Deploy / Deploy (push) Has been cancelled
feat: complete Phase 1 foundation scaffold
- Monorepo: Turborepo + pnpm workspaces with 7 apps/packages
- Backend: NestJS scaffold with 9 modules (auth, tenant, curriculum, simulation, portfolio, ai-coach, analytics, classroom, gamification)
- Frontend: React 18 + Vite + TailwindCSS + shadcn/ui with 10 pages, 14 UI primitives, 3 Zustand stores
- Database: Prisma schema with 19 models, 13 enums, seed script, multi-tenant ready
- Docker: Dev, production, and Portainer Compose files with Traefik reverse proxy
- Configuration: .env.example (47 vars), Zod validation, frontend-safe env exposure
- i18n: English + Greek locale files (10 files), ICU MessageFormat, react-i18next
- Shared packages: @investplay/types, @investplay/ui, @investplay/i18n, @investplay/utils
- CI/CD: GitHub Actions (lint, typecheck, test, build, deploy, PR checks)
- Documentation: CONTEXT.md, ARCHITECTURE.md (10 Mermaid diagrams), API.md, LOCALIZATION.md, DEVELOPMENT-ROADMAP.md
- Infrastructure: Dockerfiles (dev + prod), nginx configs, backup/healthcheck scripts
- Security: JWT auth guards, role-based access, rate limiting, Helmet, CORS, PII sanitization
2026-06-12 19:32:57 +03:00

18 KiB

InvestPlay — Project Context

Overview

InvestPlay is a cross-platform financial literacy platform targeting users aged 16-25. It combines interactive educational content, realistic market simulations, gamified progression, and AI-powered coaching to teach investing fundamentals. The platform serves both individual learners (B2C) and educational institutions (B2B) through a multi-tenant architecture with schema-level data isolation.

Vision

Make financial literacy universally accessible and engaging for the next generation of investors, starting with EU markets (Greece initial launch) and expanding globally.

Target Users

Persona Age Range Platform Key Need
Self-directed learner 16-25 Web, Mobile Learn investing at own pace, earn XP, compete on leaderboards
Student (classroom) 14-20 Web Complete assigned lessons, participate in live simulations
Teacher 25-55 Web Create classrooms, assign curriculum, monitor progress
Institution admin 30-60 Web, Desktop White-label platform, manage billing, view analytics
Parent (future) 35-55 Web, Mobile Monitor child's progress, manage subscription

Tech Stack

Layer Technology Purpose
Monorepo Turborepo 2.5 + pnpm 9.15 Orchestrate builds, share code, manage dependencies
Backend NestJS 11 + Node.js 20 Modular API server with DI, guards, interceptors
API Protocol GraphQL (Apollo Server 4) Primary CRUD — type-safe, no over-fetching
Real-time Socket.io (NestJS WebSocket gateway) Live simulations, leaderboards, AI streaming
REST Express (NestJS platform) Webhooks (Stripe, Clerk, Resend), health checks
Frontend React 18 + TypeScript + Vite 6 SPA with fast HMR, tree-shaking, code splitting
CSS TailwindCSS 4 + shadcn/ui (Radix primitives) Design tokens, accessible components, dark mode
CMS Payload CMS 3 (self-hosted, headless) Dynamic block-based content, i18n, media library
Database PostgreSQL 16 + Prisma ORM 6 ACID compliance, JSON support, multi-schema tenancy
Cache/Queue Redis 7 + BullMQ (NestJS BullMQ module) Rate limiting, job queues, session store, leaderboards
Storage S3-compatible (MinIO dev / Cloudflare R2 prod) User avatars, PDF reports, CSV market data
Auth Clerk (B2C) / JWT with SAML SSO (B2B) Social login, passwordless, institutional SSO
Email Resend + React Email templates Welcome emails, progress reports, teacher invites
Billing Stripe Connect B2B seat-based subscriptions, usage metering
AI OpenAI / Gemini (via backend proxy) Socratic AI coach with token budget enforcement
Container Docker Compose (dev) + Portainer/Traefik (prod) Consistent environments, auto-SSL, monitoring
CI/CD GitHub Actions -> GHCR -> Portainer webhook Automated testing, building, and deployment
Testing Vitest 3 + Testing Library + Playwright Unit, integration, and E2E coverage
Desktop Tauri v2 (Rust-based, future) Native Windows/macOS/Linux wrapper
Mobile Capacitor (future) Native iOS/Android wrapper

Directory Structure

investplay/
├── apps/
│   ├── api/                       # NestJS backend
│   │   ├── src/
│   │   │   ├── modules/
│   │   │   │   ├── auth/          # JWT, SSO, role guards
│   │   │   │   ├── tenant/        # Multi-tenancy, feature flags
│   │   │   │   ├── curriculum/    # CMS-backed lesson delivery
│   │   │   │   ├── simulation/    # Historical tick streaming
│   │   │   │   ├── portfolio/     # Virtual trades, P&L calc
│   │   │   │   ├── ai-coach/      # LLM proxy with cost control
│   │   │   │   ├── analytics/     # Behavioral telemetry
│   │   │   │   ├── classroom/     # Teacher dashboard, assignments
│   │   │   │   ├── gamification/  # XP, badges, streaks
│   │   │   │   └── billing/       # Stripe Connect integration
│   │   │   ├── common/
│   │   │   │   ├── guards/        # JWT guard, tenant guard, roles guard
│   │   │   │   ├── decorators/    # @CurrentUser, @TenantId
│   │   │   │   ├── filters/       # Global exception filter
│   │   │   │   └── interceptors/  # Logging, timing, tenant injection
│   │   │   ├── config/            # Env-based config modules
│   │   │   └── main.ts            # NestJS bootstrap
│   │   ├── prisma/
│   │   │   ├── schema.prisma      # Full data model
│   │   │   └── migrations/        # Prisma migration history
│   │   ├── test/                  # E2E test suites
│   │   ├── Dockerfile
│   │   └── package.json
│   ├── web/                       # React + Vite frontend
│   │   ├── src/
│   │   │   ├── components/        # UI components (per feature)
│   │   │   ├── pages/             # Route pages
│   │   │   ├── hooks/             # Custom React hooks
│   │   │   ├── stores/            # Zustand stores
│   │   │   ├── lib/               # Utilities, API client
│   │   │   ├── styles/            # TailwindCSS setup
│   │   │   └── i18n/              # i18next configuration
│   │   ├── public/                # Static assets
│   │   ├── index.html
│   │   ├── vite.config.ts
│   │   ├── Dockerfile
│   │   └── package.json
│   ├── cms/                       # Payload CMS
│   │   ├── src/
│   │   │   ├── collections/       # Content models
│   │   │   ├── globals/           # Site-wide settings
│   │   │   └── payload.config.ts
│   │   ├── Dockerfile
│   │   └── package.json
│   ├── mobile/                    # Capacitor (future)
│   └── desktop/                   # Tauri (future)
├── packages/
│   ├── types/                     # @investplay/types
│   │   └── src/
│   │       ├── user.ts
│   │       ├── tenant.ts
│   │       ├── curriculum.ts
│   │       ├── simulation.ts
│   │       ├── portfolio.ts
│   │       └── analytics.ts
│   ├── ui/                        # @investplay/ui
│   │   └── src/
│   │       ├── components/        # shadcn/ui primitives
│   │       └── lib/               # cn(), formatCurrency(), etc.
│   ├── i18n/                      # @investplay/i18n
│   │   └── src/
│   │       ├── locales/
│   │       │   ├── en/            # English translations
│   │       │   │   ├── common.json
│   │       │   │   ├── finance.json
│   │       │   │   ├── gamification.json
│   │       │   │   ├── classroom.json
│   │       │   │   └── ai-coach.json
│   │       │   └── el/            # Greek translations
│   │       │       ├── common.json
│   │       │       └── ...
│   │       └── index.ts           # i18next config
│   └── utils/                     # @investplay/utils
│       └── src/
│           ├── validation.ts      # Zod schemas
│           ├── formatting.ts      # Currency, date, number
│           ├── constants.ts       # App constants
│           └── env.ts             # Env validation
├── docker/
│   ├── traefik/
│   │   ├── traefik.yml            # Static config
│   │   └── dynamic.yml            # Dynamic config (middleware, TLS)
│   └── nginx/
│       └── nginx.conf             # Web SPA nginx config
├── scripts/
│   ├── setup.sh                   # Initial dev setup
│   ├── healthcheck.sh             # Service health verification
│   └── backup.sh                  # Database backup script
├── docs/                          # Project documentation
├── specs/                         # Detailed specifications
├── .github/
│   └── workflows/
│       ├── ci.yml                 # Continuous integration
│       ├── deploy.yml             # Deployment pipeline
│       └── pr-checks.yml          # PR quality gates
├── .env.example                   # Environment template
├── turbo.json                     # Turborepo pipeline config
├── pnpm-workspace.yaml            # Workspace definition
├── tsconfig.base.json             # Shared TS config
├── .eslintrc.js                   # ESLint root config
├── .prettierrc                    # Prettier formatting
└── package.json                   # Root package.json

Architecture Patterns

Multi-tenancy

Approach: Schema-level isolation via PostgreSQL schemas.

  • Each tenant (institution) gets its own PostgreSQL schema
  • TenantContext stored in AsyncLocalStorage — resolved from JWT tenantId claim
  • Prisma multi-schema extension for dynamic schema switching
  • Global/shared tables (lookups, platform config) in public schema
  • Connection pooling per schema via Prisma's connection_limit
  • Tenant resolution pipeline: HTTP request -> JWT middleware -> extract tenantId -> set AsyncLocalStorage -> Prisma middleware applies schema filter

API Design

  • GraphQL is the primary API for all CRUD operations — single endpoint /graphql
  • REST used only for webhooks (Stripe, Clerk, Resend) and /health
  • WebSocket namespaces for real-time features: /simulations (tick streaming), /ai-coach (chat streaming), /classroom (live teacher broadcasts)
  • Input validation via Zod schemas at every API boundary
  • All GraphQL queries have depth limiting (max 7) and cost analysis

Content Management

  • Payload CMS serves as the single source of truth for all educational content
  • Content model: Track -> Module -> Lesson -> Block (dynamic array)
  • Block types: Hero, Text, Image, Video, Quiz, Calculator, BudgetGame, TradeSimulation
  • Frontend renders content via a BlockRenderer component that maps CMS block types to React components
  • CMS localization: each content collection supports locale variants

AI Proxy Architecture

  • All LLM requests route through the NestJS AICoachModule — never direct from client
  • Context injection: backend appends portfolio state, lesson context, and user history to the system prompt
  • Rate limiting via Redis token bucket (per-user daily, per-tenant monthly)
  • BYOK (Bring Your Own Key): institutions can provide their own API key for higher quotas
  • PII sanitization: names, emails, phone numbers stripped before sending to LLM
  • Audit logging: every AI interaction logged with token count, latency, and model used

Security Architecture

  • Authentication: JWT RS256 (asymmetric) — access token (15m) + refresh token (7d)
  • Authorization: Role-based guards (@Roles(ROLE.TEACHER)) + tenant scope enforcement
  • GraphQL safety: Depth limiting (max 7), query cost analysis, persisted operations (future)
  • HTTP hardening: Helmet.js headers, CORS whitelist per tenant, rate limiting (100 req/min default)
  • WebSocket: JWT handshake authentication, room-based isolation per tenant
  • Secrets: Zero secrets in code — all via environment variables, Docker secrets, or secret manager
  • GDPR: Soft-deletes, PII anonymization pipeline, data export API, retention policies

Key Design Decisions

Decision Choice Rationale
Monorepo tool Turborepo Better DX than Nx for this scale; native ESM support; simpler cache config
Package manager pnpm Disk-efficient (hard links), strict dependency resolution, workspace protocol
Backend framework NestJS Modular architecture with DI, decorator-based guards, native GraphQL + WebSocket support
Frontend build Vite (not Next.js) SPA is sufficient (no SSR SEO needed); faster builds; simpler deployment (nginx static)
CMS Payload CMS (self-hosted) TypeScript-native, code-first config, block-based content, built-in i18n, no vendor lock-in
Database ORM Prisma Type-safe queries, migration-first workflow, multi-schema preview feature for tenancy
Auth (initial) Clerk Fast to integrate for B2C; supports social login, passwordless, webhooks
Auth (B2B future) Auth0 / SAML SSO Required for institutional customers with Active Directory / Google Workspace
API protocol GraphQL primary Type-safe contracts, no over-fetching, single endpoint, introspection for DX tools
Real-time Socket.io Mature, fallback transport, room support, NestJS integration
Background jobs BullMQ + Redis Persistence, delayed jobs, rate limiting, job events for monitoring
Styling TailwindCSS + shadcn/ui Utility-first, design tokens, accessible Radix primitives, small bundle
i18n i18next + ICU MessageFormat Industry standard, pluralization, context, rich text in translations
AI cost control Server-side token bucket Hard enforcement not reliant on OpenAI budget alerts; Redis is fast and atomic
Container registry GHCR Free with GitHub, tight integration with Actions, no extra credentials
Deployment Docker Compose + Traefik Simpler than k8s for current scale; Traefik auto-SSL with Let's Encrypt
Desktop (future) Tauri v2 Rust-based, 10x smaller binary than Electron, memory-efficient
Mobile (future) Capacitor Minimal wrapper, access to native APIs, shares web codebase

State Management

State Type Technology Usage
Server state @tanstack/react-query Data fetching, caching, optimistic updates, pagination
GraphQL state Apollo Client (future) When GraphQL adoption increases; for now REST-like via react-query
Client/auth state Zustand Auth session, UI preferences (theme, sidebar), gamification (XP, level)
Form state React Hook Form + Zod All forms with schema validation
URL state React Router v7 Search params, path params for routing
Real-time state Socket.io client Live simulation ticks, leaderboard updates, AI chat streaming

Naming Conventions

Category Convention Example
Files (components) kebab-case user-profile.tsx, simulation-chart.tsx
Files (modules/classes) PascalCase AuthModule.ts, UserService.ts
Variables camelCase isLoading, userData
Functions camelCase getUserById(), formatCurrency()
React components PascalCase UserProfile, SimulationChart
Types PascalCase User, TenantConfig
Interfaces PascalCase (no I prefix) UserRepository, AuthPayload
Enums PascalCase (singular) Role, LessonStatus, TenantTier
Database tables snake_case users, user_progress, classroom_students
Database columns snake_case tenant_id, first_name, created_at
GraphQL types PascalCase User, LessonProgress, TradeInput
GraphQL fields camelCase userId, portfolioValue, totalXp
Environment variables UPPER_SNAKE_CASE DATABASE_URL, JWT_SECRET

Environment Variables

See .env.example for the complete template with all variables and descriptions.

Key groups:

  • APPNODE_ENV, PORT, APP_URL, API_URL, CORS_ORIGINS
  • DATABASEDATABASE_URL, DATABASE_POOL_MIN, DATABASE_POOL_MAX
  • REDISREDIS_URL, REDIS_PASSWORD
  • JWTJWT_SECRET, JWT_EXPIRES_IN, JWT_REFRESH_EXPIRES_IN
  • AUTHCLERK_SECRET_KEY, CLERK_WEBHOOK_SECRET, AUTH_SSO_ENABLED
  • AIOPENAI_API_KEY, GEMINI_API_KEY, AI_DEFAULT_DAILY_LIMIT, AI_DEFAULT_MONTHLY_TOKEN_BUDGET
  • STORAGES3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET
  • EMAILRESEND_API_KEY, EMAIL_FROM
  • BILLINGSTRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET
  • TENANTTENANT_DEFAULT_AI_MODE, TENANT_MAX_TOKENS_MONTHLY
  • i18nDEFAULT_LOCALE, SUPPORTED_LOCALES
  • CMSCMS_API_URL, CMS_API_KEY
  • SECURITYRATE_LIMIT_TTL, RATE_LIMIT_MAX, GRAPHQL_DEPTH_LIMIT
  • LOGGINGLOG_LEVEL, LOG_FORMAT

Testing Strategy

Layer Tool Scope Frequency
Unit Vitest Backend services, utils, hooks, formatting Every commit
Integration Supertest + Vitest API endpoints, GraphQL resolvers Every commit
Component Vitest + Testing Library React component rendering, user interactions Every commit
E2E Playwright Critical user flows (login, lesson, simulation) CI / pre-release
Contract GraphQL schema tests Schema validation, resolver shape On schema change
Coverage target 80%+ Core business logic (services, resolvers, hooks) CI gate

Development Workflow

  1. Create a feature branch from main:
    • git checkout -b feat/my-feature (new feature)
    • git checkout -b fix/my-bug (bug fix)
  2. Start the development environment: docker compose -f docker-compose.dev.yml up
  3. Develop iteratively with hot-reload enabled
  4. Write tests following TDD cycle: Red -> Green -> Refactor
  5. Run quality gates locally:
    pnpm lint
    pnpm typecheck
    pnpm test
    
  6. Commit using conventional commits:
    feat(auth): add SAML SSO login flow
    fix(simulation): correct tick timestamp offset
    docs(api): update websocket event reference
    
  7. Push branch and open a Pull Request
  8. CI runs automatically: lint -> typecheck -> test -> build
  9. PR must pass all checks and receive code review approval
  10. Squash merge to main (keeps history clean)
  11. CI runs again on main, then auto-deploys to production