# Architecture

**Analysis Date:** 2026-04-27

## Pattern Overview

**Overall:** NestJS modular backend + React feature-based frontend with real-time WebSocket synchronization

**Key Characteristics:**
- Modular NestJS architecture with feature-based modules (Auth, Characters, Campaigns, Equipment, Battle)
- Feature-first React organization with shared components and hooks
- WebSocket Gateway real-time sync for character and battle state
- Role-based access control (ADMIN, GM, PLAYER) enforced globally
- Prisma ORM for PostgreSQL data persistence
- JWT-based stateless authentication

## Layers

**Backend: Controller Layer**
- Purpose: HTTP request handling and routing
- Location: `server/src/modules/*/[feature].controller.ts`
- Contains: REST endpoints with decorators (@Post, @Get, @Put, @Patch, @Delete)
- Depends on: Services, DTOs, Guards, Decorators
- Used by: HTTP clients (React frontend)
- Examples:
  - `server/src/modules/characters/characters.controller.ts` - Character CRUD
  - `server/src/modules/campaigns/campaigns.controller.ts` - Campaign management
  - `server/src/modules/equipment/equipment.controller.ts` - Equipment search/browse
  - `server/src/modules/auth/auth.controller.ts` - Login/Register

**Backend: Service Layer**
- Purpose: Business logic, data processing, validation
- Location: `server/src/modules/*/[feature].service.ts`
- Contains: Methods for creating, updating, deleting entities; complex calculations
- Depends on: PrismaService, other Services, external APIs (Claude)
- Used by: Controllers, Gateways, other Services
- Examples:
  - `server/src/modules/characters/characters.service.ts` - Character operations, access checks
  - `server/src/modules/characters/alchemy.service.ts` - Alchemy system logic (formulas, prepared items, vials)
  - `server/src/modules/characters/pathbuilder-import.service.ts` - Pathbuilder JSON parsing
  - `server/src/modules/equipment/equipment.service.ts` - Equipment search with filters
  - `server/src/modules/battle/battle.service.ts` - Battle session and token management

**Backend: Gateway Layer (WebSocket)**
- Purpose: Real-time bidirectional communication for live updates
- Location: `server/src/modules/*/[feature].gateway.ts`
- Contains: Socket.io event handlers, authentication, room management
- Depends on: JwtService, PrismaService, Services
- Used by: React WebSocket hooks
- Examples:
  - `server/src/modules/characters/characters.gateway.ts` - Character HP, conditions, items, alchemy real-time sync
  - `server/src/modules/battle/battle.gateway.ts` - Battle token movement, HP, initiative real-time sync

**Backend: Persistence Layer**
- Purpose: Database abstraction and ORM
- Location: `server/src/prisma/prisma.service.ts`
- Contains: Prisma client wrapper, query interface
- Depends on: PostgreSQL database
- Used by: All Services
- Data Models defined in: `server/prisma/schema.prisma`

**Frontend: Feature Modules**
- Purpose: Isolated feature domains with components, hooks, types
- Location: `client/src/features/[feature]/`
- Contains: Components, hooks, index.ts barrel exports
- Examples:
  - `client/src/features/auth/` - Login/Register pages, useAuthStore hook
  - `client/src/features/characters/` - Character sheet page, modals, utilities
  - `client/src/features/campaigns/` - Campaign list/detail pages
  - `client/src/features/battle/` - Battle canvas, tokens
  - `client/src/features/library/` - Battle maps, NPC templates (combatants)

**Frontend: Shared Layer**
- Purpose: Reusable components, hooks, utilities, types across features
- Location: `client/src/shared/`
- Contains:
  - Components: `ui/` (shadcn/ui), `layout.tsx`, `protected-route.tsx`
  - Hooks: `use-character-socket.ts`, `use-battle-socket.ts`
  - Lib: `api.ts` (API client), `utils.ts` (helpers)
  - Types: `index.ts` (all TypeScript interfaces)
- Used by: All features

**Frontend: Router**
- Purpose: Navigation and route protection
- Location: `client/src/App.tsx`
- Contains: React Router v6 routes, protected route wrapper
- Depends on: React Router, Feature components
- Entry point: `client/src/main.tsx`

**Frontend: State Management**
- Purpose: Client-side state persistence
- Location: `client/src/features/auth/hooks/use-auth-store.ts`
- Contains: Zustand store for authentication state
- Used by: Auth-related components, ProtectedRoute

## Data Flow

### Character Update (Real-Time WebSocket Sync)

1. **User Action** (React Component)
   - User damages character in `client/src/features/characters/components/hp-control.tsx`
   - Calls `api.updateCharacterHp()` → PATCH `/campaigns/:id/characters/:id/hp`

2. **Controller** (`server/src/modules/characters/characters.controller.ts`)
   - Receives HTTP request
   - Validates JWT token via `JwtAuthGuard`
   - Calls `CharactersService.updateHp()`

3. **Service** (`server/src/modules/characters/characters.service.ts`)
   - Checks campaign access (GM or campaign member)
   - Checks character ownership (owner or GM can edit)
   - Updates character HP in Prisma
   - Emits WebSocket event via `CharactersGateway.broadcast()`

4. **Gateway Broadcast** (`server/src/modules/characters/characters.gateway.ts`)
   - Broadcasts `character_update` event to all clients in character room
   - Update type: `'hp'`
   - Payload: `{ hpCurrent, hpTemp, hpMax }`

5. **Client Socket Hook** (`client/src/shared/hooks/use-character-socket.ts`)
   - Listens for `character_update` event
   - Routes to appropriate callback: `onHpUpdate?.(data)`
   - Component state updates via React state

6. **Component Re-render**
   - Character sheet displays new HP values
   - All other connected clients see update in real-time

### Equipment Database Search

1. **User Search** (React Component)
   - User searches equipment in `client/src/features/characters/components/add-item-modal.tsx`
   - Calls `api.searchEquipment({ query, category, filters, page, limit })`

2. **Controller** (`server/src/modules/equipment/equipment.controller.ts`)
   - Receives GET `/equipment` with query params
   - Calls `EquipmentService.search()`

3. **Service** (`server/src/modules/equipment/equipment.service.ts`)
   - Builds Prisma where clause from filters
   - Queries Equipment table (5,482 items)
   - Returns paginated results with pagination metadata
   - Includes category list for UI dropdown

4. **Component Display**
   - Results displayed with pagination controls
   - User can view equipment details, add to inventory
   - New item created via `api.addCharacterItem()`

### Character Creation (Pathbuilder Import)

1. **User Upload** (React Component)
   - User uploads Pathbuilder JSON in `client/src/features/characters/components/import-character-modal.tsx`
   - Calls `api.importCharacterFromPathbuilder(pathbuilderJson)`

2. **Controller** (`server/src/modules/characters/characters.controller.ts`)
   - POST `/campaigns/:id/characters/import`
   - Calls `PathbuilderImportService.importCharacter()`

3. **Pathbuilder Import Service** (`server/src/modules/characters/pathbuilder-import.service.ts`)
   - Parses Pathbuilder JSON structure
   - Extracts abilities, skills, feats, spells
   - Creates Character with all related entities (CharacterAbility, CharacterSkill, etc.)
   - Stores raw pathbuilderData for future reference

4. **Database Persistence**
   - Character created with all nested relations
   - Each skill, feat, spell stored as separate records
   - Character ready for real-time sync

### Battle Session Synchronization

1. **Token Movement** (React Component)
   - User drags token on battle canvas
   - Calls `api.moveBattleToken(positionX, positionY)`

2. **Battle Gateway** (`server/src/modules/battle/battle.gateway.ts`)
   - Receives movement update
   - Broadcasts `battle_update` event with type `'token_moved'`
   - All clients in session room receive update

3. **Client Display**
   - All participants see token at new position
   - No UI lag — updates are instant

### Alchemy System State

1. **Prepare Items** (React Component)
   - User selects formulas to prepare daily in `client/src/features/characters/components/alchemy-tab.tsx`
   - Calls `api.dailyPreparation(items)`

2. **Alchemy Service** (`server/src/modules/characters/alchemy.service.ts`)
   - Validates character has formulas
   - Creates CharacterPreparedItem records
   - Updates CharacterAlchemyState (versatileVialsCurrent, advancedAlchemyMax)
   - Emits WebSocket `alchemy_prepared` and `alchemy_state` updates

3. **Real-Time Sync**
   - UI updates showing prepared items and vial tracker
   - Ready for combat use

## Key Abstractions

**Prisma ORM Service:**
- Purpose: Database abstraction, query builder
- Location: `server/src/prisma/prisma.service.ts`
- Pattern: Singleton service injected into all modules
- Used for: All CRUD operations, complex queries with relations

**JWT Strategy & Guards:**
- Purpose: Authentication and authorization
- Location: 
  - `server/src/modules/auth/strategies/jwt.strategy.ts` - JWT validation
  - `server/src/modules/auth/guards/jwt-auth.guard.ts` - Global auth enforcement
  - `server/src/modules/auth/guards/roles.guard.ts` - Role-based access control
- Pattern: NestJS guards executed globally on every request
- Metadata: `@Roles()` and `@Public()` decorators control per-endpoint behavior

**API Client Service:**
- Purpose: HTTP communication abstraction
- Location: `client/src/shared/lib/api.ts`
- Pattern: Singleton class with axios instance
- Features: Token management, auto-retry, auth interceptors, 401 handling

**WebSocket Socket Manager:**
- Purpose: Prevent duplicate connections, manage subscriptions
- Location: `client/src/shared/hooks/use-character-socket.ts`
- Pattern: Global socket singleton with ref counting
- Features: Auto-reconnect, polling fallback, room subscription

**Character/Battle Update Types:**
- Purpose: Type-safe event dispatch
- Location: 
  - `server/src/modules/characters/characters.gateway.ts` (CharacterUpdatePayload)
  - `server/src/modules/battle/battle.gateway.ts` (BattleUpdatePayload)
- Pattern: Union types for event kind discrimination

## Entry Points

**Server Entry:**
- Location: `server/src/main.ts`
- Triggers: `npm run start:dev` or deployed container startup
- Responsibilities:
  - Create NestJS app from AppModule
  - Enable global pipes (ValidationPipe)
  - Configure CORS from env
  - Setup Swagger API docs at `/api/docs`
  - Listen on PORT (default 5000)

**Client Entry:**
- Location: `client/src/main.tsx`
- Triggers: Browser page load or `npm run dev`
- Responsibilities:
  - Render React app into #root DOM element
  - Wrap with StrictMode

**Router:**
- Location: `client/src/App.tsx`
- Pattern: React Router v6 with protected routes
- Flow:
  1. QueryClientProvider (React Query setup)
  2. BrowserRouter (React Router)
  3. AppContent checks auth state
  4. Public routes: /login, /register
  5. Protected routes: / (campaigns), /campaigns/:id, /campaigns/:id/characters/:characterId, /campaigns/:id/battle, /campaigns/:id/library
  6. ProtectedRoute wrapper ensures authentication

## Error Handling

**Strategy:** Structured error responses with HTTP status codes

**Backend Patterns:**
- `NotFoundException` - 404 when entity not found
- `ForbiddenException` - 403 when user lacks permission
- `BadRequestException` - 400 for invalid input
- `UnauthorizedException` - 401 for auth failures
- Global error filter could be added for consistent formatting
- Service methods validate access before querying: `checkCampaignAccess()`, `checkCharacterAccess()`

**Frontend Patterns:**
- API client `response.interceptors` catches 401 → redirects to /login
- Components wrapped in error boundaries (future enhancement)
- Failed requests return rejected promises to component

**WebSocket Patterns:**
- Token verification on connection → disconnect if invalid
- No structured error responses; silent failures with console logging
- Clients auto-reconnect via socket.io configuration

## Cross-Cutting Concerns

**Logging:** 
- Backend: NestJS Logger class used in services/gateways
- Frontend: Console.log for development (socket.io events log connection state)

**Validation:**
- Backend: Global ValidationPipe with DTOs (class-validator)
- Frontend: Form validation in components (manual checks in modals)
- Prisma schema enforces constraints (NOT NULL, unique, enums)

**Authentication:**
- Backend: JwtAuthGuard applied globally in app.module.ts
- Endpoints opt-out via @Public() decorator
- WebSocket: Token verified in gateway.handleConnection()
- Frontend: Token stored in localStorage (persistent) or sessionStorage (session-only)

**Authorization:**
- Backend: RolesGuard checks @Roles() metadata
- Service methods verify campaign/character ownership before allowing operations
- Pattern: Check campaign membership → check character ownership → allow operation

**Real-Time Sync:**
- WebSocket Gateway manages rooms (one room per character/session)
- Clients join room on component mount, leave on unmount
- Broadcasts to all clients in room except sender (optional)

---

*Architecture analysis: 2026-04-27*
