# 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 (5) Core modules are **always enabled** and cannot be disabled. They provide fundamental platform functionality. | Module | Description | Key Features | Permissions | |--------|-------------|--------------|-------------| | `contracts` | Cross-module protocols and interfaces | Service protocols, type-safe interfaces | - | | `core` | Dashboard, settings, profile | Basic platform operation | 5 | | `cms` | Content pages, media library, themes | Content management | 5 | | `customers` | Customer database, profiles, segmentation | Customer data management | 4 | | `tenancy` | Platform, company, vendor, admin user management | Multi-tenant infrastructure | 4 | ### Optional Modules (11) Optional modules can be **enabled or disabled per platform**. They provide additional functionality that may not be needed by all platforms. | Module | Dependencies | Description | Permissions | |--------|--------------|-------------|-------------| | `analytics` | - | Reports, dashboards | 3 | | `billing` | `payments` | Platform subscriptions, vendor invoices | 5 | | `cart` | `inventory` | Shopping cart management, session-based carts | 2 | | `catalog` | `inventory` | Customer-facing product browsing | 6 | | `checkout` | `cart`, `orders`, `payments`, `customers` | Cart-to-order conversion, checkout flow | 2 | | `inventory` | - | Stock management, locations | 3 | | `loyalty` | `customers` | Stamp/points loyalty programs, wallet integration | 4 | | `marketplace` | `inventory` | Letzshop integration | 3 | | `messaging` | - | Messages, notifications | 3 | | `orders` | `payments` | Order management, customer checkout | 4 | | `payments` | - | Payment gateway integrations (Stripe, PayPal, etc.) | 3 | ### 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/ │ ├── admin/js/ # Admin-facing JS for this module │ ├── vendor/js/ # Vendor-facing JS for this module │ │ └── analytics.js │ └── shared/js/ # Shared JS (used by both admin and vendor) ├── 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, PermissionDefinition from app.modules.enums 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", ], # Module-driven permissions (RBAC) permissions=[ PermissionDefinition( id="analytics.view", label_key="analytics.permissions.view", description_key="analytics.permissions.view_desc", category="analytics", ), PermissionDefinition( id="analytics.export", label_key="analytics.permissions.export", description_key="analytics.permissions.export_desc", category="analytics", ), ], # 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 | | `permissions` | `list[PermissionDefinition]` | RBAC permission definitions | | `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 Static Files Each module can have its own static assets (JavaScript, CSS, images) in the `static/` directory. These are automatically mounted at `/static/modules/{module_name}/`. ### Static File Structure ``` app/modules/{module}/static/ ├── admin/js/ # Admin pages for this module ├── vendor/js/ # Vendor pages for this module ├── shared/js/ # Shared across admin/vendor (e.g., feature-store.js) └── shop/js/ # Shop pages (if module has storefront UI) ``` ### Referencing in Templates Use the `{module}_static` URL name: ```html ``` ### Module vs. Platform Static Files | Put in Module | Put in Platform (`static/`) | |---------------|----------------------------| | Module-specific features | Platform-level admin (dashboard, login, platforms, vendors) | | Order management → `orders` module | Vendor core (profile, settings, team) | | Product catalog → `catalog` module | Shared utilities (api-client, utils, icons) | | Billing/subscriptions → `billing` module | Admin user management | | Analytics dashboards → `analytics` module | Platform user management | **Key distinction:** Platform users (admin-users.js, users.js) manage internal platform access. Shop customers (customers.js in customers module) are end-users who purchase from vendors. See [Frontend Structure](frontend-structure.md) for detailed JS file organization. ## 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 ## Entity Auto-Discovery Reference This section details the auto-discovery requirements for each entity type. **All entities must be in modules** - legacy locations are deprecated and will trigger architecture validation errors. ### Routes Routes define API and page endpoints. They are auto-discovered from module directories. | Type | Location | Discovery | Router Name | |------|----------|-----------|-------------| | Admin API | `routes/api/admin.py` | `app/modules/routes.py` | `admin_router` | | Vendor API | `routes/api/vendor.py` | `app/modules/routes.py` | `vendor_router` | | Storefront API | `routes/api/storefront.py` | `app/modules/routes.py` | `router` | | Admin Pages | `routes/pages/admin.py` | `app/modules/routes.py` | `admin_router` | | Vendor Pages | `routes/pages/vendor.py` | `app/modules/routes.py` | `vendor_router` | **Structure:** ``` app/modules/{module}/routes/ ├── __init__.py ├── api/ │ ├── __init__.py │ ├── admin.py # Must export admin_router │ ├── vendor.py # Must export vendor_router │ ├── storefront.py # Must export router (public storefront) │ └── admin_{feature}.py # Sub-routers aggregated in admin.py └── pages/ ├── __init__.py └── vendor.py # Must export vendor_router ``` **Example - Aggregating Sub-Routers:** ```python # app/modules/billing/routes/api/vendor.py from fastapi import APIRouter, Depends from app.api.deps import require_module_access vendor_router = APIRouter( prefix="/billing", dependencies=[Depends(require_module_access("billing"))], ) # Aggregate sub-routers from .vendor_checkout import vendor_checkout_router from .vendor_usage import vendor_usage_router vendor_router.include_router(vendor_checkout_router) vendor_router.include_router(vendor_usage_router) ``` **Legacy Locations (DEPRECATED - will cause errors):** - `app/api/v1/vendor/*.py` - Move to module `routes/api/vendor.py` - `app/api/v1/admin/*.py` - Move to module `routes/api/admin.py` --- ### Services Services contain business logic. They are not auto-discovered but should be in modules for organization. | Location | Import Pattern | |----------|----------------| | `services/*.py` | `from app.modules.{module}.services import service_name` | | `services/__init__.py` | Re-exports all public services | **Structure:** ``` app/modules/{module}/services/ ├── __init__.py # Re-exports: from .order_service import order_service ├── order_service.py # OrderService class + order_service singleton └── fulfillment_service.py # Related services ``` **Example:** ```python # app/modules/orders/services/order_service.py from sqlalchemy.orm import Session from app.modules.orders.models import Order class OrderService: def get_order(self, db: Session, order_id: int) -> Order: return db.query(Order).filter(Order.id == order_id).first() order_service = OrderService() # app/modules/orders/services/__init__.py from .order_service import order_service, OrderService __all__ = ["order_service", "OrderService"] ``` **Legacy Locations (DEPRECATED - will cause errors):** - `app/services/*.py` - Move to module `services/` - `app/services/{module}/` - Move to `app/modules/{module}/services/` --- ### Models Database models (SQLAlchemy). Currently in `models/database/`, migrating to modules. | Location | Base Class | Discovery | |----------|------------|-----------| | `models/*.py` | `Base` from `models.base` | Alembic autogenerate | **Structure:** ``` app/modules/{module}/models/ ├── __init__.py # Re-exports: from .order import Order, OrderItem ├── order.py # Order model └── order_item.py # Related models ``` **Example:** ```python # app/modules/orders/models/order.py from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.orm import relationship from models.base import Base, TimestampMixin class Order(Base, TimestampMixin): __tablename__ = "orders" id = Column(Integer, primary_key=True) vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False) status = Column(String(50), default="pending") items = relationship("OrderItem", back_populates="order") ``` **Legacy Locations (being migrated):** - `models/database/*.py` - Core models remain here, domain models move to modules --- ### Schemas Pydantic schemas for request/response validation. | Location | Base Class | Usage | |----------|------------|-------| | `schemas/*.py` | `BaseModel` from Pydantic | API routes, validation | **Structure:** ``` app/modules/{module}/schemas/ ├── __init__.py # Re-exports all schemas ├── order.py # Order request/response schemas └── order_item.py # Related schemas ``` **Example:** ```python # app/modules/orders/schemas/order.py from pydantic import BaseModel from datetime import datetime class OrderResponse(BaseModel): id: int vendor_id: int status: str created_at: datetime class Config: from_attributes = True class OrderCreateRequest(BaseModel): customer_id: int items: list[OrderItemRequest] ``` **Legacy Locations (DEPRECATED - will cause errors):** - `models/schema/*.py` - Move to module `schemas/` --- ### Tasks (Celery) Background tasks are auto-discovered by Celery from module `tasks/` directories. | Location | Discovery | Registration | |----------|-----------|--------------| | `tasks/*.py` | `app/modules/tasks.py` | Celery autodiscover | **Structure:** ``` app/modules/{module}/tasks/ ├── __init__.py # REQUIRED - imports task functions ├── import_tasks.py # Task definitions └── export_tasks.py # Related tasks ``` **Example:** ```python # app/modules/marketplace/tasks/import_tasks.py from celery import shared_task from app.core.database import SessionLocal @shared_task(bind=True) def process_import(self, job_id: int, vendor_id: int): db = SessionLocal() try: # Process import pass finally: db.close() # app/modules/marketplace/tasks/__init__.py from .import_tasks import process_import from .export_tasks import export_products __all__ = ["process_import", "export_products"] ``` **Legacy Locations (DEPRECATED - will cause errors):** - `app/tasks/*.py` - Move to module `tasks/` --- ### Exceptions Module-specific exceptions inherit from `WizamartException`. | Location | Base Class | Usage | |----------|------------|-------| | `exceptions.py` | `WizamartException` | Domain errors | **Structure:** ``` app/modules/{module}/ └── exceptions.py # All module exceptions ``` **Example:** ```python # app/modules/orders/exceptions.py from app.exceptions import WizamartException class OrderException(WizamartException): """Base exception for orders module.""" pass class OrderNotFoundError(OrderException): """Order not found.""" def __init__(self, order_id: int): super().__init__(f"Order {order_id} not found") self.order_id = order_id class OrderAlreadyFulfilledError(OrderException): """Order has already been fulfilled.""" pass ``` --- ### Templates Jinja2 templates are auto-discovered from module `templates/` directories. The template loader searches `app/templates/` first (for shared templates), then each module's `templates/` directory. | Location | URL Pattern | Discovery | |----------|-------------|-----------| | `templates/{module}/vendor/*.html` | `/vendor/{vendor}/...` | Jinja2 loader | | `templates/{module}/admin/*.html` | `/admin/...` | Jinja2 loader | | `templates/{module}/storefront/*.html` | `/storefront/...` | Jinja2 loader | | `templates/{module}/public/*.html` | `/...` (platform pages) | Jinja2 loader | **Module Template Structure:** ``` app/modules/{module}/templates/ └── {module}/ ├── admin/ │ ├── list.html │ └── partials/ # Module-specific partials │ └── my-partial.html ├── vendor/ │ ├── index.html │ └── detail.html ├── storefront/ # Customer-facing shop pages │ └── products.html └── public/ # Platform marketing pages └── pricing.html ``` **Template Reference:** ```python # In route return templates.TemplateResponse( request=request, name="{module}/vendor/index.html", context={"items": items} ) ``` **Shared Templates (in `app/templates/`):** Some templates remain in `app/templates/` because they are used across all modules: | Directory | Contents | Purpose | |-----------|----------|---------| | `admin/base.html` | Admin layout | Parent template all admin pages extend | | `vendor/base.html` | Vendor layout | Parent template all vendor pages extend | | `storefront/base.html` | Shop layout | Parent template all storefront pages extend | | `platform/base.html` | Public layout | Parent template all public pages extend | | `admin/errors/` | Error pages | HTTP error templates (404, 500, etc.) | | `vendor/errors/` | Error pages | HTTP error templates for vendor | | `storefront/errors/` | Error pages | HTTP error templates for storefront | | `admin/partials/` | Shared partials | Header, sidebar used across admin | | `vendor/partials/` | Shared partials | Header, sidebar used across vendor | | `shared/macros/` | Jinja2 macros | Reusable UI components (buttons, forms, tables) | | `shared/includes/` | Includes | Common HTML snippets | | `invoices/` | PDF templates | Invoice PDF generation | These shared templates provide the "framework" that module templates build upon. Module templates extend base layouts and import shared macros. --- ### Static Files JavaScript, CSS, and images are auto-mounted from module `static/` directories. | Location | URL | Discovery | |----------|-----|-----------| | `static/vendor/js/*.js` | `/static/modules/{module}/vendor/js/*.js` | `main.py` | | `static/admin/js/*.js` | `/static/modules/{module}/admin/js/*.js` | `main.py` | **Structure:** ``` app/modules/{module}/static/ ├── vendor/js/ │ └── {module}.js ├── admin/js/ │ └── {module}.js └── shared/js/ └── common.js ``` **Template Reference:** ```html ``` --- ### Locales (i18n) Translation files are auto-discovered from module `locales/` directories. | Location | Format | Discovery | |----------|--------|-----------| | `locales/*.json` | JSON key-value | `app/utils/i18n.py` | **Structure:** ``` app/modules/{module}/locales/ ├── en.json ├── de.json ├── fr.json └── lb.json ``` **Example:** ```json { "orders.title": "Orders", "orders.status.pending": "Pending", "orders.status.fulfilled": "Fulfilled" } ``` **Usage:** ```python from app.utils.i18n import t message = t("orders.title", locale="en") # "Orders" ``` --- ### Configuration Module-specific environment configuration. | Location | Base Class | Discovery | |----------|------------|-----------| | `config.py` | `BaseSettings` | `app/modules/config.py` | **Example:** ```python # app/modules/marketplace/config.py from pydantic_settings import BaseSettings class MarketplaceConfig(BaseSettings): api_timeout: int = 30 batch_size: int = 100 model_config = {"env_prefix": "MARKETPLACE_"} config = MarketplaceConfig() ``` **Environment Variables:** ```bash MARKETPLACE_API_TIMEOUT=60 MARKETPLACE_BATCH_SIZE=500 ``` ## 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 | | MOD-016 | ERROR | Routes must be in modules, not `app/api/v1/` | | MOD-017 | ERROR | Services must be in modules, not `app/services/` | | MOD-018 | ERROR | Tasks must be in modules, not `app/tasks/` | | MOD-019 | ERROR | Schemas must be in modules, not `models/schema/` | | MOD-020 | WARNING | Module definition must have required attributes (code, name, description, version, features) | | MOD-021 | WARNING | Modules with menus should have features defined | | MOD-022 | INFO | Feature modules should have permissions (unless internal or storefront-only) | | MOD-023 | INFO | Modules with routers should use `get_*_with_routers` pattern | 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