docs: map existing codebase
This commit is contained in:
319
.planning/codebase/ARCHITECTURE.md
Normal file
319
.planning/codebase/ARCHITECTURE.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# 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*
|
||||
Reference in New Issue
Block a user