- Rename shop-api-reference.md to storefront-api-reference.md - Update all /api/v1/shop paths to /api/v1/storefront - Add cart, catalog, checkout modules to module-system.md - Update API index to reference Storefront API - Mark Phase 7 as complete in PLAN document Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
517 lines
20 KiB
Markdown
517 lines
20 KiB
Markdown
# Module System Architecture
|
|
|
|
The Wizamart platform uses a **plug-and-play modular architecture** where modules are fully self-contained and automatically discovered. Simply create a module directory with the required structure, and the framework handles registration, routing, and resource loading automatically.
|
|
|
|
## Key Features
|
|
|
|
- **Auto-Discovery**: Modules are automatically discovered from `app/modules/*/definition.py`
|
|
- **Zero Configuration**: No changes to `main.py`, `registry.py`, or other framework files needed
|
|
- **Self-Contained**: Each module owns its routes, services, models, templates, and translations
|
|
- **Hot-Pluggable**: Add or remove modules by simply adding/removing directories
|
|
|
|
## Overview
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ FRAMEWORK LAYER │
|
|
│ (Infrastructure that modules depend on - not modules themselves) │
|
|
│ │
|
|
│ Config │ Database │ Auth │ Permissions │ Observability │ Celery │
|
|
└─────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ AUTO-DISCOVERED MODULE LAYER │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
│ │ CORE MODULES (Always Enabled) │ │
|
|
│ │ core │ tenancy │ cms │ customers │ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
│ │ OPTIONAL MODULES (Per-Platform) │ │
|
|
│ │ payments │ billing │ inventory │ orders │ marketplace │ ...│ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
│ │ INTERNAL MODULES (Admin Only) │ │
|
|
│ │ dev-tools │ monitoring │ │
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Auto-Discovery System
|
|
|
|
All module components are automatically discovered by the framework:
|
|
|
|
| Component | Discovery Location | Auto-Loaded By |
|
|
|-----------|-------------------|----------------|
|
|
| **Registry** | `*/definition.py` | `app/modules/discovery.py` |
|
|
| **Configuration** | `*/config.py` | `app/modules/config.py` |
|
|
| **API Routes** | `*/routes/api/*.py` | `app/modules/routes.py` |
|
|
| **Page Routes** | `*/routes/pages/*.py` | `app/modules/routes.py` |
|
|
| **Tasks** | `*/tasks/__init__.py` | `app/modules/tasks.py` |
|
|
| **Templates** | `*/templates/` | `app/templates_config.py` |
|
|
| **Static Files** | `*/static/` | `main.py` |
|
|
| **Locales** | `*/locales/*.json` | `app/utils/i18n.py` |
|
|
| **Migrations** | `*/migrations/versions/` | `app/modules/migrations.py` |
|
|
|
|
### Creating a New Module (Zero Framework Changes)
|
|
|
|
```bash
|
|
# 1. Create module directory
|
|
mkdir -p app/modules/mymodule/{routes/{api,pages},services,models,schemas,templates/mymodule/vendor,static/vendor/js,locales,tasks}
|
|
|
|
# 2. Create required files
|
|
touch app/modules/mymodule/__init__.py
|
|
touch app/modules/mymodule/definition.py
|
|
touch app/modules/mymodule/exceptions.py
|
|
|
|
# 3. That's it! The framework auto-discovers and registers everything.
|
|
```
|
|
|
|
## Three-Tier Classification
|
|
|
|
### Core Modules (4)
|
|
|
|
Core modules are **always enabled** and cannot be disabled. They provide fundamental platform functionality.
|
|
|
|
| Module | Description | Key Features |
|
|
|--------|-------------|--------------|
|
|
| `core` | Dashboard, settings, profile | Basic platform operation |
|
|
| `tenancy` | Platform, company, vendor, admin user management | Multi-tenant infrastructure |
|
|
| `cms` | Content pages, media library, themes | Content management |
|
|
| `customers` | Customer database, profiles, segmentation | Customer data management |
|
|
|
|
### Optional Modules (10)
|
|
|
|
Optional modules can be **enabled or disabled per platform**. They provide additional functionality that may not be needed by all platforms.
|
|
|
|
| Module | Dependencies | Description |
|
|
|--------|--------------|-------------|
|
|
| `cart` | - | Shopping cart management, session-based carts |
|
|
| `catalog` | - | Customer-facing product browsing |
|
|
| `checkout` | `cart`, `orders`, `payments` | Cart-to-order conversion, checkout flow |
|
|
| `payments` | - | Payment gateway integrations (Stripe, PayPal, etc.) |
|
|
| `billing` | `payments` | Platform subscriptions, vendor invoices |
|
|
| `inventory` | - | Stock management, locations |
|
|
| `orders` | `payments` | Order management, customer checkout |
|
|
| `marketplace` | `inventory` | Letzshop integration |
|
|
| `analytics` | - | Reports, dashboards |
|
|
| `messaging` | - | Messages, notifications |
|
|
|
|
### Internal Modules (2)
|
|
|
|
Internal modules are **admin-only tools** not exposed to customers or vendors.
|
|
|
|
| Module | Description |
|
|
|--------|-------------|
|
|
| `dev-tools` | Component library, icon browser |
|
|
| `monitoring` | Logs, background tasks, Flower, Grafana integration |
|
|
|
|
## Self-Contained Module Structure
|
|
|
|
Every module follows this standardized structure:
|
|
|
|
```
|
|
app/modules/analytics/
|
|
├── __init__.py # Module package marker
|
|
├── definition.py # ModuleDefinition (REQUIRED for auto-discovery)
|
|
├── config.py # Environment config (auto-discovered)
|
|
├── exceptions.py # Module-specific exceptions
|
|
├── routes/
|
|
│ ├── __init__.py
|
|
│ ├── api/ # API endpoints (auto-discovered)
|
|
│ │ ├── __init__.py
|
|
│ │ ├── admin.py # Must export: router = APIRouter()
|
|
│ │ └── vendor.py # Must export: router = APIRouter()
|
|
│ └── pages/ # HTML page routes (auto-discovered)
|
|
│ ├── __init__.py
|
|
│ └── vendor.py # Must export: router = APIRouter()
|
|
├── services/
|
|
│ ├── __init__.py
|
|
│ └── stats_service.py
|
|
├── models/
|
|
│ ├── __init__.py
|
|
│ └── report.py
|
|
├── schemas/
|
|
│ ├── __init__.py
|
|
│ └── stats.py
|
|
├── templates/ # Auto-discovered by Jinja2
|
|
│ └── analytics/
|
|
│ └── vendor/
|
|
│ └── analytics.html
|
|
├── static/ # Auto-mounted at /static/modules/analytics/
|
|
│ └── vendor/
|
|
│ └── js/
|
|
│ └── analytics.js
|
|
├── locales/ # Auto-loaded translations
|
|
│ ├── en.json
|
|
│ ├── de.json
|
|
│ ├── fr.json
|
|
│ └── lu.json
|
|
├── tasks/ # Auto-discovered by Celery
|
|
│ ├── __init__.py # REQUIRED for Celery discovery
|
|
│ └── reports.py
|
|
└── migrations/ # Auto-discovered by Alembic
|
|
├── __init__.py # REQUIRED for discovery
|
|
└── versions/
|
|
├── __init__.py # REQUIRED for discovery
|
|
└── analytics_001_create_reports.py
|
|
```
|
|
|
|
## Module Definition
|
|
|
|
Each module must have a `definition.py` with a `ModuleDefinition` instance:
|
|
|
|
```python
|
|
# app/modules/analytics/definition.py
|
|
from app.modules.base import ModuleDefinition
|
|
from models.database.admin_menu_config import FrontendType
|
|
|
|
analytics_module = ModuleDefinition(
|
|
# Identity
|
|
code="analytics",
|
|
name="Analytics & Reporting",
|
|
description="Dashboard analytics, custom reports, and data exports.",
|
|
version="1.0.0",
|
|
|
|
# Classification (determines tier)
|
|
is_core=False, # Set True for core modules
|
|
is_internal=False, # Set True for admin-only modules
|
|
|
|
# Dependencies
|
|
requires=[], # List other module codes this depends on
|
|
|
|
# Features (for tier-based gating)
|
|
features=[
|
|
"basic_reports",
|
|
"analytics_dashboard",
|
|
"custom_reports",
|
|
],
|
|
|
|
# Menu items per frontend
|
|
menu_items={
|
|
FrontendType.ADMIN: [], # Analytics uses dashboard
|
|
FrontendType.VENDOR: ["analytics"],
|
|
},
|
|
|
|
# Self-contained module configuration
|
|
is_self_contained=True,
|
|
services_path="app.modules.analytics.services",
|
|
models_path="app.modules.analytics.models",
|
|
schemas_path="app.modules.analytics.schemas",
|
|
exceptions_path="app.modules.analytics.exceptions",
|
|
templates_path="templates",
|
|
locales_path="locales",
|
|
)
|
|
```
|
|
|
|
### ModuleDefinition Fields
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `code` | `str` | Unique identifier (e.g., "billing") |
|
|
| `name` | `str` | Display name |
|
|
| `description` | `str` | What the module provides |
|
|
| `version` | `str` | Semantic version (default: "1.0.0") |
|
|
| `requires` | `list[str]` | Module codes this depends on |
|
|
| `features` | `list[str]` | Feature codes for tier gating |
|
|
| `menu_items` | `dict` | Menu items per frontend type |
|
|
| `is_core` | `bool` | Cannot be disabled if True |
|
|
| `is_internal` | `bool` | Admin-only if True |
|
|
| `is_self_contained` | `bool` | Uses self-contained structure |
|
|
|
|
## Route Auto-Discovery
|
|
|
|
Routes in `routes/api/` and `routes/pages/` are automatically discovered and registered.
|
|
|
|
### API Routes (`routes/api/vendor.py`)
|
|
|
|
```python
|
|
# app/modules/analytics/routes/api/vendor.py
|
|
from fastapi import APIRouter, Depends
|
|
from app.api.deps import get_current_vendor_api, get_db
|
|
|
|
router = APIRouter() # MUST be named 'router' for auto-discovery
|
|
|
|
@router.get("")
|
|
def get_analytics(
|
|
current_user = Depends(get_current_vendor_api),
|
|
db = Depends(get_db),
|
|
):
|
|
"""Get vendor analytics."""
|
|
pass
|
|
```
|
|
|
|
**Auto-registered at:** `/api/v1/vendor/analytics`
|
|
|
|
### Page Routes (`routes/pages/vendor.py`)
|
|
|
|
```python
|
|
# app/modules/analytics/routes/pages/vendor.py
|
|
from fastapi import APIRouter, Depends, Request
|
|
from fastapi.responses import HTMLResponse
|
|
|
|
router = APIRouter() # MUST be named 'router' for auto-discovery
|
|
|
|
@router.get("/{vendor_code}/analytics", response_class=HTMLResponse)
|
|
async def analytics_page(request: Request, vendor_code: str):
|
|
"""Render analytics page."""
|
|
pass
|
|
```
|
|
|
|
**Auto-registered at:** `/vendor/{vendor_code}/analytics`
|
|
|
|
## Framework Layer
|
|
|
|
The Framework Layer provides infrastructure that modules depend on. These are **not modules** - they're always available and cannot be disabled.
|
|
|
|
| Component | Location | Purpose |
|
|
|-----------|----------|---------|
|
|
| Config | `app/core/config.py` | Settings management |
|
|
| Database | `app/core/database.py` | SQLAlchemy sessions |
|
|
| Logging | `app/core/logging.py` | Structured logging |
|
|
| Permissions | `app/core/permissions.py` | RBAC definitions |
|
|
| Feature Gate | `app/core/feature_gate.py` | Tier-based access |
|
|
| Celery | `app/core/celery_config.py` | Task queue |
|
|
| Observability | `app/core/observability.py` | Health checks, metrics, Sentry |
|
|
| Auth Middleware | `middleware/auth.py` | JWT authentication |
|
|
| Context Middleware | `middleware/platform_context.py` | Multi-tenancy |
|
|
| Dependencies | `app/api/deps.py` | FastAPI DI |
|
|
| Base Exceptions | `app/exceptions/base.py` | Exception hierarchy |
|
|
|
|
## Module Dependencies
|
|
|
|
Modules can depend on other modules. When enabling a module, its dependencies are automatically enabled.
|
|
|
|
```
|
|
payments
|
|
↙ ↘
|
|
billing orders ←──┐
|
|
↑ │
|
|
inventory │ │
|
|
↓ cart │
|
|
marketplace ↘ │
|
|
checkout
|
|
```
|
|
|
|
**Dependency Rules:**
|
|
|
|
1. Core modules cannot depend on optional modules
|
|
2. Enabling a module auto-enables its dependencies
|
|
3. Disabling a module auto-disables modules that depend on it
|
|
4. Circular dependencies are not allowed
|
|
|
|
## Module Registry
|
|
|
|
The registry auto-discovers all modules:
|
|
|
|
```python
|
|
from app.modules.registry import (
|
|
MODULES, # All modules (auto-discovered)
|
|
CORE_MODULES, # Core only
|
|
OPTIONAL_MODULES, # Optional only
|
|
INTERNAL_MODULES, # Internal only
|
|
get_module,
|
|
get_core_module_codes,
|
|
get_module_tier,
|
|
)
|
|
|
|
# Get a specific module
|
|
billing = get_module("billing")
|
|
|
|
# Check module tier
|
|
tier = get_module_tier("billing") # Returns "optional"
|
|
|
|
# Get all core module codes
|
|
core_codes = get_core_module_codes() # {"core", "tenancy", "cms", "customers"}
|
|
```
|
|
|
|
## Module Service
|
|
|
|
The `ModuleService` manages module enablement per platform:
|
|
|
|
```python
|
|
from app.modules.service import module_service
|
|
|
|
# Check if module is enabled
|
|
if module_service.is_module_enabled(db, platform_id, "billing"):
|
|
pass
|
|
|
|
# Get all enabled modules for a platform
|
|
modules = module_service.get_platform_modules(db, platform_id)
|
|
|
|
# Enable a module (auto-enables dependencies)
|
|
module_service.enable_module(db, platform_id, "billing", user_id=current_user.id)
|
|
|
|
# Disable a module (auto-disables dependents)
|
|
module_service.disable_module(db, platform_id, "billing", user_id=current_user.id)
|
|
```
|
|
|
|
## Module Configuration
|
|
|
|
Modules can have environment-based configuration using Pydantic Settings. The `config.py` file is auto-discovered by `app/modules/config.py`.
|
|
|
|
```python
|
|
# app/modules/marketplace/config.py
|
|
from pydantic import Field
|
|
from pydantic_settings import BaseSettings
|
|
|
|
class MarketplaceConfig(BaseSettings):
|
|
"""Configuration for marketplace module."""
|
|
|
|
# Settings loaded from env vars with MARKETPLACE_ prefix
|
|
api_timeout: int = Field(default=30, description="API timeout in seconds")
|
|
batch_size: int = Field(default=100, description="Import batch size")
|
|
max_retries: int = Field(default=3, description="Max retry attempts")
|
|
|
|
model_config = {"env_prefix": "MARKETPLACE_"}
|
|
|
|
# Export for auto-discovery
|
|
config_class = MarketplaceConfig
|
|
config = MarketplaceConfig()
|
|
```
|
|
|
|
**Usage:**
|
|
|
|
```python
|
|
# Direct import
|
|
from app.modules.marketplace.config import config
|
|
timeout = config.api_timeout
|
|
|
|
# Via discovery
|
|
from app.modules.config import get_module_config
|
|
config = get_module_config("marketplace")
|
|
```
|
|
|
|
**Environment variables:**
|
|
```bash
|
|
MARKETPLACE_API_TIMEOUT=60
|
|
MARKETPLACE_BATCH_SIZE=500
|
|
```
|
|
|
|
## Module Migrations
|
|
|
|
Each module owns its database migrations in the `migrations/versions/` directory. Alembic auto-discovers these via `app/modules/migrations.py`.
|
|
|
|
### Migration Structure
|
|
|
|
```
|
|
app/modules/cms/migrations/
|
|
├── __init__.py # REQUIRED for discovery
|
|
└── versions/
|
|
├── __init__.py # REQUIRED for discovery
|
|
├── cms_001_create_content_pages.py
|
|
├── cms_002_add_sections.py
|
|
└── cms_003_add_media_library.py
|
|
```
|
|
|
|
### Naming Convention
|
|
|
|
```
|
|
{module_code}_{sequence}_{description}.py
|
|
```
|
|
|
|
### Migration Template
|
|
|
|
```python
|
|
# app/modules/cms/migrations/versions/cms_001_create_content_pages.py
|
|
"""Create content_pages table.
|
|
|
|
Revision ID: cms_001
|
|
Create Date: 2026-01-28
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision = "cms_001"
|
|
down_revision = None
|
|
branch_labels = ("cms",) # Module-specific branch
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"content_pages",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("vendor_id", sa.Integer(), sa.ForeignKey("vendors.id")),
|
|
sa.Column("slug", sa.String(100), nullable=False),
|
|
sa.Column("title", sa.String(200), nullable=False),
|
|
)
|
|
|
|
def downgrade() -> None:
|
|
op.drop_table("content_pages")
|
|
```
|
|
|
|
### Running Migrations
|
|
|
|
Module migrations are automatically discovered:
|
|
|
|
```bash
|
|
# Run all migrations (core + modules)
|
|
alembic upgrade head
|
|
|
|
# View migration history
|
|
alembic history
|
|
```
|
|
|
|
### Current State
|
|
|
|
Currently, all migrations reside in central `alembic/versions/`. The module-specific directories are in place for:
|
|
- **New modules**: Should create migrations in their own `migrations/versions/`
|
|
- **Future reorganization**: Existing migrations will be moved to modules pre-production
|
|
|
|
## Architecture Validation Rules
|
|
|
|
The architecture validator (`scripts/validate_architecture.py`) enforces module structure:
|
|
|
|
| Rule | Severity | Description |
|
|
|------|----------|-------------|
|
|
| MOD-001 | ERROR | Self-contained modules must have required directories |
|
|
| MOD-002 | WARNING | Services must contain actual code, not re-exports |
|
|
| MOD-003 | WARNING | Schemas must contain actual code, not re-exports |
|
|
| MOD-004 | WARNING | Routes must import from module, not legacy locations |
|
|
| MOD-005 | WARNING | Modules with UI must have templates and static |
|
|
| MOD-006 | INFO | Modules should have locales for i18n |
|
|
| MOD-007 | ERROR | Definition paths must match directory structure |
|
|
| MOD-008 | WARNING | Self-contained modules must have exceptions.py |
|
|
| MOD-009 | ERROR | Modules must have definition.py for auto-discovery |
|
|
| MOD-010 | WARNING | Route files must export `router` variable |
|
|
| MOD-011 | WARNING | Tasks directory must have `__init__.py` |
|
|
| MOD-012 | INFO | Locales should have all language files |
|
|
| MOD-013 | INFO | config.py should export `config` or `config_class` |
|
|
| MOD-014 | WARNING | Migrations must follow naming convention |
|
|
| MOD-015 | WARNING | Migrations directory must have `__init__.py` files |
|
|
|
|
Run validation:
|
|
```bash
|
|
python scripts/validate_architecture.py
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Do
|
|
|
|
- Keep modules focused on a single domain
|
|
- Use `requires` for hard dependencies
|
|
- Provide `health_check` for critical modules
|
|
- Use events for cross-module communication
|
|
- Follow the standard directory structure
|
|
- Export `router` variable in route files
|
|
- Include all supported languages in locales
|
|
|
|
### Don't
|
|
|
|
- Create circular dependencies
|
|
- Make core modules depend on optional modules
|
|
- Put framework-level code in modules
|
|
- Skip migration naming conventions
|
|
- Forget `__init__.py` in tasks directory
|
|
- Manually register modules in registry.py (use auto-discovery)
|
|
|
|
## Related Documentation
|
|
|
|
- [Creating Modules](../development/creating-modules.md) - Step-by-step guide
|
|
- [Menu Management](menu-management.md) - Sidebar configuration
|
|
- [Observability](observability.md) - Health checks integration
|
|
- [Feature Gating](../implementation/feature-gating-system.md) - Tier-based access
|