Architecture Overview

Stack

LayerTechnology
RuntimeBun
FrontendReact 19 + Redux Toolkit + RTK Query + MUI v6
HTTP serverBun.serve()
BackendController → Service → Repository
ORMDrizzle
DatabasePostgreSQL
BundlerBun (replaces Vite/webpack)
Testsbun: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:

AliasResolves 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 instance

Tests inject mock repositories directly instead of touching the database.

Environment variables

  • Server-side vars: plain names in .env, typed in bun-env.d.ts.
  • Client-side vars: must be prefixed BUN_PUBLIC_ to be inlined at bundle time.
  • Never use dotenv — Bun loads .env automatically.
  • Update both .env.example and bun-env.d.ts whenever a var is added, renamed, or removed.