# 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 ```python # ❌ 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 ```python # ✅ 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 ```python # ❌ 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 ```python # ❌ 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: ```python 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 ```python # ✅ 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 ```python # ✅ 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 ```python # 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 ```python # 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: ```bash # 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.py` → `services/gameplay_service.py` - `../discord-app/in_game/ai_manager.py` → `services/ai_service.py` - Game creation/management logic → `services/game_service.py` **Medium Priority (Core Engine - Keep Stateless)**: - `../discord-app/in_game/simulations.py` → `engine/simulation.py` - `../discord-app/dice.py` → `engine/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 ```python # ❌ 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 ```python # ❌ 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 ```python # ✅ 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.