Skip to main content

ARC // OS — Architecture Documentation

Last Updated: 2026-01-18


Overview​

ARC // OS is a full-stack TypeScript application built with a service-oriented architecture. The system follows strict design principles to ensure maintainability, testability, and scalability.


System Architecture​

High-Level Architecture​

┌─────────────────┐
│ React Frontend │ (Vite + TypeScript)
│ (Port 3000) │
└────────┬────────┘
│ HTTP/REST
│
┌────────▼────────┐
│ Fastify API │ (TypeScript)
│ (Port 3001) │
└────────┬────────┘
│
┌────────▼────────┐
│ PostgreSQL │ (Database)
│ (Port 5432) │
└─────────────────┘

Technology Stack​

Backend:

  • Fastify 4.x - High-performance HTTP server
  • TypeScript - Strict mode, no any types
  • Prisma ORM - Type-safe database access
  • PostgreSQL - Relational database
  • Zod - Runtime validation
  • JWT - Authentication (httpOnly cookies)
  • Pino - Structured logging

Frontend:

  • React 18 - UI framework
  • TypeScript - Strict mode
  • Vite - Build tool
  • React Router - Navigation
  • CSS Modules - Scoped styling
  • Lucide React - Icon library

Testing:

  • Vitest - Unit, integration, component tests
  • Playwright - E2E tests
  • Test Database - Separate from development

Backend Architecture​

Service Layer Pattern​

The backend follows a service-oriented architecture where:

  1. Routes are thin - Routes handle HTTP concerns only (request/response, validation, authentication)
  2. Services contain business logic - All business rules, data manipulation, and domain logic live in services
  3. Services are pure and testable - Services are classes with methods that can be tested in isolation

Example Structure​

/routes
└── task.routes.ts # HTTP handlers (thin)
/services
└── task.service.ts # Business logic (thick)
/utils
└── auth.ts # Utilities

Route Handler Pattern​

// Route: Thin HTTP handler
fastify.post('/api/tasks', async (request, reply) => {
const user = await authenticateRequest(request);
if (!user) {
sendError(reply, 'UNAUTHORIZED', 'Not authenticated', 401);
return;
}

try {
const body = createTaskSchema.parse(request.body);
const service = new TaskService();
const task = await service.create(user.id, body);
sendSuccess(reply, task, 201);
} catch (error) {
// Error handling
}
});

Service Pattern​

// Service: Business logic
export class TaskService {
async create(userId: string, input: CreateTaskInput): Promise<unknown> {
// Validation
if (!input.title) {
throw new Error('Title is required');
}

// Business rules
if (input.timebox && input.timebox > 480) {
throw new Error('Timebox cannot exceed 8 hours');
}

// Data access
return prisma.task.create({
data: {
userId,
title: input.title,
timebox: input.timebox,
// ...
},
});
}
}

Database Architecture​

Prisma ORM​

  • Schema-first - Database schema defined in schema.prisma
  • Migrations - Version-controlled database changes
  • Type-safe - Generated TypeScript types from schema
  • Relations - Heavily relational design (no arrays/JSON columns)

Relational Design Principle​

⚠️ CRITICAL RULE: Never use arrays or JSON columns. Use linked tables instead.

Bad:

model Task {
tags String[] // ❌ NO ARRAYS
metadata Json // ❌ NO JSON
}

Good:

model Task {
tags TaskTag[]
}

model TaskTag {
taskId String
tagId String
task Task @relation(...)
tag Tag @relation(...)
}

Key Models​

  • User - Core user entity
  • Week - Monday-Sunday container
  • Day - 24-hour period (atomic unit)
  • Task - Work items with timeboxes
  • TrainingSession - Gym workouts
  • Exercise - Reusable exercise definitions
  • PerimeterTask - Reusable daily focus tasks
  • ChecklistItem - Daily habits
  • Project - Work/study projects
  • ProjectTask - Kanban board tasks
  • Person - Social contacts
  • SocialHappening - Social events
  • Transaction - Financial transactions
  • Item - Personal items database
  • Outfit - Outfit definitions
  • File - File uploads with variants

Authentication & Authorization​

JWT Authentication​

  • Token Storage - httpOnly cookies (secure, not accessible via JavaScript)
  • Token Generation - JWT signed with JWT_SECRET
  • Token Validation - Middleware validates token on protected routes
  • Session Tracking - Sessions logged for security auditing

User Ownership​

All services verify user ownership before operations:

const task = await prisma.task.findFirst({
where: {
id: taskId,
userId, // Always verify ownership
},
});

if (!task) {
throw new Error('Task not found');
}

File Storage Architecture​

Adapter Pattern​

File storage uses the adapter pattern for flexibility:

IFileStorageAdapter (interface)
├── DiskStorageAdapter (development/local)
└── S3StorageAdapter (production - future)

Benefits:

  • Easy to swap storage backends
  • Consistent interface
  • Testable with mocks

Image Processing​

  • Multiple Variants - Automatic resizing (thumbnail, small, medium, large, original)
  • Eager/Lazy Processing - Configurable generation mode
  • Sharp Library - High-performance image processing

Error Handling​

Consistent Error Format​

{
ok: false,
error: {
code: "ERROR_CODE",
message: "Human-readable message",
details: { ... }
}
}

Error Codes​

  • UNAUTHORIZED - Authentication required
  • NOT_FOUND - Resource not found
  • VALIDATION_ERROR - Invalid input
  • INTERNAL_ERROR - Server error

Error Handling Pattern​

try {
// Business logic
} catch (error) {
if (error instanceof z.ZodError) {
sendError(reply, 'VALIDATION_ERROR', 'Invalid input', 400, { errors: error.errors });
return;
}
if (error instanceof Error && error.message.includes('not found')) {
sendError(reply, 'NOT_FOUND', error.message, 404);
return;
}
sendError(reply, 'INTERNAL_ERROR', 'Operation failed', 500);
}

Validation​

Zod Schemas​

All API inputs validated with Zod:

const createTaskSchema = z.object({
title: z.string().min(1).max(500),
timebox: z.number().int().positive().max(480),
categoryId: z.string().uuid().optional(),
});

Benefits:

  • Runtime validation
  • Type inference
  • Detailed error messages
  • Prevents invalid data

Frontend Architecture​

Component Structure​

/src
/components # Reusable UI components
/pages # Page components
/styles # CSS modules and design tokens
/utils # Utilities (API client, auth)
/types # TypeScript types

Design System​

CSS Modules​

All styles use CSS Modules for scoped styling:

import styles from './Button.module.css';

<button className={styles.button}>Click</button>

Design Tokens​

Centralized design tokens in variables.css:

:root {
--bg: #ffffff;
--fg: #000000;
--muted: #666666;
--border: #e0e0e0;
--space-1: 0.25rem;
--space-2: 0.5rem;
/* ... */
}

Monochrome Design​

  • Black, white, grayscale only - No colors, no gradients
  • Technical aesthetic - Clean, minimal, functional
  • Icon-first - Lucide React icons for UI elements

State Management​

React Hooks​

  • Local State - useState for component state
  • Context - useAuth() for authentication
  • No Global State Library - Keep state local or in context

API Client Pattern​

All API calls go through centralized api utility:

import { api } from '@/utils/api';

const response = await api.post<Task>('/api/tasks', {
title: 'Task title',
timebox: 60,
});

if (response.ok) {
// Handle success
} else {
// Handle error
}

Benefits:

  • Consistent error handling
  • Automatic cookie inclusion
  • Type-safe responses
  • Centralized configuration

Routing​

React Router​

  • Protected Routes - Authentication required
  • Nested Routes - Organized by feature
  • Deep Linking - Direct navigation to specific pages

Code Splitting​

  • Lazy Loading - Pages loaded on demand
  • React.lazy() - Dynamic imports
  • Suspense - Loading fallbacks

Data Flow​

Request Flow​

1. User Action (Frontend)
↓
2. API Call (api utility)
↓
3. HTTP Request (Fastify)
↓
4. Authentication (auth middleware)
↓
5. Route Handler (validation)
↓
6. Service Layer (business logic)
↓
7. Database (Prisma)
↓
8. Response (JSON)
↓
9. Frontend Update (React state)

Data Relationships​

Heavily Relational Design:

  • User → Week → Day → ChecklistItem, Task, TrainingSession, etc.
  • Task → TaskCategory (many-to-many)
  • TrainingSession → Exercise → TrainingEntry
  • SocialHappening → Person (many-to-many via guests)
  • Transaction → Account, SocialHappening (optional)
  • Item → Outfit → OutfitWear → Day

Benefits:

  • Cross-domain analytics
  • Pattern detection
  • Predictive power
  • Holistic management

Testing Architecture​

Test Structure​

/server/src
/routes/__tests__ # Integration tests (API endpoints)
/services/__tests__ # Unit tests (business logic)
/utils/__tests__ # Unit tests (utilities)

/web/src
/components/__tests__ # Component tests
/pages/__tests__ # Page tests
/utils/__tests__ # Utility tests

/e2e # E2E tests (Playwright)

Test Categories​

  1. Unit Tests - Services, utilities, business logic
  2. Integration Tests - API endpoints with real database
  3. Component Tests - React components
  4. Page Tests - Full page components
  5. E2E Tests - Critical user journeys

Test Database​

  • Separate Database - Isolated from development
  • Automatic Cleanup - Reset between tests
  • Migrations Applied - Schema synced before tests

Security Architecture​

Authentication​

  • JWT Tokens - Signed with secret
  • httpOnly Cookies - Not accessible via JavaScript
  • Secure in Production - HTTPS only
  • Session Tracking - Logged for auditing

Authorization​

  • User Ownership - All operations verify ownership
  • No Cross-User Access - Users can only access their own data
  • Service Layer Enforcement - Authorization in services, not routes

Data Validation​

  • Zod Schemas - All inputs validated
  • Type Safety - TypeScript strict mode
  • SQL Injection Prevention - Prisma parameterized queries
  • XSS Prevention - React automatic escaping

File Upload Security​

  • File Type Validation - Only allowed MIME types
  • Size Limits - Configurable max file sizes
  • Path Sanitization - Prevent directory traversal
  • User Isolation - Users can only access their own files

Deployment Architecture​

Development​

Local Development:
- Frontend: Vite dev server (port 3000)
- Backend: Fastify dev server (port 3001)
- Database: Local PostgreSQL (port 5432)

Production (Docker)​

Docker Compose:
- Frontend: Nginx (serves static files)
- Backend: Node.js (Fastify)
- Database: PostgreSQL

Environment Variables​

Backend:

  • DATABASE_URL - PostgreSQL connection
  • JWT_SECRET - Token signing secret
  • PORT - Server port
  • NODE_ENV - Environment (development/production)
  • CORS_ORIGIN - Frontend origin
  • FILE_STORAGE_ADAPTER - Storage adapter (disk/s3)

Frontend:

  • VITE_API_URL - Backend API URL

Design Principles​

1. Strong Typing​

  • TypeScript strict mode - No any, explicit types everywhere
  • Zod validation - Runtime type checking
  • Prisma types - Database types generated

2. Business Logic Outside Frameworks​

  • Services contain logic - Not in routes or Prisma
  • Pure functions - Testable, predictable
  • No framework coupling - Logic can be reused

3. Mandatory Testing​

  • 90% minimum coverage - Enforced
  • 100% for critical paths - Auth, validation, business rules
  • Tests with code - Not after

4. Heavily Relational​

  • Link everything - Maximum relational power
  • No arrays/JSON - Use linked tables
  • Cross-domain analytics - Enabled by relationships

5. Monochrome UI​

  • Black/white/grayscale - Technical aesthetic
  • Icon-first - Lucide React icons
  • Consistent spacing - Design tokens

6. Local-First (Future)​

  • Works offline - Sync when online
  • Data integrity - Local-first ensures consistency

Key Design Patterns​

1. Service Layer Pattern​

Business logic in services, routes are thin wrappers.

2. Adapter Pattern​

File storage, email service use adapters for flexibility.

3. Factory Pattern​

Service factories for dependency injection (e.g., getFileStorageAdapter()).

4. Repository Pattern (via Prisma)​

Prisma provides repository-like interface for data access.

5. Dependency Injection​

Services instantiated in route handlers, not globally.


Performance Considerations​

Backend​

  • Fastify - High-performance HTTP server
  • Connection Pooling - Prisma connection pool
  • Parallel Queries - Promise.all() for independent queries
  • Indexes - Database indexes on foreign keys and search fields

Frontend​

  • Code Splitting - Lazy-loaded pages
  • CSS Modules - Scoped styles (no global conflicts)
  • React.memo - Component memoization where needed
  • Virtual Scrolling - For long lists (future)

Database​

  • Indexes - On foreign keys, search fields, date ranges
  • Cascade Deletes - Efficient cleanup
  • Query Optimization - Prisma query optimization

Future Enhancements​

Scalability​

  • Caching - Redis for frequently accessed data
  • Rate Limiting - API rate limiting
  • CDN - For static assets and images
  • Database Replication - Read replicas

Features​

  • Real-time Updates - WebSocket support
  • Offline Support - Service workers, local storage
  • Mobile Apps - React Native
  • API Versioning - Versioned endpoints

For implementation details of specific features, see docs/features/.