# Models Directory This directory contains pure data models for the Paper Dynasty web app, migrated from the Discord app following the **Model/Service Architecture** pattern. ## Architecture Principle **Models = Pure Data | Services = Business Logic** - **Models**: Field definitions, relationships, basic validators only - **Services**: Complex logic, UI formatting, game management, AI decisions ## Migration Status ### ✅ Completed Models | Model | Status | Description | Business Logic Extracted | |-------|--------|-------------|--------------------------| | `ManagerAi` | ✅ Complete | AI configuration data | → `AIService` (9 methods) | | `Cardset` | ✅ Complete | Card set metadata | None (pure data) | ### 🚧 In Progress | Model | Status | Description | Business Logic to Extract | |-------|--------|-------------|--------------------------| | `Team` | 📋 Next | Team identity data | → `UIService` (embed property) | | `Player` | 📋 Planned | Player metadata | → `UIService` (Discord markdown) | ### 📋 Future Phases - **Phase 3**: Game structure (Game, Play models) - **Phase 4**: Card and rating models - **Phase 5**: Web-specific models (sessions, preferences) ## Model Patterns ### Pure Data Model Structure ```python # Base model for validation and field definitions class ModelBase(SQLModel): id: int | None = Field(default=None, primary_key=True) name: str = Field(index=True, description="Field description") @field_validator('name') @classmethod def validate_name(cls, v: str) -> str: # Basic validation only if not v or not v.strip(): raise ValueError("Name cannot be empty") return v # Table model for database operations class Model(ModelBase, table=True): # relationships: List["RelatedModel"] = Relationship(...) pass ``` ### What STAYS in Models ✅ **Field definitions and types** ```python name: str = Field(index=True) ranked_legal: bool = Field(default=False) ``` ✅ **Database relationships** ```python players: List["Player"] = Relationship(back_populates="cardset") ``` ✅ **Basic field validation** ```python @field_validator('name') def validate_name_not_empty(cls, v: str) -> str: if not v.strip(): raise ValueError("Name cannot be empty") return v ``` ### What MOVES to Services ❌ **Complex business logic** ```python # BEFORE (in model) def check_steal_opportunity(self, game, to_base): # Complex AI decision logic... # AFTER (in service) def check_steal_opportunity(self, manager_ai, game, to_base): # Same logic but in AIService ``` ❌ **UI formatting** ```python # BEFORE (in model) @property def embed(self) -> discord.Embed: # Discord-specific formatting... # AFTER (in service) def format_team_display(self, team) -> dict: # Platform-agnostic formatting ``` ❌ **Game mechanics** ```python # BEFORE (in model) def initialize_play(self, session): # Complex game setup logic... # AFTER (in service) def initialize_game(self, game_id) -> Play: # Same logic but in GameService ``` ## Testing Strategy All models use the factory pattern with transaction rollback: ```python # test_model.py def test_model_creation(db_session): model = ModelFactory.create(db_session, field="value") assert model.field == "value" # Automatic rollback ensures isolation ``` See `tests/README.md` for complete testing documentation. ## File Organization ``` models/ ├── __init__.py # Export all models ├── manager_ai.py # ✅ AI configuration (complete) ├── cardset.py # ✅ Card set metadata (complete) ├── team.py # 🚧 Team identity (next) ├── player.py # 📋 Player metadata (planned) ├── game.py # 📋 Game structure (planned) ├── play.py # 📋 Gameplay state (planned) └── ai_responses.py # AI decision response models ``` ## Migration Guidelines When migrating a model from `../discord-app/`: ### 1. Analyze Original Model ```bash # Find the model in Discord app grep -r "class ModelName" ../discord-app/ ``` ### 2. Separate Data from Logic - **Keep**: Field definitions, relationships, basic validation - **Extract**: Methods, computed properties, complex logic ### 3. Create Pure Data Model ```python class ModelBase(SQLModel): # Only field definitions and basic validation class Model(ModelBase, table=True): # Only relationships ``` ### 4. Extract Business Logic ```python class ModelService(BaseService): def extracted_method(self, model_instance, params): # Migrated business logic ``` ### 5. Create Comprehensive Tests ```python # Validation tests (no database) def test_model_validation(): model = ModelFactory.build(invalid_field="bad") # Test validation # Database tests (with rollback) def test_model_persistence(db_session): model = ModelFactory.create(db_session) # Test database operations ``` ### 6. Update Migration Plan - Mark model as complete in `.claude/model-migration-plan.md` - Update this README with new model status ## Dependencies Models depend on: - `sqlmodel` - Database ORM and validation - `pydantic` - Field validation and serialization - `sqlalchemy` - Advanced database features Models should NOT depend on: - `discord.py` - Platform-specific library - `fastapi` - Web framework - Service classes - Business logic layer ## Best Practices ### DO: - ✅ Keep models as simple data containers - ✅ Use descriptive field documentation - ✅ Add basic validation for data integrity - ✅ Follow naming conventions from original models - ✅ Create comprehensive factory-based tests ### DON'T: - ❌ Add business logic methods to models - ❌ Include platform-specific dependencies - ❌ Create computed properties with complex logic - ❌ Hard-code values that belong in services - ❌ Skip validation or tests ## Future Considerations As we complete the migration: 1. **Web-specific models** will be added for session management 2. **Performance optimization** may require relationship tuning 3. **Database migrations** will be managed via Alembic 4. **API serialization** will use Pydantic's serialization features The goal is to have a clean, testable, platform-agnostic data layer that can support web, mobile, and future interfaces.