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:
2026-02-01 21:02:56 +01:00
parent 09d7d282c6
commit d7a0ff8818
307 changed files with 5536 additions and 3826 deletions

View File

@@ -1,24 +1,39 @@
# Menu Management Architecture
The Wizamart platform provides a flexible menu system that supports per-platform and per-user customization. This document explains the menu architecture, configuration options, and how menus integrate with the module system.
The Wizamart platform provides a **module-driven menu system** where each module defines its own menu items. The `MenuDiscoveryService` aggregates menus from all enabled modules, applying visibility configuration and permission filtering.
## Overview
```
┌─────────────────────────────────────────────────────────────────────────┐
MENU REGISTRY (Source of Truth)
│ app/config/menu_registry.py │
MODULE DEFINITIONS (Source of Truth) │
│ app/modules/*/definition.py │
│ │
│ Each module defines its menu items per FrontendType: │
│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ ADMIN_MENU_REGISTRY │ │ VENDOR_MENU_REGISTRY │ │
│ │ (Admin panel menus) │ │ (Vendor dashboard menus) │ │
│ │ catalog.definition.py │ │ orders.definition.py │ │
│ │ menus={ADMIN: [...], │ │ menus={ADMIN: [...], │ │
│ │ VENDOR: [...]} │ │ VENDOR: [...]} │ │
│ └─────────────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
VISIBILITY CONFIGURATION
models/database/admin_menu_config.py
MENU DISCOVERY SERVICE
app/modules/core/services/menu_discovery_service.py
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 1. Collect menu items from all enabled modules │ │
│ │ 2. Filter by user permissions (super_admin_only) │ │
│ │ 3. Apply visibility overrides (AdminMenuConfig) │ │
│ │ 4. Sort by section/item order │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ VISIBILITY CONFIGURATION │
│ app/modules/core/models/admin_menu_config.py │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ AdminMenuConfig Table │ │
@@ -32,95 +47,156 @@ The Wizamart platform provides a flexible menu system that supports per-platform
┌─────────────────────────────────────────────────────────────────────────┐
│ FILTERED MENU OUTPUT │
│ │
Registry - Hidden Items - Disabled Modules = Visible Menu Items
Module Menus - Disabled Modules - Hidden Items = Visible Menu
└─────────────────────────────────────────────────────────────────────────┘
```
## Frontend Types
The system supports two distinct frontend types:
The system supports four distinct frontend types:
| Frontend | Description | Users |
|----------|-------------|-------|
| `PLATFORM` | Public marketing pages | Unauthenticated visitors |
| `ADMIN` | Admin panel | Super admins, platform admins |
| `VENDOR` | Vendor dashboard | Vendors on a platform |
| `STOREFRONT` | Customer-facing shop | Shop customers |
```python
from models.database.admin_menu_config import FrontendType
from app.modules.enums import FrontendType
# Use in code
FrontendType.ADMIN # "admin"
FrontendType.VENDOR # "vendor"
FrontendType.PLATFORM # "platform"
FrontendType.ADMIN # "admin"
FrontendType.VENDOR # "vendor"
FrontendType.STOREFRONT # "storefront"
```
## Menu Registry
## Module-Driven Menus
The menu registry (`app/config/menu_registry.py`) is the **single source of truth** for menu structure. The database only stores visibility overrides.
Each module defines its menu items in `definition.py` using dataclasses:
### Admin Menu Structure
### Menu Item Definition
```python
ADMIN_MENU_REGISTRY = {
"frontend_type": FrontendType.ADMIN,
"sections": [
{
"id": "main",
"label": None, # No header
"items": [
{"id": "dashboard", "label": "Dashboard", "icon": "home", "url": "/admin/dashboard"},
],
},
{
"id": "superAdmin",
"label": "Super Admin",
"super_admin_only": True, # Only visible to super admins
"items": [
{"id": "admin-users", "label": "Admin Users", "icon": "shield", "url": "/admin/admin-users"},
],
},
# ... more sections
],
}
# app/modules/base.py
@dataclass
class MenuItemDefinition:
"""Single menu item definition."""
id: str # Unique identifier (e.g., "catalog.products")
label_key: str # i18n key for label
icon: str # Lucide icon name
route: str # URL path
order: int = 100 # Sort order within section
is_mandatory: bool = False # Cannot be hidden by user
is_super_admin_only: bool = False # Only visible to super admins
@dataclass
class MenuSectionDefinition:
"""Section containing menu items."""
id: str # Section identifier
label_key: str # i18n key for section label
icon: str # Section icon
order: int = 100 # Sort order
items: list[MenuItemDefinition] = field(default_factory=list)
is_super_admin_only: bool = False
```
### Admin Menu Sections
### Example Module Definition
| Section ID | Label | Description |
|------------|-------|-------------|
| `main` | (none) | Dashboard - always at top |
| `superAdmin` | Super Admin | Super admin only tools |
| `platformAdmin` | Platform Administration | Companies, vendors, messages |
| `vendorOps` | Vendor Operations | Products, customers, inventory, orders |
| `marketplace` | Marketplace | Letzshop integration |
| `billing` | Billing & Subscriptions | Tiers, subscriptions, billing history |
| `contentMgmt` | Content Management | Platforms, content pages, themes |
| `devTools` | Developer Tools | Components, icons |
| `platformHealth` | Platform Health | Capacity, testing, code quality |
| `monitoring` | Platform Monitoring | Imports, tasks, logs, notifications |
| `settingsSection` | Platform Settings | General settings, email templates, my menu |
```python
# app/modules/catalog/definition.py
### Vendor Menu Sections
from app.modules.base import ModuleDefinition, MenuSectionDefinition, MenuItemDefinition
from app.modules.enums import FrontendType
| Section ID | Label | Description |
|------------|-------|-------------|
| `main` | (none) | Dashboard, analytics |
| `products` | Products & Inventory | Products, inventory, marketplace import |
| `sales` | Sales & Orders | Orders, Letzshop orders, invoices |
| `customers` | Customers | Customers, messages, notifications |
| `shop` | Shop & Content | Content pages, media library |
| `account` | Account & Settings | Team, profile, billing, settings |
catalog_module = ModuleDefinition(
code="catalog",
name="Product Catalog",
description="Product and category management",
version="1.0.0",
is_core=True,
menus={
FrontendType.ADMIN: [
MenuSectionDefinition(
id="catalog",
label_key="menu.catalog",
icon="package",
order=30,
items=[
MenuItemDefinition(
id="products",
label_key="menu.products",
icon="box",
route="/admin/products",
order=10,
is_mandatory=True
),
MenuItemDefinition(
id="categories",
label_key="menu.categories",
icon="folder-tree",
route="/admin/categories",
order=20
),
]
)
],
FrontendType.VENDOR: [
MenuSectionDefinition(
id="products",
label_key="menu.my_products",
icon="package",
order=10,
items=[
MenuItemDefinition(
id="products",
label_key="menu.products",
icon="box",
route="/vendor/{vendor_code}/products",
order=10,
is_mandatory=True
),
]
)
],
}
)
```
### Menu Item Properties
## Menu Discovery Service
Each menu item has these properties:
The `MenuDiscoveryService` aggregates menus from all enabled modules:
| Property | Type | Description |
|----------|------|-------------|
| `id` | string | Unique identifier (used for config and access control) |
| `label` | string | Display text |
| `icon` | string | Heroicons icon name |
| `url` | string | Route URL |
| `super_admin_only` | boolean | (Optional) Only visible to super admins |
```python
from app.modules.core.services.menu_discovery_service import menu_discovery_service
# Get menu for a frontend type
sections = menu_discovery_service.get_menu_for_frontend(
db=db,
frontend_type=FrontendType.ADMIN,
platform_id=1,
user_id=current_user.id,
is_super_admin=current_user.is_super_admin,
)
# Get all menu items for configuration UI
all_items = menu_discovery_service.get_all_menu_items(
db=db,
frontend_type=FrontendType.ADMIN,
platform_id=1,
)
```
### Discovery Flow
1. **Collect**: Get menu definitions from all modules in `MODULES` registry
2. **Filter by Module**: Only include menus from enabled modules for the platform
3. **Filter by Permissions**: Remove `super_admin_only` items for non-super admins
4. **Apply Visibility**: Check `AdminMenuConfig` for hidden items
5. **Sort**: Order sections and items by their `order` field
6. **Return**: List of `MenuSectionDefinition` with filtered items
## Visibility Configuration
@@ -133,23 +209,16 @@ The system uses an **opt-out model**:
### Mandatory Items
Certain menu items cannot be hidden:
Certain menu items cannot be hidden. These are marked with `is_mandatory=True` in their definition:
```python
MANDATORY_MENU_ITEMS = {
FrontendType.ADMIN: frozenset({
"dashboard", # Landing page
"companies", # Platform admin core
"vendors", # Platform admin core
"admin-users", # Super admin core
"settings", # Configuration access
"my-menu", # Must be able to undo changes
}),
FrontendType.VENDOR: frozenset({
"dashboard", # Landing page
"settings", # Configuration access
}),
}
MenuItemDefinition(
id="dashboard",
label_key="menu.dashboard",
icon="home",
route="/admin/dashboard",
is_mandatory=True, # Cannot be hidden
)
```
### Scope Types
@@ -210,6 +279,9 @@ CREATE TABLE admin_menu_configs (
### Examples
```python
from app.modules.core.models import AdminMenuConfig
from app.modules.enums import FrontendType
# Platform "OMS" hides inventory from admin panel
AdminMenuConfig(
frontend_type=FrontendType.ADMIN,
@@ -237,97 +309,65 @@ AdminMenuConfig(
## Module Integration
Menu items are associated with modules. When a module is disabled for a platform, its menu items are automatically hidden.
### Automatic Menu Discovery
### Module to Menu Item Mapping
Each module defines its menu items per frontend:
When a module is enabled/disabled for a platform, the menu discovery service automatically includes/excludes its menu items:
```python
# In module definition
billing_module = ModuleDefinition(
code="billing",
menu_items={
FrontendType.ADMIN: ["subscription-tiers", "subscriptions", "billing-history"],
FrontendType.VENDOR: ["billing", "invoices"],
},
)
```
# Module enablement check happens automatically in MenuDiscoveryService
enabled_modules = module_service.get_enabled_module_codes(db, platform_id)
### Menu Filtering Logic
```python
from app.modules.service import module_service
# Get available menu items (module-aware)
available_items = module_service.get_module_menu_items(
db, platform_id, FrontendType.ADMIN
)
# Check if specific menu item's module is enabled
is_available = module_service.is_menu_item_module_enabled(
db, platform_id, "subscription-tiers", FrontendType.ADMIN
)
# Only modules in enabled_modules have their menus included
for module_code, module_def in MODULES.items():
if module_code in enabled_modules:
# Include this module's menus
pass
```
### Three-Layer Filtering
The final visible menu is computed by three layers:
1. **Registry**: All possible menu items
1. **Module Definitions**: All possible menu items from modules
2. **Module Enablement**: Items from disabled modules are hidden
3. **Visibility Config**: Explicitly hidden items are removed
```
Final Menu = Registry Items
Final Menu = Module Menu Items
- Items from Disabled Modules
- Items with is_visible=False in config
- Items not matching user role (super_admin_only)
```
## Helper Functions
## Menu Service Integration
### Getting Menu Items
The `MenuService` provides the interface used by templates:
```python
from app.config.menu_registry import (
get_all_menu_item_ids,
get_menu_item,
is_super_admin_only_item,
ADMIN_MENU_REGISTRY,
VENDOR_MENU_REGISTRY,
from app.modules.core.services import menu_service
# Get menu for rendering in templates
menu_data = menu_service.get_menu_for_rendering(
db=db,
frontend_type=FrontendType.ADMIN,
platform_id=platform_id,
user_id=user_id,
is_super_admin=is_super_admin,
vendor_code=vendor_code, # For vendor frontend
)
# Get all item IDs for a frontend
admin_items = get_all_menu_item_ids(FrontendType.ADMIN)
# Returns: {'dashboard', 'admin-users', 'companies', ...}
# Get specific item details
item = get_menu_item(FrontendType.ADMIN, "subscription-tiers")
# Returns: {
# 'id': 'subscription-tiers',
# 'label': 'Subscription Tiers',
# 'icon': 'tag',
# 'url': '/admin/subscription-tiers',
# 'section_id': 'billing',
# 'section_label': 'Billing & Subscriptions'
# Returns legacy format for template compatibility:
# {
# "frontend_type": "admin",
# "sections": [
# {
# "id": "main",
# "label": None,
# "items": [{"id": "dashboard", "label": "Dashboard", ...}]
# },
# ...
# ]
# }
# Check if item requires super admin
is_restricted = is_super_admin_only_item("admin-users") # True
```
### Menu Enums
```python
from app.config.menu_registry import AdminMenuItem, VendorMenuItem
# Use enums for type safety
AdminMenuItem.DASHBOARD.value # "dashboard"
AdminMenuItem.INVENTORY.value # "inventory"
VendorMenuItem.PRODUCTS.value # "products"
VendorMenuItem.ANALYTICS.value # "analytics"
```
## Access Control
@@ -380,24 +420,74 @@ Located at `/admin/my-menu`:
- Personal preferences that don't affect other users
- Useful for hiding rarely-used features
## Adding Menu Items to a Module
1. **Define menu sections and items** in your module's `definition.py`:
```python
# app/modules/mymodule/definition.py
from app.modules.base import ModuleDefinition, MenuSectionDefinition, MenuItemDefinition
from app.modules.enums import FrontendType
mymodule = ModuleDefinition(
code="mymodule",
name="My Module",
description="Description of my module",
version="1.0.0",
menus={
FrontendType.ADMIN: [
MenuSectionDefinition(
id="mymodule",
label_key="mymodule.menu.section",
icon="star",
order=50, # Position in sidebar
items=[
MenuItemDefinition(
id="mymodule-main",
label_key="mymodule.menu.main",
icon="star",
route="/admin/mymodule",
order=10,
),
]
)
],
}
)
```
2. **Add translation keys** in your module's locales:
```json
// app/modules/mymodule/locales/en.json
{
"mymodule.menu.section": "My Module",
"mymodule.menu.main": "Main Page"
}
```
3. **The menu is automatically discovered** - no registration needed.
## Best Practices
### Do
- Use menu item enums (`AdminMenuItem`, `VendorMenuItem`) for type safety
- Check module enablement before showing module-specific menu items
- Respect mandatory items - don't try to hide them
- Use `require_menu_access` dependency to protect routes
- Define menus in module `definition.py` using dataclasses
- Use translation keys (`label_key`) for labels
- Set appropriate `order` values for positioning
- Mark essential items as `is_mandatory=True`
- Use `is_super_admin_only=True` for admin-only features
### Don't
- Hardcode menu item IDs as strings (use enums)
- Hardcode menu item labels (use i18n keys)
- Store `is_visible=True` in database (default state, wastes space)
- Allow hiding mandatory items via API
- Create menu items without registering them in the registry
- Create menu items outside of module definitions
## Related Documentation
- [Module System](module-system.md) - Module architecture and menu item mapping
- [Module System](module-system.md) - Module architecture and definitions
- [Multi-Tenant System](multi-tenant.md) - Platform isolation
- [Feature Gating](../implementation/feature-gating-system.md) - Tier-based access

View File

@@ -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) |