paper-dynasty-gameplay-webapp/DEVELOPMENT_GUIDE_V2.md
Cal Corum c09f9d1302 CLAUDE: Initialize Paper Dynasty web app with Model/Service Architecture
Establishes foundation for migrating baseball simulation from Discord bot to web application using service-oriented architecture pattern.

Key components:
- FastAPI application structure with dependency injection
- Service layer foundation with base classes and container
- Comprehensive directory documentation with README files
- PostgreSQL containerization with Docker Compose
- Testing structure for unit/integration/e2e tests
- Migration planning documentation
- Rotating log configuration per user requirements

Architecture follows Model/Service/Controller pattern to improve testability, maintainability, and scalability over original monolithic Discord app.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-27 21:44:12 -05:00

19 KiB

Paper Dynasty Web App Development Guide v2.0

Model/Service Architecture Edition

Overview

This guide provides instructions for AI agents working on the Paper Dynasty web app migration project using a Model/Service Architecture. This approach separates business logic into services, improving testability, maintainability, and scalability over the original monolithic approach.

Quick Start for AI Agents

Before Starting Any Session:

  1. Check Progress: Read ./.claude/plans/web-migration-v2.md to understand current status
  2. Review TODO List: Use TodoWrite tool to track your progress throughout the session
  3. Understand Context: This is a migration from ../discord-app/ to a web interface using Model/Service Architecture
  4. Follow Standards: Use the code review methodology defined below

Current Project Status

  • Migration Type: Fork approach with Model/Service Architecture
  • Current Phase: Check ./.claude/plans/web-migration-v2.md for latest status
  • Architecture: Clean separation between Models, Services, Controllers (FastAPI routes)
  • Next Tasks: Always work from the current phase checklist in the migration plan

Architecture Overview

Model/Service Architecture Benefits

  • Separation of Concerns: Clear boundaries between data, business logic, and web interface
  • Testability: Services can be unit tested independently of database and web framework
  • Scalability: Easy to add caching, background tasks, API versioning
  • Maintainability: Business logic centralized in services, not scattered across routes
  • Future-Proofing: Ready for mobile API, microservices, or different frontends

Project Structure

gameplay-website/
├── .claude/
│   └── web-migration-v2.md
├── app/
│   ├── main.py                 # FastAPI application setup
│   ├── models/                 # Pure data models (SQLModel)
│   │   ├── game.py            # Game, Team, Player, Lineup, Play
│   │   ├── web_models.py      # Session, Preferences, Notifications
│   │   └── exceptions.py       # Custom exceptions
│   ├── services/               # Business logic layer ⭐ NEW
│   │   ├── __init__.py
│   │   ├── game_service.py     # Game creation, management, queries
│   │   ├── gameplay_service.py # Core gameplay simulation & flow
│   │   ├── ai_service.py       # AI decision making
│   │   ├── user_service.py     # User sessions, preferences
│   │   ├── notification_service.py # Web notifications
│   │   └── auth_service.py     # Discord OAuth, session management
│   ├── repositories/           # Data access layer ⭐ NEW (optional)
│   │   ├── __init__.py
│   │   ├── game_repository.py  # Complex game queries
│   │   └── user_repository.py  # User/session queries
│   ├── routers/                # FastAPI route handlers ⭐ ENHANCED
│   │   ├── __init__.py
│   │   ├── games.py           # Game-related endpoints
│   │   ├── auth.py            # Authentication routes
│   │   ├── api.py             # JSON API for HTMX
│   │   └── pages.py           # HTML page routes
│   ├── engine/                 # Core simulation engine (stateless)
│   │   ├── dice.py            # Dice rolling, fielding checks
│   │   ├── simulation.py      # Pitcher vs batter mechanics
│   │   └── calculations.py    # Statistics, WPA calculations
│   ├── config/
│   │   └── constants.py
│   ├── static/
│   └── templates/
├── tests/                      # Comprehensive test structure
│   ├── unit/                   # Unit tests for services
│   ├── integration/            # Integration tests
│   └── e2e/                    # End-to-end tests
├── requirements.txt
└── DEVELOPMENT_GUIDE_V2.md     # This file

Code Review Methodology

When Migrating Code from Discord App:

1. Service Extraction First

# ❌ Old approach: Business logic in routes
@app.post("/games/start")
async def start_game(request: Request):
    # 50+ lines of game creation logic mixed with HTTP handling

# ✅ New approach: Clean service separation
@router.post("/games/start")
async def start_game(request: StartGameRequest):
    game = await game_service.create_game(
        away_team_id=request.away_team_id,
        home_team_id=request.home_team_id,
        game_type=request.game_type
    )
    return {"game_id": game.id, "status": "created"}

2. Service Layer Design

# ✅ Service pattern example
from sqlmodel import Session
from typing import Optional, List
from ..models.game import Game, Team
from ..repositories.game_repository import GameRepository

class GameService:
    def __init__(self, session: Session):
        self.session = session
        self.game_repo = GameRepository(session)

    async def create_game(
        self,
        away_team_id: int,
        home_team_id: int,
        game_type: str
    ) -> Game:
        """Create a new game with validation and business rules"""
        # Validation
        await self._validate_teams_available(away_team_id, home_team_id)

        # Business logic
        game = Game(
            away_team_id=away_team_id,
            home_team_id=home_team_id,
            game_type=game_type,
            season=self._get_current_season()
        )

        # Persistence
        return await self.game_repo.create(game)

    async def _validate_teams_available(self, away_id: int, home_id: int):
        """Private method for business rule validation"""
        # Implementation here
        pass

3. Dependency Cleanup

# ❌ Remove these Discord-specific imports
import discord
from discord.ext import commands
from discord import app_commands

# ✅ Replace with web-appropriate imports
from fastapi import FastAPI, Request, Response, Depends
from sqlmodel import Session
from typing import List, Optional

4. Error Handling Review

# ❌ Discord-specific error handling
await interaction.response.send_message("Error occurred", ephemeral=True)

# ✅ Service-layer error handling
from ..models.exceptions import GameNotFoundException

class GameService:
    async def get_game(self, game_id: int) -> Game:
        game = await self.game_repo.get_by_id(game_id)
        if not game:
            raise GameNotFoundException(f"Game {game_id} not found")
        return game

# ✅ Route-layer error handling
@router.get("/games/{game_id}")
async def get_game(game_id: int, game_service: GameService = Depends()):
    try:
        game = await game_service.get_game(game_id)
        return game
    except GameNotFoundException as e:
        raise HTTPException(status_code=404, detail=str(e))

5. Logging Standards

Follow the user's CLAUDE.md requirements:

import logging
logger = logging.getLogger(f'{__name__}.GameService')

class GameService:
    async def create_game(self, away_team_id: int, home_team_id: int) -> Game:
        logger.info(f'GameService.create_game - Creating game: {away_team_id} vs {home_team_id}')
        try:
            # Business logic
            result = await self._create_game_logic(away_team_id, home_team_id)
            logger.info(f'GameService.create_game - Game created successfully: {result.id}')
            return result
        except Exception as e:
            logger.error(f'GameService.create_game - Failed to create game: {str(e)}')
            raise

6. Service Testing Pattern

# ✅ Unit test services independently
import pytest
from unittest.mock import Mock
from app.services.game_service import GameService
from app.models.game import Game

@pytest.fixture
def mock_session():
    return Mock()

@pytest.fixture
def game_service(mock_session):
    return GameService(mock_session)

@pytest.mark.asyncio
async def test_create_game_success(game_service):
    # Test business logic without database
    result = await game_service.create_game(
        away_team_id=1,
        home_team_id=2,
        game_type="ranked"
    )
    assert result.away_team_id == 1
    assert result.home_team_id == 2

Code Review Checklist Template

For each migrated file, check:

  • Business logic extracted into appropriate service
  • Service has clear, single responsibility
  • All Discord imports removed
  • Error handling appropriate for service layer
  • Logging follows project standards
  • Services have comprehensive unit tests
  • Routes are thin, delegating to services
  • Database access goes through repositories (if using repository pattern)
  • Type hints on all service methods
  • No obvious performance issues

Service Layer Guidelines

Service Responsibilities

GameService

  • Game creation and validation
  • Game state management
  • Game queries and filtering
  • Game lifecycle (start, pause, complete)

GameplayService

  • Core gameplay simulation
  • Play execution and validation
  • Inning management
  • Score calculation

AIService

  • AI decision making
  • Manager AI logic
  • Automated gameplay

UserService

  • User session management
  • User preferences
  • Authentication state

NotificationService

  • Web notifications
  • Game event notifications
  • Real-time updates

Service Design Principles

  1. Single Responsibility: Each service has one clear purpose
  2. Dependency Injection: Services receive dependencies via constructor
  3. Stateless: Services don't maintain instance state between calls
  4. Testable: Services can be unit tested with mocked dependencies
  5. Pure Business Logic: No HTTP, database, or UI concerns in services

Service Interaction Patterns

# ✅ Service-to-service communication
class GameplayService:
    def __init__(self, session: Session, ai_service: AIService):
        self.session = session
        self.ai_service = ai_service

    async def execute_play(self, game_id: int, play_data: dict) -> PlayResult:
        # Get current game state
        game = await self.game_repo.get_by_id(game_id)

        # Check if AI needs to make decisions
        if game.ai_team:
            ai_decision = await self.ai_service.get_defensive_decision(game)
            play_data.update(ai_decision)

        # Execute the play
        return await self._execute_play_logic(game, play_data)

Testing Architecture

Test Structure

tests/
├── unit/                       # Fast, isolated unit tests
│   ├── services/
│   │   ├── test_game_service.py
│   │   ├── test_gameplay_service.py
│   │   └── test_ai_service.py
│   ├── engine/
│   │   ├── test_dice.py
│   │   └── test_simulation.py
│   └── models/
│       └── test_game_models.py
├── integration/                # Database + service integration
│   ├── test_game_flow.py
│   └── test_auth_flow.py
└── e2e/                       # Full application tests
    ├── test_game_creation.py
    └── test_gameplay_flow.py

Testing Patterns

Unit Testing Services

# test_game_service.py
@pytest.fixture
def mock_game_repo():
    return Mock(spec=GameRepository)

@pytest.fixture
def game_service(mock_game_repo):
    return GameService(session=Mock(), game_repo=mock_game_repo)

@pytest.mark.asyncio
async def test_create_game_validates_teams(game_service, mock_game_repo):
    # Test business logic without database
    mock_game_repo.get_by_id.return_value = None  # Team not found

    with pytest.raises(TeamNotFoundException):
        await game_service.create_game(999, 1000, "ranked")

Integration Testing

# test_game_flow.py
@pytest.mark.asyncio
async def test_complete_game_creation_flow(db_session):
    # Test with real database but isolated transaction
    game_service = GameService(db_session)

    # Create test data
    team1 = await create_test_team(db_session)
    team2 = await create_test_team(db_session)

    # Test full flow
    game = await game_service.create_game(team1.id, team2.id, "ranked")

    assert game.id is not None
    assert game.away_team_id == team1.id

What to Test by Layer

Services (Unit Tests)

  • Critical: Business logic validation, error handling, service interactions
  • Important: Edge cases, complex calculations
  • Nice to have: Performance characteristics

Engine (Unit Tests)

  • Critical: Game simulation accuracy, dice probability, calculation correctness
  • Important: Edge cases in game rules, random seed consistency
  • Nice to have: Performance optimization

Integration Tests

  • Critical: Database operations, service composition, authentication flow
  • Important: Error propagation, transaction handling
  • Nice to have: Caching behavior

Migration Strategy

Phase 1: Service Foundation

  1. Create basic service interfaces
  2. Extract simple business logic from Discord app
  3. Set up dependency injection patterns
  4. Create service unit tests

Phase 2: Core Service Implementation

  1. Implement GameService with full game management
  2. Implement GameplayService with simulation logic
  3. Implement AIService with decision making
  4. Add comprehensive service tests

Phase 3: Web Integration

  1. Create FastAPI routes that use services
  2. Add authentication and session services
  3. Implement HTMX endpoints with service calls
  4. Integration testing

Phase 4: Advanced Features

  1. Add repository layer if needed for complex queries
  2. Implement notification service
  3. Add caching and performance optimization
  4. End-to-end testing

Performance Considerations

Service Layer Performance

  • Use connection pooling for database sessions
  • Implement caching at service level for expensive operations
  • Consider async/await for I/O bound operations
  • Monitor service call patterns for optimization opportunities

Database Optimization

  • Repositories handle complex queries and optimization
  • Use eager loading for frequently accessed relationships
  • Implement query result caching where appropriate
  • Monitor N+1 query patterns

Development Workflow

Starting a New Session

  1. Read current progress in ./.claude/web-migration-v2.md
  2. Update TodoWrite with current tasks focusing on service implementation
  3. Work on the current phase tasks in order
  4. Follow the service-first approach for any new business logic
  5. Update progress in migration plan when completing major milestones

Service Development Process

  1. Design: Define service interface and responsibilities
  2. Test: Write unit tests for expected behavior
  3. Implement: Build service following single responsibility principle
  4. Integrate: Connect service to routes and other services
  5. Validate: Run full test suite and integration tests

Virtual Environment Setup

Always check for virtual environment:

# Check for existing venv
ls -la | grep venv

# Create if doesn't exist (user works with discord.py, wants venv)
python -m venv venv
source venv/bin/activate  # Linux/Mac
# or
venv\Scripts\activate     # Windows

Testing Requirements

User's CLAUDE.md specifies:

  • "You do not need to ask for permission to run tests"
  • Use pytest for all testing
  • Include unit tests for services, integration tests for full flows
  • Test critical game logic thoroughly
  • Aim for 80%+ test coverage on services

Git Commit Standards

User's CLAUDE.md specifies:

  • Prefix commit messages with "CLAUDE: "
  • Focus on the "why" rather than the "what"
  • Example: "CLAUDE: Extract game creation logic into GameService for better testability"

Migration-Specific Guidelines

Files to Prioritize for Service Extraction

High Priority (Extract to Services First):

  • ../discord-app/command_logic/logic_gameplay.pyservices/gameplay_service.py
  • ../discord-app/in_game/ai_manager.pyservices/ai_service.py
  • Game creation/management logic → services/game_service.py

Medium Priority (Core Engine - Keep Stateless):

  • ../discord-app/in_game/simulations.pyengine/simulation.py
  • ../discord-app/dice.pyengine/dice.py

Lower Priority (Supporting Services):

  • Authentication logic → services/auth_service.py
  • User management → services/user_service.py
  • Notifications → services/notification_service.py

Service Extraction Patterns

Pattern 1: Discord Command → Service Method

# ❌ Discord app pattern
@app_commands.command(name="start_game")
async def start_game(interaction: discord.Interaction):
    # 50+ lines of business logic mixed with Discord API calls

# ✅ Web app pattern
class GameService:
    async def create_game(self, away_team_id: int, home_team_id: int) -> Game:
        # Pure business logic, no Discord dependencies

@router.post("/games/start")
async def start_game(request: StartGameRequest, game_service: GameService = Depends()):
    return await game_service.create_game(request.away_team_id, request.home_team_id)

Pattern 2: Game Logic → Service + Engine

# ❌ Mixed business logic and game engine
def process_at_bat(interaction, game_id, batter_decision):
    # Mixed: Discord UI + game rules + database + simulation

# ✅ Separated concerns
class GameplayService:
    async def process_at_bat(self, game_id: int, batter_decision: str) -> PlayResult:
        # Business logic: validation, state management

    def _simulate_at_bat(self, pitcher_stats, batter_stats) -> SimulationResult:
        # Pure game engine logic

Pattern 3: AI Decision Making → AIService

# ✅ Clean AI service
class AIService:
    async def get_pitching_decision(self, game_state: GameState) -> PitchingDecision:
        # AI logic without Discord dependencies

    async def get_batting_decision(self, game_state: GameState) -> BattingDecision:
        # AI logic without Discord dependencies

Success Metrics

Architecture Quality

  • Clear separation between models, services, routes
  • Services have single responsibilities
  • Business logic not mixed with web framework code
  • Comprehensive service unit tests

Code Quality Standards

  • All services have 90%+ test coverage
  • Type hints required for all service methods
  • Error handling appropriate for service layer
  • Performance acceptable for multiple concurrent users
  • No Discord-specific dependencies remaining

Migration Success

  • Working single-player baseball game with Discord OAuth
  • Live scoreboard with news ticker
  • Clean, service-oriented codebase ready for scaling
  • Comprehensive test coverage across all layers
  • Responsive web interface (mobile + desktop)

Emergency Contacts & Resources

  • Project Owner: Check project README for contact info
  • Discord App Source: ../discord-app/ directory
  • Migration Plan: ./.claude/web-migration-v2.md
  • User Preferences: /home/cal/.claude/CLAUDE.md
  • Architecture Reference: This file (DEVELOPMENT_GUIDE_V2.md)

Remember: This is a migration project with the goal of creating a scalable, maintainable web application using Model/Service Architecture. Always prioritize clean architecture and comprehensive testing over speed of development.