System Architecture
Understanding the architecture of Bitrate — a modern Turborepo monorepo.
🏗️ Repository Structure
bitrate/
├── apps/ # Applications
│ ├── api/ # Backend API (NestJS)
│ ├── web-player/ # Web app (Next.js App Router, Feature-Sliced Design)
│ ├── web-artists/ # Artists web app (TanStack Start)
│ ├── mobile/ # Mobile app (React Native + Expo)
│ ├── desktop/ # Desktop app (Tauri 2 + React)
│ └── docs/ # Documentation (Docusaurus 3)
│
├── packages/ # Shared packages (@bitrate/ namespace)
│ ├── ui-react/ # React 19 component library (Tailwind v4, shadcn/ui, Storybook)
│ │ # also owns the design tokens: hand-written @theme layers
│ ├── contracts/ # OpenAPI TypeScript types (auto-generated from Swagger)
│ ├── vite-svgr/ # Vite plugin — SVG generation integrated into Vite build
│ ├── svgr/ # SVG → typed React component converter
│ ├── converter/ # Media/audio conversion utilities (FFmpeg wrapper)
│ └── ncs-parser/ # NCS (audio format) parser
│
├── infra/ # Docker Compose files + shell scripts
│ ├── docker-compose.dev.yaml # Minimal: postgres + redis
│ ├── docker-compose.preprod.yaml # Full dev stack
│ ├── docker-compose.prod.yaml # Production
│ ├── nginx/ # Nginx configuration
│ └── docker-monitor.sh # health/resource/db reporting — see `task monitor:*`
│
├── scripts/ # Cross-platform helper scripts (Node.js)
├── Taskfile.yml # Cross-platform task runner (go-task)
├── CLAUDE.md # AI assistant guide for this codebase
└── .changeset/ # Changesets versioning config
🎯 Design Principles
1. Monorepo Structure
All applications and packages live in a single repository, managed by pnpm workspaces and Turborepo.
Benefits:
- Shared code across projects
- Atomic commits across multiple apps
- Centralized dependency management
- Faster CI/CD with Turbo caching
2. Type Safety
TypeScript is used throughout the entire stack:
- @bitrate/contracts — Shared types between frontend and backend (generated from Swagger)
- Prisma — Type-safe database access
- Zod — Runtime validation and env schema (
apps/api/env.schema.ts)
3. Clean Architecture
Backend (NestJS)
Each feature lives in its own module under apps/api/src/modules/<feature>/
(controller/service/module plus decorators, DTOs, entities, errors), with
apps/api/src/common/ and apps/api/src/infra/ for cross-cutting utilities and
infrastructure. Path aliases: @modules/, @infra/, @common/, @test/. Three Jest test
tiers (.unit-spec.ts, .int-spec.ts, test/e2e/**/*.e2e-spec.ts). Full module anatomy,
the Swagger-decorator rule, and test conventions: api-rules (.claude/rules/api-rules.md)
and the jest skill.
Frontend (Feature-Sliced Design)
apps/web-player/src/ is organized in FSD layers (app → views → widgets → features → entities → shared), each importing only from the layers below it. Full layer anatomy, the
cross-layer import permission matrix, and the public-API barrel rule:
.claude/rules/fsd-web-player.md; the API client, state management, and component
conventions built on top of it: web-player-rules
(.claude/rules/web-player-rules.md).
API client (src/shared/api/client/):
fetchClient.ts—openapi-fetchwith automatic JWT refresh middlewarereactQueryClient.ts—openapi-react-querywrapper
📐 Architecture contracts
The invariants every change is reviewed against. Each is owned by a rule file or an ADR, which is where the reasoning lives — this list is the summary, not the source.
- Web-player imports flow one way.
app → views → widgets → features → entities → shared, never upward, and never sideways between slices of the same layer. Biome enforces it, so a violation failspnpm lintrather than review (ADR-0002). - Next.js route files are adapters. A
page.tsxreads params and server data and renders a view fromviews/; a full screen is never composed inside the route file (ADR-0010). - API contracts are generated, not written. Swagger decorators produce the OpenAPI document,
@bitrate/contractsis generated from it, and both web apps consume those types — so a backend change that breaks a frontend shows up as a type error (ADR-0004). - Server state is TanStack Query; client state is per-slice Zustand. New client state belongs to the slice that owns it, not to a global store (ADR-0005).
- Shared React primitives and design tokens both live in
@bitrate/ui-react. The tokens are hand-written Tailwind@themelayers, not generated output (ADR-0023). - User-facing web UI targets WCAG 2.2 AA — semantic controls, visible focus, keyboard operation, reduced motion, and usable layout at 320 CSS px and 400% zoom.
🔄 Data Flow
Authentication Flow
Music Streaming Flow
📦 Package Dependencies
Dependency Graph
Package Relationships
| Package | Depends On | Used By |
|---|---|---|
@bitrate/ui-react | @bitrate/tailwind | web-player, mobile, desktop |
@bitrate/tailwind | — | ui-react, web-player, web-artists, admin |
@bitrate/contracts | — | api, web-player, mobile, desktop |
@bitrate/converter | — | api |
@bitrate/vite-svgr | @bitrate/svgr | ui-react (build-time SVG generation) |
@bitrate/svgr | — | vite-svgr |
🎨 Design Token Pipeline
The design values are hand-written Tailwind v4 @theme layers under
packages/tailwind/src/ — there is no generator and no tokens.json:
tailwind/src/
├─ palette.css raw colour scales
├─ layout.css spacing, radii, shadows, breakpoints, z-index
├─ typography.css families, sizes, weights
└─ themes.css barrel → themes/{base,global/*,components/*}.css
The default theme is declared in @theme; every other theme overrides the same roles under
a :root.{name} selector in the same part-file. See
the design-token contract.
🗄️ Database Schema
Core Entities
🚀 Deployment Architecture
Production Setup
Container Services (docker-compose.prod.yaml)
- nginx — Reverse proxy (ports 80/443)
- api — NestJS backend (port 3000)
- web — Next.js web-player (port 3001)
- postgres — Primary database (port 5432)
- redis — Session store & cache (port 6379)
⚡ Performance Optimizations
Backend
- Connection pooling — Prisma connection management
- Query optimization — Database indexing
- Caching — Redis for sessions and frequently accessed data
- Background jobs — BullMQ for async tasks (track processing, emails)
Frontend
- Code splitting — Next.js automatic splitting
- Image optimization — Next.js Image component
- Incremental builds — Turbo caching
- Bundle optimization — Vite library mode for packages
Build System
- Turborepo — Incremental builds with remote caching
- pnpm — Fast, disk-efficient package manager
- Vite — Fast library builds with Rollup, watch mode, dev server integration
- Tailwind v4 — Rust-based, microsecond rebuilds
🔐 Security
Authentication
- JWT tokens — Access + Refresh token pattern
- HTTP-only cookies — Refresh token storage
- CORS — Configured for specific origins
- Rate limiting — Prevents brute force attacks
- 2FA — TOTP-based two-factor authentication
- OAuth 2.0 — Google, Facebook, Discord
Data Protection
- Password hashing — SHA-3
- SQL injection protection — Prisma parameterized queries
- XSS protection — React automatic escaping + CSP
- CSRF protection — Token-based validation
- Helmet — Security HTTP headers
📊 Monitoring & Logging
Application Monitoring
- Health checks —
/healthendpoints - Error tracking — Sentry
- Structured logging — nestjs-pino
- Metrics — Prometheus + Grafana
Development Tools
- Swagger — Interactive API explorer at
/swagger - Prisma Studio — Database GUI
- Storybook — UI component catalog (port 6006)
🔄 CI/CD Pipeline
20+ GitHub Actions workflows in .github/workflows/:
- Per-app workflows:
api.yml,web_player.yml,mobile.yml,desktop.yml - Shared reusable workflows:
*_reusable.yml - Cross-cutting:
monitoring.yml,security.yml,release.yml
push/PR → build → test → lint → type-check → deploy
See .github/workflows/README.md for detailed CI/CD documentation.
📦 Versioning
The project uses Changesets for version management:
pnpm changeset # describe change, select bump type
pnpm changeset:version # apply changesets → bump versions + CHANGELOG
Releasing runs on master, not develop. Today release.yml is triggered by a push to master,
consumes the pending changesets, bumps every changed workspace, writes its CHANGELOG.md, tags each
bump, and creates one release tag; it then publishes a GitHub Release, dispatches the five image
builds at that tag, and dispatches the deploy, which waits for one manual approval.
That is being reworked. ADR-0029 proposes
cutting a release branch from develop into a pull request against master, so that a human's
merge is what triggers the build and deploy, and tagging each release with the product's semantic
version derived from the changesets it consumes. It is a proposal, not yet implemented — see
Deployment for both the current and the proposed procedures.
Next: Development Setup — Get your local environment ready.