    # Modular Platform Architecture - Design Plan

## Executive Summary

Design a modular architecture where platforms can enable/disable feature modules. This creates a hierarchy:

```
Global (SaaS Provider)
└── Platform (Business Product - OMS, Loyalty, etc.)
    ├── Modules (Enabled features - Billing, Marketplace, Inventory, etc.)
    │   ├── Routes (API + Page routes)
    │   ├── Services (Business logic)
    │   ├── Menu Items (Sidebar entries)
    │   └── Templates (UI components)
    └── Frontends
        ├── Admin (Platform management)
        ├── Store (Store dashboard)
        └── Customer (Storefront) - future
```

---

## Current State Analysis

### What Exists

| Component | Status | Location |
|-----------|--------|----------|
| Platform Model | ✅ Complete | `models/database/platform.py` |
| Platform Configs | ⚠️ Partial | `app/platforms/{oms,loyalty}/config.py` (routes/templates empty) |
| Feature Registry | ✅ Complete | `models/database/feature.py` (50+ features) |
| Feature Gating | ✅ Complete | `app/core/feature_gate.py` + `app/services/feature_service.py` |
| Subscription Tiers | ✅ Complete | `models/database/subscription.py` (tier→features mapping) |
| Menu System | ✅ Complete | `app/config/menu_registry.py` + `AdminMenuConfig` model |
| Platform Context | ✅ Complete | `middleware/platform_context.py` (domain/path detection) |

### Key Insight: Features vs Modules

**Current "Features"** = granular capabilities (e.g., `analytics_dashboard`, `letzshop_sync`)
- Assigned to subscription tiers
- Gated at API route level
- 50+ individual features

**Proposed "Modules"** = cohesive feature bundles (e.g., `billing`, `marketplace`, `inventory`)
- Enabled/disabled per platform
- Contains multiple features, routes, menu items
- ~10-15 modules total

---

## Proposed Architecture

### Module Definition

A **Module** is a self-contained unit of functionality:

```python
# app/modules/base.py
class ModuleDefinition:
    """Base class for all modules."""

    # Identity
    code: str                    # "billing", "marketplace", "inventory"
    name: str                    # "Billing & Subscriptions"
    description: str

    # Dependencies
    requires: list[str] = []     # Other module codes required

    # Components
    features: list[str] = []     # Feature codes this module provides
    menu_items: dict[FrontendType, list[str]] = {}  # Menu items per frontend

    # Routes (registered dynamically)
    admin_router: APIRouter | None = None
    store_router: APIRouter | None = None

    # Status
    is_core: bool = False        # Core modules cannot be disabled
```

### Module Registry

```python
# app/modules/registry.py
MODULES = {
    # Core modules (always enabled)
    "core": ModuleDefinition(
        code="core",
        name="Core Platform",
        is_core=True,
        features=["dashboard", "settings", "profile"],
        menu_items={
            FrontendType.ADMIN: ["dashboard", "settings"],
            FrontendType.STORE: ["dashboard", "settings"],
        },
    ),

    # Optional modules
    "billing": ModuleDefinition(
        code="billing",
        name="Billing & Subscriptions",
        features=["subscription_management", "billing_history", "stripe_integration"],
        menu_items={
            FrontendType.ADMIN: ["subscription-tiers", "subscriptions", "billing-history"],
            FrontendType.STORE: ["billing"],
        },
        admin_router=billing_admin_router,
        store_router=billing_store_router,
    ),

    "marketplace": ModuleDefinition(
        code="marketplace",
        name="Marketplace (Letzshop)",
        requires=["inventory"],  # Depends on inventory module
        features=["letzshop_sync", "marketplace_import"],
        menu_items={
            FrontendType.ADMIN: ["marketplace-letzshop"],
            FrontendType.STORE: ["letzshop", "marketplace"],
        },
    ),

    "inventory": ModuleDefinition(
        code="inventory",
        name="Inventory Management",
        features=["inventory_basic", "inventory_locations", "low_stock_alerts"],
        menu_items={
            FrontendType.ADMIN: ["inventory"],
            FrontendType.STORE: ["inventory"],
        },
    ),

    # ... more modules
}
```

### Proposed Modules

| Module | Description | Features | Core? |
|--------|-------------|----------|-------|
| `core` | Dashboard, Settings, Profile | 3 | Yes |
| `platform-admin` | Merchants, Stores, Admin Users | 5 | Yes |
| `billing` | Subscriptions, Tiers, Billing History | 4 | No |
| `inventory` | Stock management, locations, alerts | 5 | No |
| `orders` | Order management, fulfillment | 6 | No |
| `marketplace` | Letzshop integration, import | 3 | No |
| `customers` | Customer management, CRM | 4 | No |
| `cms` | Content pages, media library | 6 | No |
| `analytics` | Dashboard, reports, exports | 4 | No |
| `messaging` | Internal messages, notifications | 3 | No |
| `dev-tools` | Components, icons (internal) | 2 | No |
| `monitoring` | Logs, background tasks, imports | 4 | No |

---

## Database Changes

### Option A: JSON Field (Simpler)

Use existing `Platform.settings` JSON field:

```python
# Platform.settings example
{
    "enabled_modules": ["core", "billing", "inventory", "orders"],
    "module_config": {
        "billing": {"stripe_mode": "live"},
        "inventory": {"low_stock_threshold": 10}
    }
}
```

**Pros:** No migration needed, flexible
**Cons:** No referential integrity, harder to query

### Option B: Junction Table (Recommended)

New `PlatformModule` model:

```python
# models/database/platform_module.py
class PlatformModule(Base, TimestampMixin):
    """Module enablement per platform."""

    __tablename__ = "platform_modules"

    id = Column(Integer, primary_key=True)
    platform_id = Column(Integer, ForeignKey("platforms.id"), nullable=False)
    module_code = Column(String(50), nullable=False)
    is_enabled = Column(Boolean, default=True)
    config = Column(JSON, default={})  # Module-specific config
    enabled_at = Column(DateTime)
    enabled_by_user_id = Column(Integer, ForeignKey("users.id"))

    __table_args__ = (
        UniqueConstraint("platform_id", "module_code"),
    )
```

**Pros:** Proper normalization, audit trail, queryable
**Cons:** Requires migration

---

## Implementation Phases

### Phase 1: Module Registry (No DB Changes)

1. Create `app/modules/` directory structure:
   ```
   app/modules/
   ├── __init__.py
   ├── base.py              # ModuleDefinition class
   ├── registry.py          # MODULES dict
   └── service.py           # ModuleService
   ```

2. Define all modules in registry (data only, no behavior change)

3. Create `ModuleService`:
   ```python
   class ModuleService:
       def get_platform_modules(platform_id: int) -> list[str]
       def is_module_enabled(platform_id: int, module_code: str) -> bool
       def get_module_menu_items(platform_id: int, frontend_type: FrontendType) -> list[str]
   ```

4. Initially read from `Platform.settings["enabled_modules"]` (Option A)

### Phase 2: Integrate with Menu System

1. Update `MenuService.get_menu_for_rendering()`:
   - Filter menu items based on enabled modules
   - Module-disabled items don't appear (not just hidden)

2. Update `AdminMenuConfig` logic:
   - Can only configure visibility for module-enabled items
   - Module-disabled items are completely removed

### Phase 3: Database Model (Optional)

1. Create `PlatformModule` model
2. Migration to create table
3. Migrate data from `Platform.settings["enabled_modules"]`
4. Update `ModuleService` to use new table

### Phase 4: Dynamic Route Registration

1. Modify `app/api/v1/admin/__init__.py`:
   ```python
   def register_module_routes(app: FastAPI, platform_code: str):
       enabled_modules = module_service.get_enabled_modules(platform_code)
       for module in enabled_modules:
           if module.admin_router:
               app.include_router(module.admin_router)
   ```

2. Add module check middleware for routes

### Phase 5: Admin UI for Module Management

1. Create `/admin/platforms/{code}/modules` page
2. Toggle modules on/off per platform
3. Show module dependencies
4. Module-specific configuration

---

## Directory Structure Evolution

### Current
```
app/
├── api/v1/
│   ├── admin/          # All admin routes mixed
│   └── store/         # All store routes mixed
├── platforms/
│   ├── oms/config.py   # Platform config only
│   └── loyalty/config.py
└── services/           # All services mixed
```

### Proposed (Gradual Migration)
```
app/
├── modules/
│   ├── base.py
│   ├── registry.py
│   ├── service.py
│   ├── core/           # Core module
│   │   ├── __init__.py
│   │   └── definition.py
│   ├── billing/        # Billing module
│   │   ├── __init__.py
│   │   ├── definition.py
│   │   ├── routes/
│   │   │   ├── admin.py
│   │   │   └── store.py
│   │   └── services/
│   │       └── subscription_service.py
│   ├── marketplace/    # Marketplace module
│   │   └── ...
│   └── inventory/      # Inventory module
│       └── ...
├── api/v1/             # Legacy routes (gradually migrate)
└── platforms/          # Platform-specific overrides
    ├── oms/
    └── loyalty/
```

---

## Key Design Decisions Needed

### 1. Migration Strategy

| Option | Description |
|--------|-------------|
| **A: Big Bang** | Move all code to modules at once |
| **B: Gradual** | Keep existing structure, modules are metadata only initially |
| **C: Hybrid** | New features in modules, migrate existing over time |

**Recommendation:** Option C (Hybrid) - Start with module definitions as metadata, then gradually move code.

### 2. Module Granularity

| Option | Example |
|--------|---------|
| **Coarse** | 5-8 large modules (billing, operations, content) |
| **Medium** | 10-15 medium modules (billing, inventory, orders, cms) |
| **Fine** | 20+ small modules (subscription-tiers, invoices, stock-levels) |

**Recommendation:** Medium granularity - matches current menu sections.

### 3. Core vs Optional

Which modules should be mandatory (cannot be disabled)?

**Proposed Core:**
- `core` (dashboard, settings)
- `platform-admin` (merchants, stores, admin-users)

**Everything else optional** (including billing - some platforms may not charge).

---

## Relationship to Existing Systems

### Modules → Features
- Module contains multiple features
- Enabling module makes its features available for tier assignment
- Features still gated by subscription tier

### Modules → Menu Items
- Module specifies which menu items it provides
- Menu items only visible if module enabled AND menu visibility allows

### Modules → Routes
- Module can provide admin and store routers
- Routes only registered if module enabled
- Existing `require_menu_access()` still applies

### Platform Config → Modules
- `app/platforms/oms/config.py` can specify default modules
- Database `PlatformModule` or `Platform.settings` overrides defaults

---

## Verification Plan

1. **Module definition only (Phase 1)**
   - Define all modules in registry
   - Add `enabled_modules` to Platform.settings
   - Verify ModuleService returns correct modules

2. **Menu integration (Phase 2)**
   - Disable "billing" module for Loyalty platform
   - Verify billing menu items don't appear in sidebar
   - Verify `/admin/subscriptions` returns 404 or redirect

3. **Full module isolation (Phase 4)**
   - Create new platform with minimal modules
   - Verify only enabled module routes are accessible
   - Verify module dependencies are enforced

---

## Decisions Made

| Decision | Choice | Rationale |
|----------|--------|-----------|
| **Storage** | JSON field (`Platform.settings`) | No migration needed, can upgrade to table later |
| **Migration** | Gradual | Module definitions as metadata first, migrate code over time |
| **Billing** | Optional module | Some platforms may not charge (e.g., internal loyalty) |
| **First module** | `billing` | Self-contained, clear routes/services, good isolation test |

---

## Implementation Roadmap

### Immediate (Phase 1): Module Foundation
1. Create `app/modules/` directory with base classes
2. Define module registry with all ~12 modules
3. Create `ModuleService` reading from `Platform.settings`
4. Add `enabled_modules` to OMS and Loyalty platform settings

### Next (Phase 2): Menu Integration
1. Update `MenuService` to filter by enabled modules
2. Test: Disable billing module → billing menu items disappear

### Then (Phase 3): Billing Module Extraction
1. Create `app/modules/billing/` structure
2. Move billing routes and services into module
3. Register billing routes dynamically based on module status

### Future: Additional Modules
- Extract marketplace, inventory, orders, etc.
- Consider junction table if audit trail becomes important

---

## Files to Create/Modify

| File | Action | Purpose |
|------|--------|---------|
| `app/modules/__init__.py` | CREATE | Module package init |
| `app/modules/base.py` | CREATE | ModuleDefinition dataclass |
| `app/modules/registry.py` | CREATE | MODULES dict with all definitions |
| `app/modules/service.py` | CREATE | ModuleService class |
| `app/services/menu_service.py` | MODIFY | Filter by enabled modules |
| `app/platforms/oms/config.py` | MODIFY | Add enabled_modules |
| `app/platforms/loyalty/config.py` | MODIFY | Add enabled_modules |

