refactor: complete module-driven architecture migration
This commit completes the migration to a fully module-driven architecture: ## Models Migration - Moved all domain models from models/database/ to their respective modules: - tenancy: User, Admin, Vendor, Company, Platform, VendorDomain, etc. - cms: MediaFile, VendorTheme - messaging: Email, VendorEmailSettings, VendorEmailTemplate - core: AdminMenuConfig - models/database/ now only contains Base and TimestampMixin (infrastructure) ## Schemas Migration - Moved all domain schemas from models/schema/ to their respective modules: - tenancy: company, vendor, admin, team, vendor_domain - cms: media, image, vendor_theme - messaging: email - models/schema/ now only contains base.py and auth.py (infrastructure) ## Routes Migration - Moved admin routes from app/api/v1/admin/ to modules: - menu_config.py -> core module - modules.py -> tenancy module - module_config.py -> tenancy module - app/api/v1/admin/ now only aggregates auto-discovered module routes ## Menu System - Implemented module-driven menu system with MenuDiscoveryService - Extended FrontendType enum: PLATFORM, ADMIN, VENDOR, STOREFRONT - Added MenuItemDefinition and MenuSectionDefinition dataclasses - Each module now defines its own menu items in definition.py - MenuService integrates with MenuDiscoveryService for template rendering ## Documentation - Updated docs/architecture/models-structure.md - Updated docs/architecture/menu-management.md - Updated architecture validation rules for new exceptions ## Architecture Validation - Updated MOD-019 rule to allow base.py in models/schema/ - Created core module exceptions.py and schemas/ directory - All validation errors resolved (only warnings remain) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,34 +2,26 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This project uses a **hybrid architecture** for models and schemas:
|
||||
This project uses a **module-based architecture** for models and schemas:
|
||||
|
||||
1. **CORE models/schemas** in `models/database/` and `models/schema/` - Framework-level entities used across modules
|
||||
2. **Module models/schemas** in `app/modules/<module>/models/` and `app/modules/<module>/schemas/` - Domain-specific entities
|
||||
1. **Infrastructure** in `models/database/` and `models/schema/` - Base classes only
|
||||
2. **Module models/schemas** in `app/modules/<module>/models/` and `app/modules/<module>/schemas/` - All domain entities
|
||||
3. **Inline schemas** defined directly in API route files - Endpoint-specific request/response models
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
models/ # CORE framework models
|
||||
├── database/ # SQLAlchemy models (ORM)
|
||||
│ ├── __init__.py # Exports all models + module discovery
|
||||
│ ├── base.py # Base class, mixins
|
||||
│ ├── user.py # User authentication
|
||||
│ ├── vendor.py # Vendor, VendorUser, Role
|
||||
│ ├── company.py # Company management
|
||||
│ ├── platform.py # Multi-platform support
|
||||
│ ├── media.py # MediaFile (cross-cutting)
|
||||
│ └── ...
|
||||
models/ # INFRASTRUCTURE ONLY
|
||||
├── database/ # SQLAlchemy base classes
|
||||
│ ├── __init__.py # Exports Base, TimestampMixin only
|
||||
│ └── base.py # Base class, mixins
|
||||
│
|
||||
└── schema/ # Pydantic schemas (API validation)
|
||||
├── __init__.py
|
||||
├── auth.py # Login, tokens, password reset
|
||||
├── vendor.py # Vendor CRUD schemas
|
||||
├── company.py # Company schemas
|
||||
└── ...
|
||||
└── schema/ # Pydantic base classes
|
||||
├── __init__.py # Exports base and auth only
|
||||
├── base.py # Base schema classes
|
||||
└── auth.py # Auth schemas (cross-cutting)
|
||||
|
||||
app/modules/<module>/ # Domain modules
|
||||
app/modules/<module>/ # Domain modules (ALL domain entities)
|
||||
├── models/ # Module-specific database models
|
||||
│ ├── __init__.py # Canonical exports
|
||||
│ └── *.py # Model definitions
|
||||
@@ -43,20 +35,28 @@ app/modules/<module>/ # Domain modules
|
||||
|
||||
## Architecture Principles
|
||||
|
||||
### 1. CORE vs Module Models
|
||||
### 1. Infrastructure vs Module Code
|
||||
|
||||
**CORE models** (`models/database/`) are framework-level entities used across multiple modules:
|
||||
- `User` - Authentication, used by all modules
|
||||
- `Vendor` - Multi-tenant anchor, used by all vendor-scoped modules
|
||||
- `Company` - Business entity management
|
||||
- `Platform` - Multi-platform CMS support
|
||||
- `MediaFile` - File storage, used by catalog, CMS, etc.
|
||||
**Infrastructure** (`models/database/`, `models/schema/`) provides base classes only:
|
||||
- `Base` - SQLAlchemy declarative base
|
||||
- `TimestampMixin` - created_at/updated_at columns
|
||||
- `BaseModel` patterns in `models/schema/base.py`
|
||||
- `auth.py` - Authentication schemas (cross-cutting concern)
|
||||
|
||||
**Module models** (`app/modules/<module>/models/`) are domain-specific:
|
||||
- `billing/models/` - Feature, SubscriptionTier, VendorSubscription
|
||||
- `catalog/models/` - Product, ProductTranslation, ProductMedia
|
||||
- `orders/models/` - Order, OrderItem, Invoice
|
||||
- `inventory/models/` - Inventory, InventoryTransaction
|
||||
**Module code** (`app/modules/<module>/`) contains all domain-specific entities:
|
||||
|
||||
| Module | Models | Schemas |
|
||||
|--------|--------|---------|
|
||||
| `tenancy` | User, Admin, Vendor, Company, Platform, VendorDomain | company, vendor, admin, team, vendor_domain |
|
||||
| `billing` | Feature, SubscriptionTier, VendorSubscription | billing, subscription |
|
||||
| `catalog` | Product, ProductTranslation, ProductMedia | catalog, product, vendor_product |
|
||||
| `orders` | Order, OrderItem, Invoice | order, invoice, order_item_exception |
|
||||
| `inventory` | Inventory, InventoryTransaction | inventory |
|
||||
| `cms` | ContentPage, MediaFile, VendorTheme | content_page, media, image, vendor_theme |
|
||||
| `messaging` | Email, VendorEmailSettings, VendorEmailTemplate, Message, Notification | email, message, notification |
|
||||
| `customers` | Customer, PasswordResetToken | customer, context |
|
||||
| `marketplace` | 5 models | 4 schemas |
|
||||
| `core` | AdminMenuConfig | (inline) |
|
||||
|
||||
### 2. Schema Patterns
|
||||
|
||||
@@ -84,7 +84,7 @@ class InventoryResponse(BaseModel):
|
||||
For schemas only used by a single endpoint or closely related endpoints.
|
||||
|
||||
```python
|
||||
# app/api/v1/admin/platforms.py
|
||||
# app/modules/tenancy/routes/api/admin_platforms.py
|
||||
class PlatformResponse(BaseModel):
|
||||
"""Platform response schema - only used in this file."""
|
||||
id: int
|
||||
@@ -122,7 +122,6 @@ class VendorPlatform(Base):
|
||||
| Schema used by single endpoint | Inline | In the route file |
|
||||
| Admin-only endpoint schemas | Inline | In the admin route file |
|
||||
| Model not exposed via API | No schema | N/A |
|
||||
| Cross-module utility schemas | Dedicated file | `models/schema/` |
|
||||
|
||||
---
|
||||
|
||||
@@ -168,74 +167,100 @@ class VendorPlatform(Base):
|
||||
|
||||
## Import Guidelines
|
||||
|
||||
### Canonical Imports (Preferred)
|
||||
### Canonical Imports (Required)
|
||||
|
||||
```python
|
||||
# CORE models - from models.database
|
||||
from models.database import User, Vendor, MediaFile
|
||||
# Infrastructure - base classes only
|
||||
from models.database import Base, TimestampMixin
|
||||
from models.schema.auth import LoginRequest, TokenResponse
|
||||
|
||||
# Module models - from app.modules.<module>.models
|
||||
from app.modules.tenancy.models import User, Vendor, Company
|
||||
from app.modules.billing.models import Feature, SubscriptionTier
|
||||
from app.modules.catalog.models import Product, ProductMedia
|
||||
from app.modules.orders.models import Order, OrderItem
|
||||
|
||||
# CORE schemas - from models.schema
|
||||
from models.schema.auth import LoginRequest, TokenResponse
|
||||
from app.modules.cms.models import MediaFile, VendorTheme
|
||||
from app.modules.messaging.models import Email, VendorEmailTemplate
|
||||
|
||||
# Module schemas - from app.modules.<module>.schemas
|
||||
from app.modules.tenancy.schemas import VendorCreate, CompanyResponse
|
||||
from app.modules.cms.schemas import MediaItemResponse, VendorThemeResponse
|
||||
from app.modules.messaging.schemas import EmailTemplateResponse
|
||||
from app.modules.inventory.schemas import InventoryCreate, InventoryResponse
|
||||
from app.modules.orders.schemas import OrderResponse
|
||||
```
|
||||
|
||||
### Legacy Re-exports (Backwards Compatibility)
|
||||
### Legacy Imports (DEPRECATED)
|
||||
|
||||
`models/database/__init__.py` re-exports module models for backwards compatibility:
|
||||
The following import patterns are deprecated and will cause architecture validation errors:
|
||||
|
||||
```python
|
||||
# These work but prefer canonical imports
|
||||
from models.database import Product # Re-exported from catalog module
|
||||
from models.database import Order # Re-exported from orders module
|
||||
# DEPRECATED - Don't import domain models from models.database
|
||||
from models.database import User, Vendor # WRONG
|
||||
|
||||
# DEPRECATED - Don't import domain schemas from models.schema
|
||||
from models.schema.vendor import VendorCreate # WRONG
|
||||
from models.schema.company import CompanyResponse # WRONG
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gap Analysis: Models vs Schemas
|
||||
## Module Ownership Reference
|
||||
|
||||
Not every database model needs a dedicated schema file. Here's the current alignment:
|
||||
### Tenancy Module (`app/modules/tenancy/`)
|
||||
|
||||
### CORE Framework
|
||||
**Models:**
|
||||
- `User` - User authentication and profile
|
||||
- `Admin` - Admin user management
|
||||
- `Vendor` - Vendor/merchant entities
|
||||
- `VendorUser` - Vendor team members
|
||||
- `Company` - Company management
|
||||
- `Platform` - Multi-platform support
|
||||
- `AdminPlatform` - Admin-platform association
|
||||
- `VendorPlatform` - Vendor-platform association
|
||||
- `PlatformModule` - Module configuration per platform
|
||||
- `VendorDomain` - Custom domain configuration
|
||||
|
||||
| Database Model | Schema | Notes |
|
||||
|----------------|--------|-------|
|
||||
| `user.py` | `auth.py` | Auth schemas cover user operations |
|
||||
| `vendor.py` | `vendor.py` | Full CRUD schemas |
|
||||
| `company.py` | `company.py` | Full CRUD schemas |
|
||||
| `media.py` | `media.py` | Upload/response schemas |
|
||||
| `platform.py` | Inline | Admin-only, in `platforms.py` route |
|
||||
| `platform_module.py` | Inline | Admin-only, in `modules.py` route |
|
||||
| `admin_menu_config.py` | Inline | Admin-only, in `menu_config.py` route |
|
||||
| `vendor_email_settings.py` | Inline | In `email_settings.py` route |
|
||||
| `vendor_email_template.py` | None | Internal email service use |
|
||||
| `vendor_platform.py` | None | Internal association table |
|
||||
**Schemas:**
|
||||
- `company.py` - Company CRUD schemas
|
||||
- `vendor.py` - Vendor CRUD and Letzshop export schemas
|
||||
- `admin.py` - Admin user and audit log schemas
|
||||
- `team.py` - Team management and invitation schemas
|
||||
- `vendor_domain.py` - Domain configuration schemas
|
||||
|
||||
### Modules
|
||||
### CMS Module (`app/modules/cms/`)
|
||||
|
||||
| Module | Models | Schemas | Alignment |
|
||||
|--------|--------|---------|-----------|
|
||||
| billing | feature, subscription | billing, subscription | ✅ |
|
||||
| cart | cart | cart | ✅ |
|
||||
| catalog | product, product_media, product_translation | catalog, product, vendor_product | ✅ |
|
||||
| checkout | - | checkout | Schema-only (orchestration) |
|
||||
| cms | content_page | content_page, homepage_sections | ✅ |
|
||||
| customers | customer, password_reset_token | customer, context | ✅ (password reset uses auth schemas) |
|
||||
| dev_tools | architecture_scan, test_run | Inline | Admin-only, inline in route files |
|
||||
| inventory | inventory, inventory_transaction | inventory | ✅ (transaction included in inventory.py) |
|
||||
| loyalty | 5 models | 5 schemas | ✅ |
|
||||
| marketplace | 5 models | 4 schemas | ✅ (translation in product schema) |
|
||||
| messaging | admin_notification, message | message, notification | ✅ |
|
||||
| orders | order, order_item, invoice | order, invoice, order_item_exception | ✅ |
|
||||
| payments | - | payment | Schema-only (external APIs) |
|
||||
| analytics | - | stats | Schema-only (aggregation views) |
|
||||
**Models:**
|
||||
- `ContentPage` - CMS content pages
|
||||
- `MediaFile` - File storage and management
|
||||
- `VendorTheme` - Theme customization
|
||||
|
||||
**Schemas:**
|
||||
- `content_page.py` - Content page schemas
|
||||
- `media.py` - Media upload/response schemas
|
||||
- `image.py` - Image handling schemas
|
||||
- `vendor_theme.py` - Theme configuration schemas
|
||||
|
||||
### Messaging Module (`app/modules/messaging/`)
|
||||
|
||||
**Models:**
|
||||
- `Email` - Email records
|
||||
- `VendorEmailSettings` - Email configuration
|
||||
- `VendorEmailTemplate` - Email templates
|
||||
- `Message` - Internal messages
|
||||
- `AdminNotification` - Admin notifications
|
||||
|
||||
**Schemas:**
|
||||
- `email.py` - Email template schemas
|
||||
- `message.py` - Message schemas
|
||||
- `notification.py` - Notification schemas
|
||||
|
||||
### Core Module (`app/modules/core/`)
|
||||
|
||||
**Models:**
|
||||
- `AdminMenuConfig` - Menu visibility configuration
|
||||
|
||||
**Schemas:** (inline in route files)
|
||||
|
||||
---
|
||||
|
||||
@@ -244,16 +269,14 @@ Not every database model needs a dedicated schema file. Here's the current align
|
||||
### Database Model Checklist
|
||||
|
||||
1. **Determine location:**
|
||||
- Cross-module use → `models/database/`
|
||||
- Module-specific → `app/modules/<module>/models/`
|
||||
- All domain models → `app/modules/<module>/models/`
|
||||
|
||||
2. **Create the model file:**
|
||||
```python
|
||||
# app/modules/mymodule/models/my_entity.py
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
from models.database.base import TimestampMixin
|
||||
from models.database import Base, TimestampMixin
|
||||
|
||||
class MyEntity(Base, TimestampMixin):
|
||||
__tablename__ = "my_entities"
|
||||
@@ -299,7 +322,7 @@ Not every database model needs a dedicated schema file. Here's the current align
|
||||
|
||||
3. **Or use inline schema:**
|
||||
```python
|
||||
# app/api/v1/vendor/my_entity.py
|
||||
# app/modules/mymodule/routes/api/vendor.py
|
||||
from pydantic import BaseModel
|
||||
|
||||
class MyEntityResponse(BaseModel):
|
||||
@@ -320,8 +343,8 @@ Not every database model needs a dedicated schema file. Here's the current align
|
||||
|-----------|----------------|
|
||||
| Services access DB directly | `from app.modules.X.models import Model` |
|
||||
| APIs validate with schemas | Request/response Pydantic models |
|
||||
| Reusable schemas | Dedicated files in `schemas/` |
|
||||
| Reusable schemas | Dedicated files in `app/modules/<module>/schemas/` |
|
||||
| Endpoint-specific schemas | Inline in route files |
|
||||
| Internal models | No schema needed |
|
||||
| CORE models | `models/database/` |
|
||||
| Module models | `app/modules/<module>/models/` |
|
||||
| All domain models | `app/modules/<module>/models/` |
|
||||
| Infrastructure only | `models/database/` (Base, TimestampMixin only) |
|
||||
|
||||
Reference in New Issue
Block a user