# 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*