Backend Architecture

The backend is a Bun + Hono TypeScript API server with OpenAPI support, running on port 3000 behind nginx. Entry point: backend/src/index.ts.

Layered Architecture

  HTTP Request                    BullMQ Job
       │                               │
  ┌────▼─────┐                         │
  │ Middleware │  security → cors → sanitization → rateLimiter → auth
  ├───────────┤                  ┌─────▼──────┐
  │  Routes   │  OpenAPI handlers │ Processors │   ← presentation (HTTP / batch)
  ├───────────┤                  └─────┬──────┘
  │ Services  │  actor, authz, Zod, DTO │
  ├───────────┴─────────────────────────▼┐
  │ Domain modules                       │  operations, invariants — sole Drizzle owner
  ├───────────────────────────────────────┤
  │    DB     Drizzle ORM, schema         │
  └───────────────────────────────────────┘

Routes delegate to services; services delegate persistence to domain modules; job processors consume domain modules directly (peer clients of services, no services slice). Legacy direct DB access is allowlisted and migrates on touch — see Persistence Topology (I#2013) and Service Layer Pattern.

Dependency Injection

The backend uses a DI container pattern through Hono’s context variables. See Dependency Injection for full details.

// In route handlers — access services via c.var
const { documentRequests, portal } = c.var.services;
 
// Infrastructure and cross-cutting concerns
const { logger, cache } = c.var.container.infrastructure;

Container is initialized at startup and injected into every request via middleware. Service interfaces are defined in backend/src/interfaces/ (71 files).

Directory Structure

File counts as of 2026-06 (excluding co-located tests):

DirectoryFilesPurpose
src/routes/199API endpoint handlers with OpenAPI schemas
src/services/148Business logic (persistence delegated to domain modules)
src/lib/163Shared utilities + domain modules (jobs/, billing/, file-operations/)
src/interfaces/71TypeScript interfaces for DI contracts
src/middleware/11Request processing pipeline
src/db/Schema, mocks, shared schemas
src/test/Shared test infrastructure (new tests are co-located)

Middleware Stack

Processing order defined in backend/src/index.ts. See Backend Middleware and Middleware Stack for details.

MiddlewareFilePurpose
Securitymiddleware/security.tsCSP, HSTS, X-Frame-Options headers
CORS(Hono built-in)Cross-origin request handling
Sanitizationmiddleware/sanitization.tsDOMPurify XSS protection
Rate Limitermiddleware/rateLimiter.tsAuth: 5/15min, API: 100/min
Authmiddleware/auth.tsJWT verification, user context
Portal Authmiddleware/portal-auth.tsExternal stakeholder auth
RBACmiddleware/rbac.tsRole-based access control
Internal Usermiddleware/require-internal-user.tsRestrict routes to internal employees
Audit Loggermiddleware/auditLogger.tsAction audit trail
Error Handlermiddleware/errorHandler.tsStructured error responses
Timeoutmiddleware/timeout.tsRequest timeout enforcement

Key Libraries

ModulePathPurpose
Authsrc/lib/auth.tsJWT, argon2id password hashing via Bun.password
Encryptionsrc/lib/encryption.tsAES-256-GCM via ENCRYPTION_KEY for OAuth tokens, PII
Sanitizationsrc/lib/sanitization-helpers.tsstrictTextField, sanitizedLocalizedText
Ownership Guardssrc/lib/ownership-guards.tsverifyProjectAccess(), verifyBuildingAccess()
Storagesrc/lib/storage.tsTigris S3 client — stream-based access goes through FileService (buffer helpers deprecated)

Testing

  • Runner: Bun’s built-in test runner
  • New tests are co-located with production code: .unit.test.ts (no DB/network) and .integration.test.ts (real DB or HTTP routes) — convention from PR#1690; legacy tests under src/test/{unit,integration}/ stay put
  • Shared test infrastructure: src/test/ (setup.ts, utils/db.ts, fixtures/)
  • Helpers: setupTestServer(), resetTestDatabase(), createAuthenticatedTestUser(), apiRequest()
  • Coverage: 70% threshold; CI runs backend tests in 3 shards with merged coverage + diff-coverage gate (PR#2000)

See Error Handling Pattern and Validation Pattern for request/response handling conventions.