Architecture

This guide provides a high-level overview of the backend and frontend architecture.

Backend Architecture

Layered Pattern

The backend follows a layered architecture with constructor-based dependency injection:

Router → Controller → Service → Repository / Integration
  • Routers — Hono routes with Zod-validated OpenAPI schemas. Each module registers routes via app.openapi(). Security (Cognito auth) is applied per-route.
  • Controllers — Compose service methods and extract auth context. Every controller has an interface and a mock implementation for testing.
  • Services — Business logic. Pure functions where possible. No direct HTTP or database access.
  • Repositories — DynamoDB operations via the document client. Method naming convention: getXyz, findXyzByAbc, createXyz, updateXyz, upsertXyz, deleteXyz.
  • Integrations — External service clients (AWS Secrets Manager, Cognito, TimeBack API).

Dependency Injection (Container Pattern)

All classes receive dependencies through constructor parameters. Container functions in backend/entrypoints/containers/ wire the full dependency graph (services → controllers → routers). See any container file for the pattern.

Modules

Module Purpose
authentication Sign-up, LTI launch, magic links
chat AI streaming chat with MCP tools
hello-world Example endpoint
common Shared utilities, docs router, error handling

Entry Points

The backend has multiple Lambda entry points, each with its own container:

Handler Path Purpose
api-handler.lambda.ts Core API routes Authentication, hello-world, common
chat-api-handler.lambda.ts Chat routes AI streaming chat with long timeout
cognito-trigger.lambda.ts Cognito triggers Custom auth challenges (magic links)

Each handler bootstraps its own dependency container, keeping Lambda cold starts focused on only the required dependencies.

Local Development Server

The backend/debug/dev.ts file composes all handlers into a single Hono app running on port 3001. It adds credential refresh (every 45 minutes), CORS headers, and the Scalar documentation viewer.

Frontend Architecture

Module Structure

The frontend is organized into feature modules under frontend/src/:

Module Purpose
main/ Core infrastructure — routing, store, services, utilities
common/ Reusable components, hooks, and utilities
config/ Environment configuration management
user-management/ Authentication screens and user state
chat/ AI chat interface

Each module follows a consistent internal structure — see frontend/docs/module-definition/ for the full specification. Key conventions:

  • Screens are lazy-loaded route entry points
  • Slices use Redux Toolkit for state management
  • Services are singletons accessed via service-container.service.ts

State Management

The frontend uses Redux Toolkit with the following patterns:

  • Slices define reducers and actions for each feature
  • Thunks handle async operations (API calls, side effects)
  • Selectors provide derived state with memoization
  • Persisted slices survive page refreshes via Redux Persist

API Integration

The frontend consumes the auto-generated API client from shared/ts/api/generated/. The client is wrapped in a service singleton (ApiClientService) that handles:

  • Base URL configuration (switches per environment)
  • Authentication token injection
  • Error handling and retry logic

Usage pattern:

import { getApiClientService } from '@/main/services/service-container.service';

const client = getApiClientService().getClient();
const response = await client.helloWorld.getHelloWorld();

Environment Switching

The frontend supports runtime environment switching via the ?setEnv query parameter. Environment configs (API URLs, Cognito settings) are loaded from a JSON file hosted on S3, allowing the same frontend build to target any backend.