Architecture Overview
Stack
| Layer | Technology |
|---|---|
| Runtime | Bun |
| Frontend | React 19 + Redux Toolkit + RTK Query + MUI v6 |
| HTTP server | Bun.serve() |
| Backend | Controller → Service → Repository |
| ORM | Drizzle |
| Database | PostgreSQL |
| Bundler | Bun (replaces Vite/webpack) |
| Tests | bun:test + Playwright (E2E) |
Request flow
Client → Bun.serve() → Middleware → Controller → Service → Repository → Drizzle → PostgreSQL
- Middleware — validates JWT auth tokens before the request reaches a controller.
- Controller — parses the request, validates input, calls the service, returns a
Response. No business logic or DB queries. - Service — business logic, data transformation, strips sensitive fields. Returns
ErrorOr<T>for expected failures instead of throwing. - Repository — Drizzle queries only. No business logic.
Directory structure
├── backend/
│ ├── controllers/ # parse + validate request → call service → return Response
│ ├── services/ # business logic
│ ├── repositories/ # Drizzle queries
│ ├── middleware/ # JWT auth
│ ├── routes/ # maps HTTP routes to controller methods
│ └── db/
│ ├── schemas/ # Drizzle table definitions
│ └── migrations/
├── frontend/
│ └── features/<name>/
│ ├── components/
│ ├── hooks/
│ ├── logic/
│ ├── state/ # Redux slices
│ └── tests/
├── frontend/shared/ # cross-feature components and hooks
├── frontend/redux/ # RTK Query API slices + store
├── rest/ # HTTP route documentation (one file per domain)
└── docs/ # this vault
Path aliases
Use these when crossing layer boundaries — never ../../ relative imports:
| Alias | Resolves to |
|---|---|
@backend/* | ./backend/* |
@frontend/* | ./frontend/* |
@type/* | ./types/* |
Dependency injection
All controllers and services use factory functions so they can be unit-tested without mocking modules:
export const createUserService = (repo: typeof UserRepositoryType) => ({ ... });
export const userService = createUserService(userRepository); // wired-up instanceTests inject mock repositories directly instead of touching the database.
Environment variables
- Server-side vars: plain names in
.env, typed inbun-env.d.ts. - Client-side vars: must be prefixed
BUN_PUBLIC_to be inlined at bundle time. - Never use
dotenv— Bun loads.envautomatically. - Update both
.env.exampleandbun-env.d.tswhenever a var is added, renamed, or removed.