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>
138 lines
4.0 KiB
Markdown
138 lines
4.0 KiB
Markdown
# Routers Directory
|
|
|
|
This directory contains **FastAPI route handlers** following the Model/Service Architecture pattern. Routes are thin controllers that handle HTTP concerns and delegate business logic to services.
|
|
|
|
## Architecture Pattern
|
|
|
|
### Route Responsibilities
|
|
- **HTTP Handling**: Request/response processing, status codes
|
|
- **Input Validation**: Request data validation and parsing
|
|
- **Service Delegation**: Delegate business logic to services
|
|
- **Response Formatting**: Transform service results for HTTP responses
|
|
|
|
### Design Principles
|
|
- **Thin Controllers**: Minimal logic, delegate to services
|
|
- **Dependency Injection**: Use FastAPI `Depends()` for services
|
|
- **Error Handling**: Transform service exceptions to HTTP errors
|
|
- **Documentation**: FastAPI auto-generates OpenAPI documentation
|
|
|
|
## Current Files
|
|
|
|
### `auth.py`
|
|
Authentication routes:
|
|
- **Discord OAuth**: Login redirect and callback handling
|
|
- **Session Management**: Login/logout functionality
|
|
- **Uses**: `AuthService` for business logic
|
|
|
|
### `games.py`
|
|
Game-related endpoints:
|
|
- **Game Creation**: Start new games
|
|
- **Game Retrieval**: Get game details and listings
|
|
- **Uses**: `GameService` for business logic
|
|
|
|
### `api.py`
|
|
JSON API endpoints for HTMX:
|
|
- **Live Updates**: Scoreboard and game state
|
|
- **Player Actions**: Execute gameplay actions
|
|
- **Uses**: `GameplayService` for real-time updates
|
|
|
|
### `pages.py`
|
|
HTML page routes with Jinja2 templates:
|
|
- **Home Page**: Main application entry point
|
|
- **Game Interface**: Live game viewing
|
|
- **Template Rendering**: Server-side HTML generation
|
|
|
|
## Route Pattern Example
|
|
|
|
```python
|
|
from fastapi import APIRouter, HTTPException
|
|
from app.services.service_container import GameServiceDep
|
|
|
|
router = APIRouter()
|
|
|
|
@router.post("/games/start")
|
|
async def start_game(
|
|
request: StartGameRequest,
|
|
game_service: GameServiceDep
|
|
):
|
|
"""Start a new game - delegates to GameService."""
|
|
try:
|
|
game = await game_service.create_game(
|
|
away_team_id=request.away_team_id,
|
|
home_team_id=request.home_team_id
|
|
)
|
|
return {"game_id": game.id, "status": "created"}
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
```
|
|
|
|
## Service Integration
|
|
|
|
Routes use dependency injection to access services:
|
|
|
|
```python
|
|
# Service dependencies from service_container.py
|
|
GameServiceDep = Annotated[GameService, Depends(get_game_service)]
|
|
UserServiceDep = Annotated[UserService, Depends(get_user_service)]
|
|
AuthServiceDep = Annotated[AuthService, Depends(get_auth_service)]
|
|
```
|
|
|
|
## Route Organization
|
|
|
|
### `/auth` - Authentication
|
|
- `GET /auth/login` - Discord OAuth redirect
|
|
- `GET /auth/callback` - OAuth callback handler
|
|
- `POST /auth/logout` - Logout and session cleanup
|
|
|
|
### `/games` - Game Management
|
|
- `POST /games/start` - Create new game
|
|
- `GET /games/{game_id}` - Get game details
|
|
- `GET /games/` - List active games
|
|
|
|
### `/api` - HTMX JSON API
|
|
- `GET /api/scoreboard/{game_id}` - Live scoreboard data
|
|
- `POST /api/play` - Execute gameplay action
|
|
|
|
### `/` - HTML Pages
|
|
- `GET /` - Home page
|
|
- `GET /game/{game_id}` - Game interface page
|
|
|
|
## Error Handling
|
|
|
|
Routes should transform service exceptions to appropriate HTTP responses:
|
|
|
|
```python
|
|
try:
|
|
result = await service.do_something()
|
|
return result
|
|
except NotFoundException as e:
|
|
raise HTTPException(status_code=404, detail=str(e))
|
|
except ValidationError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error: {e}")
|
|
raise HTTPException(status_code=500, detail="Internal server error")
|
|
```
|
|
|
|
## Testing Routes
|
|
|
|
Routes should be tested with service dependencies mocked:
|
|
|
|
```python
|
|
from fastapi.testclient import TestClient
|
|
from unittest.mock import Mock
|
|
|
|
def test_start_game(mock_game_service):
|
|
client = TestClient(app)
|
|
|
|
# Mock service response
|
|
mock_game_service.create_game.return_value = Game(id=1)
|
|
|
|
response = client.post("/games/start", json={
|
|
"away_team_id": 1,
|
|
"home_team_id": 2
|
|
})
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["game_id"] == 1
|
|
``` |