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>
574 lines
19 KiB
Markdown
574 lines
19 KiB
Markdown
# 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.
|