feat: implement modular platform architecture (Phase 1)
Add module system for enabling/disabling feature bundles per platform. Module System: - ModuleDefinition dataclass for defining modules - 12 modules: core, platform-admin, billing, inventory, orders, marketplace, customers, cms, analytics, messaging, dev-tools, monitoring - Core modules (core, platform-admin) cannot be disabled - Module dependencies (e.g., marketplace requires inventory) MenuService Integration: - Menu items filtered by module enablement - MenuItemConfig includes is_module_enabled and module_code fields - Module-disabled items hidden from sidebar Platform Configuration: - BasePlatformConfig.enabled_modules property - OMS: all modules enabled (full commerce) - Loyalty: focused subset (no billing/inventory/orders/marketplace) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
112
app/modules/base.py
Normal file
112
app/modules/base.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# app/modules/base.py
|
||||
"""
|
||||
Base module definition class.
|
||||
|
||||
A Module is a self-contained unit of functionality that can be enabled/disabled
|
||||
per platform. Each module contains:
|
||||
- Features: Granular capabilities for tier-based access control
|
||||
- Menu items: Sidebar entries per frontend type
|
||||
- Routes: API and page routes (future: dynamically registered)
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastapi import APIRouter
|
||||
|
||||
from models.database.admin_menu_config import FrontendType
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModuleDefinition:
|
||||
"""
|
||||
Definition of a platform module.
|
||||
|
||||
A module groups related functionality that can be enabled/disabled per platform.
|
||||
Core modules cannot be disabled and are always available.
|
||||
|
||||
Attributes:
|
||||
code: Unique identifier (e.g., "billing", "marketplace")
|
||||
name: Display name (e.g., "Billing & Subscriptions")
|
||||
description: Description of what this module provides
|
||||
requires: List of module codes this module depends on
|
||||
features: List of feature codes this module provides
|
||||
menu_items: Dict mapping FrontendType to list of menu item IDs
|
||||
is_core: Core modules cannot be disabled
|
||||
admin_router: FastAPI router for admin routes (future)
|
||||
vendor_router: FastAPI router for vendor routes (future)
|
||||
|
||||
Example:
|
||||
billing_module = ModuleDefinition(
|
||||
code="billing",
|
||||
name="Billing & Subscriptions",
|
||||
description="Subscription tiers, billing history, and payment processing",
|
||||
features=["subscription_management", "billing_history", "stripe_integration"],
|
||||
menu_items={
|
||||
FrontendType.ADMIN: ["subscription-tiers", "subscriptions", "billing-history"],
|
||||
FrontendType.VENDOR: ["billing"],
|
||||
},
|
||||
)
|
||||
"""
|
||||
|
||||
# Identity
|
||||
code: str
|
||||
name: str
|
||||
description: str = ""
|
||||
|
||||
# Dependencies
|
||||
requires: list[str] = field(default_factory=list)
|
||||
|
||||
# Components
|
||||
features: list[str] = field(default_factory=list)
|
||||
menu_items: dict[FrontendType, list[str]] = field(default_factory=dict)
|
||||
|
||||
# Status
|
||||
is_core: bool = False
|
||||
|
||||
# Routes (registered dynamically) - Future implementation
|
||||
admin_router: "APIRouter | None" = None
|
||||
vendor_router: "APIRouter | None" = None
|
||||
|
||||
def get_menu_items(self, frontend_type: FrontendType) -> list[str]:
|
||||
"""Get menu item IDs for a specific frontend type."""
|
||||
return self.menu_items.get(frontend_type, [])
|
||||
|
||||
def get_all_menu_items(self) -> set[str]:
|
||||
"""Get all menu item IDs across all frontend types."""
|
||||
all_items = set()
|
||||
for items in self.menu_items.values():
|
||||
all_items.update(items)
|
||||
return all_items
|
||||
|
||||
def has_feature(self, feature_code: str) -> bool:
|
||||
"""Check if this module provides a specific feature."""
|
||||
return feature_code in self.features
|
||||
|
||||
def has_menu_item(self, menu_item_id: str) -> bool:
|
||||
"""Check if this module provides a specific menu item."""
|
||||
return menu_item_id in self.get_all_menu_items()
|
||||
|
||||
def check_dependencies(self, enabled_modules: set[str]) -> list[str]:
|
||||
"""
|
||||
Check if all required modules are enabled.
|
||||
|
||||
Args:
|
||||
enabled_modules: Set of enabled module codes
|
||||
|
||||
Returns:
|
||||
List of missing required module codes
|
||||
"""
|
||||
return [req for req in self.requires if req not in enabled_modules]
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.code)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, ModuleDefinition):
|
||||
return self.code == other.code
|
||||
return False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Module({self.code}, core={self.is_core})>"
|
||||
Reference in New Issue
Block a user