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>
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:
- Check Progress: Read
./.claude/plans/web-migration-v2.mdto understand current status - Review TODO List: Use TodoWrite tool to track your progress throughout the session
- Understand Context: This is a migration from
../discord-app/to a web interface using Model/Service Architecture - 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.mdfor 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
- Single Responsibility: Each service has one clear purpose
- Dependency Injection: Services receive dependencies via constructor
- Stateless: Services don't maintain instance state between calls
- Testable: Services can be unit tested with mocked dependencies
- 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
- Create basic service interfaces
- Extract simple business logic from Discord app
- Set up dependency injection patterns
- Create service unit tests
Phase 2: Core Service Implementation
- Implement GameService with full game management
- Implement GameplayService with simulation logic
- Implement AIService with decision making
- Add comprehensive service tests
Phase 3: Web Integration
- Create FastAPI routes that use services
- Add authentication and session services
- Implement HTMX endpoints with service calls
- Integration testing
Phase 4: Advanced Features
- Add repository layer if needed for complex queries
- Implement notification service
- Add caching and performance optimization
- 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
- Read current progress in
./.claude/web-migration-v2.md - Update TodoWrite with current tasks focusing on service implementation
- Work on the current phase tasks in order
- Follow the service-first approach for any new business logic
- Update progress in migration plan when completing major milestones
Service Development Process
- Design: Define service interface and responsibilities
- Test: Write unit tests for expected behavior
- Implement: Build service following single responsibility principle
- Integrate: Connect service to routes and other services
- 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.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
# ❌ 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.