Add comprehensive project documentation and Docker infrastructure for Paper Dynasty Real-Time Game Engine - a web-based multiplayer baseball simulation platform replacing the legacy Google Sheets system. Documentation Added: - Complete PRD (Product Requirements Document) - Project README with dual development workflows - Implementation guide with 5-phase roadmap - Architecture docs (backend, frontend, database, WebSocket) - CLAUDE.md context files for each major directory Infrastructure Added: - Root docker-compose.yml for full stack orchestration - Dockerfiles for backend and both frontends (multi-stage builds) - .dockerignore files for optimal build context - .env.example with all required configuration - Updated .gitignore for Python, Node, Nuxt, and Docker Project Structure: - backend/ - FastAPI + Socket.io game engine (Python 3.11+) - frontend-sba/ - SBA League Nuxt 3 frontend - frontend-pd/ - PD League Nuxt 3 frontend - .claude/implementation/ - Detailed implementation guides Supports two development workflows: 1. Local dev (recommended): Services run natively with hot-reload 2. Full Docker: One-command stack orchestration for testing/demos Next: Phase 1 implementation (backend/frontend foundations)
9.8 KiB
9.8 KiB
Backend - Paper Dynasty Game Engine
Overview
FastAPI-based real-time game backend handling WebSocket communication, game state management, and database persistence for both SBA and PD leagues.
Technology Stack
- Framework: FastAPI (Python 3.11+)
- WebSocket: Socket.io (python-socketio)
- Database: PostgreSQL 14+ with SQLAlchemy 2.0 (async)
- ORM: SQLAlchemy with asyncpg driver
- Validation: Pydantic v2
- Testing: pytest with pytest-asyncio
- Code Quality: black, flake8, mypy
Project Structure
backend/
├── app/
│ ├── main.py # FastAPI app + Socket.io initialization
│ ├── config.py # Settings with pydantic-settings
│ │
│ ├── core/ # Game logic (Phase 2+)
│ │ ├── game_engine.py # Main game simulation
│ │ ├── state_manager.py # In-memory state
│ │ ├── play_resolver.py # Play outcome resolution
│ │ ├── dice.py # Secure random rolls
│ │ └── validators.py # Rule validation
│ │
│ ├── config/ # League configurations (Phase 2+)
│ │ ├── base_config.py # Shared configuration
│ │ ├── league_configs.py # SBA/PD specific
│ │ └── result_charts.py # d20 outcome tables
│ │
│ ├── models/ # Data models
│ │ ├── db_models.py # SQLAlchemy ORM models
│ │ ├── game_models.py # Pydantic game state models (Phase 2+)
│ │ └── player_models.py # Polymorphic player models (Phase 2+)
│ │
│ ├── websocket/ # WebSocket handling
│ │ ├── connection_manager.py # Connection lifecycle
│ │ ├── handlers.py # Event handlers
│ │ └── events.py # Event definitions (Phase 2+)
│ │
│ ├── api/ # REST API
│ │ ├── routes/
│ │ │ ├── health.py # Health check endpoints
│ │ │ ├── auth.py # Discord OAuth (Phase 1)
│ │ │ └── games.py # Game CRUD (Phase 2+)
│ │ └── dependencies.py # FastAPI dependencies
│ │
│ ├── database/ # Database layer
│ │ ├── session.py # Async session management
│ │ └── operations.py # DB operations (Phase 2+)
│ │
│ ├── data/ # External data (Phase 2+)
│ │ ├── api_client.py # League REST API client
│ │ └── cache.py # Caching layer
│ │
│ └── utils/ # Utilities
│ ├── logging.py # Logging setup
│ └── auth.py # JWT utilities (Phase 1)
│
├── tests/
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests
│ └── e2e/ # End-to-end tests
│
├── logs/ # Application logs (gitignored)
├── venv/ # Virtual environment (gitignored)
├── .env # Environment variables (gitignored)
├── .env.example # Environment template
├── requirements.txt # Production dependencies
├── requirements-dev.txt # Dev dependencies
├── Dockerfile # Container definition
├── docker-compose.yml # Redis for local dev
└── pytest.ini # Pytest configuration
Key Architectural Patterns
1. Hybrid State Management
- In-Memory: Active game states for fast access (<500ms response)
- PostgreSQL: Persistent storage for recovery and history
- Pattern: Write-through cache (update memory + async DB write)
2. Polymorphic Player Models
# Base class with abstract methods
class BasePlayer(BaseModel, ABC):
@abstractmethod
def get_image_url(self) -> str: ...
# League-specific implementations
class SbaPlayer(BasePlayer): ...
class PdPlayer(BasePlayer): ...
# Factory pattern for instantiation
Lineup.from_api_data(config, data)
3. League-Agnostic Core
- Game engine works for any league
- League-specific logic in config classes
- Result charts loaded per league
4. Async-First
- All database operations use
async/await - Database writes don't block game logic
- Connection pooling for efficiency
Development Workflow
Daily Development
# Activate virtual environment
source venv/bin/activate
# Start Redis (in separate terminal)
docker-compose up
# Run backend with hot-reload
python -m app.main
# Backend available at http://localhost:8000
# API docs at http://localhost:8000/docs
Testing
# Run all tests
pytest tests/ -v
# Run with coverage
pytest tests/ --cov=app --cov-report=html
# Run specific test file
pytest tests/unit/test_game_engine.py -v
# Type checking
mypy app/
# Code formatting
black app/ tests/
# Linting
flake8 app/ tests/
Coding Standards
Python Style
- Formatting: Black with default settings
- Line Length: 88 characters (black default)
- Imports: Group stdlib, third-party, local (isort compatible)
- Type Hints: Required for all public functions
- Docstrings: Google style for classes and public methods
Logging Pattern
import logging
logger = logging.getLogger(f'{__name__}.ClassName')
# Usage
logger.info(f"User {user_id} connected")
logger.error(f"Failed to process action: {error}", exc_info=True)
Error Handling
- Raise or Return: Never return
Optionalunless specifically required - Custom Exceptions: Use for domain-specific errors
- Logging: Always log exceptions with context
Dataclasses
from dataclasses import dataclass
@dataclass
class GameState:
game_id: str
inning: int
outs: int
# ... fields
Database Patterns
Async Session Usage
from app.database.session import get_session
async def some_function():
async with get_session() as session:
result = await session.execute(query)
# session.commit() happens automatically
Model Definitions
from app.database.session import Base
from sqlalchemy import Column, String, Integer
class Game(Base):
__tablename__ = "games"
id = Column(UUID(as_uuid=True), primary_key=True)
# ... columns
WebSocket Patterns
Event Handler Registration
@sio.event
async def some_event(sid, data):
"""Handle some_event from client"""
try:
# Validate data
# Process action
# Emit response
await sio.emit('response_event', result, room=sid)
except Exception as e:
logger.error(f"Error handling event: {e}")
await sio.emit('error', {'message': str(e)}, room=sid)
Broadcasting
# To specific game room
await connection_manager.broadcast_to_game(
game_id,
'game_state_update',
state_data
)
# To specific user
await connection_manager.emit_to_user(
sid,
'decision_required',
decision_data
)
Environment Variables
Required in .env:
# Database
DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/dbname
# Application
SECRET_KEY=your-secret-key-at-least-32-chars
# Discord OAuth
DISCORD_CLIENT_ID=your-client-id
DISCORD_CLIENT_SECRET=your-client-secret
# League APIs
SBA_API_URL=https://sba-api.example.com
SBA_API_KEY=your-api-key
PD_API_URL=https://pd-api.example.com
PD_API_KEY=your-api-key
Performance Targets
- Action Response: < 500ms from user action to state update
- WebSocket Delivery: < 200ms
- Database Write: < 100ms (async, non-blocking)
- State Recovery: < 2 seconds
- Concurrent Games: Support 10+ simultaneous games
- Memory: < 1GB with 10 active games
Security Considerations
- Authentication: All WebSocket connections require valid JWT
- Authorization: Verify team ownership before allowing actions
- Input Validation: Pydantic models validate all inputs
- SQL Injection: Prevented by SQLAlchemy ORM
- Dice Rolls: Cryptographically secure random generation
- Server-Side Logic: All game rules enforced server-side
Common Tasks
Adding a New API Endpoint
- Create route in
app/api/routes/ - Define Pydantic request/response models
- Add dependency injection if needed
- Register router in
app/main.py
Adding a New WebSocket Event
- Define event handler in
app/websocket/handlers.py - Register with
@sio.eventdecorator - Validate data with Pydantic
- Add corresponding client handling in frontend
Adding a New Database Model
- Define SQLAlchemy model in
app/models/db_models.py - Create Alembic migration:
alembic revision --autogenerate -m "description" - Apply migration:
alembic upgrade head
Troubleshooting
Import Errors
- Ensure virtual environment is activated
- Check
PYTHONPATHif using custom structure - Verify all
__init__.pyfiles exist
Database Connection Issues
- Verify
DATABASE_URLin.envis correct - Test connection:
psql $DATABASE_URL - Check firewall/network access
- Verify database exists
WebSocket Not Connecting
- Check CORS settings in
config.py - Verify token is being sent from client
- Check logs for connection errors
- Ensure Socket.io versions match (client/server)
References
- Implementation Guide:
../.claude/implementation/01-infrastructure.md - Backend Architecture:
../.claude/implementation/backend-architecture.md - WebSocket Protocol:
../.claude/implementation/websocket-protocol.md - Database Design:
../.claude/implementation/database-design.md - Full PRD:
../prd-web-scorecard-1.1.md
Current Phase: Phase 1 - Core Infrastructure Next Phase: Phase 2 - Game Engine Core