Architecture¶
Dictionary is a classic SPA + REST API split into two independent
sub-projects: dashboard/ (client) and backend/ (API server). The
only contact point between them is HTTP/JSON — there is no shared code;
each sub-project has its own dependency tree.
+----------------------+ HTTPS/JSON +------------------------+
| dashboard/ (SPA) | ------------------------> | backend/ (REST API) |
| React 18 + Vite | /api/v1/... (JWT) | Express + TypeScript |
| Ant Design + Redux | <------------------------ | |
| Toolkit | | |
+----------------------+ +------------+-----------+
| mongodb://
v
+----------------+
| MongoDB |
| (never |
| containerized, |
| runs |
| externally) |
+----------------+
Backend layers (backend/src/)¶
Every business area (auth, dictionary, people) follows the same
4-layer pattern under backend/src/modules/<area>/:
| Layer | File | Responsibility |
|---|---|---|
| Route | <area>.routes.ts |
Express router — wires URLs to controllers |
| Controller | <area>.controller.ts |
HTTP req/res, status codes, error mapping |
| Service | <area>.service.ts |
Business rules (authorization checks, validation flow) |
| Repository | <area>.repository.mongo.ts |
MongoDB access (the single real persistence point) |
| Types/Validation | <area>.types.ts, <area>.validation.ts |
Type definitions, request schema validation |
This layering is what lets tests (see Testing) swap the repository for an in-memory fake and run all business logic without a real MongoDB.
Other top-level directories:
backend/src/config/— environment variable loading (env.ts), DB connection (db.ts)backend/src/middleware/— JWT verification, permission checks, rate limiting, error handlersbackend/src/docs/— Swagger/OpenAPI generation (see Backend API)backend/src/legacy/— old endpoints kept for backward compatibilitybackend/src/tests/+backend/src/tests/support/— the Vitest suite and its in-memory fake repositories
Frontend layers (dashboard/src/)¶
See Frontend / Dashboard for details. In short:
routes/ (React Router definitions plus the requireAuth/
requirePermission guard components), store/ (Redux Toolkit slices),
services/ (API client), components/ (feature-scoped UI, e.g.
dictionary/, users/, auth/, settings/, theme/).
Inter-service communication¶
- Development (hot reload): Vite's own dev-server proxy
(
vite.config.ts) forwards/apirequests to the backend; dashboard code always uses relative paths (/api/v1/...) —VITE_API_BASE_URLis intentionally ignored in dev. - Production build: the build-time env var
VITE_API_BASE_URL(seedashboard/src/utils/index.tsx) sets the backend's real address — the client is no longer behind a dev-server proxy. - Docker (development):
docker-compose.ymlpublishes the backend under thedictionary-backendservice name on Docker's internal DNS; the dashboard container usesVITE_DEV_API_PROXY_TARGET=http://dictionary-backend:3001to reach it. - Docker (production):
docker-compose.prod.ymlserves static files vianginx(inside the dashboard image); the backend comes up as its own container/image with ahealthcheck, and the dashboard depends on it viadepends_on: condition: service_healthy.
Database¶
MongoDB is never containerized or managed by this project — it
connects to whatever MONGODB_URI in backend/.env points to (host
machine, another server on the LAN, etc.). In Docker mode this is
usually resolved to the host machine via Docker's host.docker.internal
DNS alias (see extra_hosts in docker-compose.yml /
docker-compose.prod.yml).
Relationship to deployment/CI¶
Docker images and docker compose validation are built in CI purely for
LOCAL VALIDATION — nothing is ever pushed to a registry (see
Git & CI/CD). The real production install runs
via scripts/windows/install.ps1 / scripts/linux/install.sh,
completely independent of CI (see
Installation / Update / Uninstall).