Some checks failed
All route files (admin.py, store.py) now export `router` instead of `admin_router`/`store_router`. Consumer code (definition.py, __init__.py) imports as `router as admin_router` where distinction is needed. ModuleDefinition fields remain admin_router/store_router. 64 files changed across all modules. Architecture rules, docs, and migration plan updated. Added noqa:API001 support to validator for pre-existing raw dict endpoints now visible with standardized router name. All 1114 tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
538 lines
22 KiB
Markdown
538 lines
22 KiB
Markdown
# Cross-Module Import Migration Plan
|
|
|
|
**Created:** 2026-02-26
|
|
**Updated:** 2026-02-27
|
|
**Status:** Complete (Service Layer) — Cat 1-4 fully migrated
|
|
**Rules:** MOD-025, MOD-026
|
|
|
|
This document tracks the migration of all cross-module model imports to proper service-based access patterns.
|
|
|
|
## Overview
|
|
|
|
| Category | Description | Files | Priority | Status |
|
|
|----------|-------------|-------|----------|--------|
|
|
| Cat 5 | UserContext legacy import path | 74 | URGENT | Pending |
|
|
| Cat 1 | Direct queries on another module's models | ~47 | URGENT | **DONE** |
|
|
| Cat 2 | Creating instances across module boundaries | ~15 | URGENT | **DONE** |
|
|
| Cat 3 | Aggregation/count queries across boundaries | ~11 | URGENT | **DONE** |
|
|
| Cat 4 | Join queries involving another module's models | ~4 | URGENT | **DONE** |
|
|
| P5 | Provider pattern gaps (widgets, metrics) | ~8 modules | Incremental | Pending |
|
|
| P6 | Route variable naming standardization | ~70 files | Low | **DONE** |
|
|
|
|
## Completed Service-Layer Migration (2026-02-27)
|
|
|
|
**Result:** Zero top-level cross-module model imports remain in any service file. All 1114 tests pass.
|
|
|
|
### Patterns Used
|
|
|
|
| Pattern | When Used | Files |
|
|
|---------|-----------|-------|
|
|
| **Service method call** | Owning module has/got a service method | Most files — replaced `db.query(Model)` with `some_service.get_by_id()` |
|
|
| **`from __future__ import annotations` + `TYPE_CHECKING`** | Type hints only, no runtime usage | `invoice_service.py`, `marketplace_import_job_service.py`, `stripe_service.py`, etc. |
|
|
| **Method-body deferred import** | 1-2 methods need the model | `product_service.py`, `product_media_service.py`, `platform_settings_service.py` |
|
|
| **`_get_model()` helper** | 3+ methods use same infrastructure model | `log_service.py`, `admin_audit_service.py`, `admin_settings_service.py`, `admin_notification_service.py`, `platform_service.py` |
|
|
| **Instance-cached `self._Model`** | Model used in nearly every method | `letzshop/order_service.py` (Order/OrderItem) |
|
|
| **`joinedload()` replacement** | Replaced `.join(Model)` with eager loading via ORM relationship | `inventory_service.py`, `admin_audit_service.py` |
|
|
| **Pre-query ID filtering** | Get IDs from service, then `Model.id.in_(ids)` | All `*_metrics.py`, `*_features.py` files (StorePlatform → `platform_service.get_store_ids_for_platform()`) |
|
|
|
|
See [Cross-Module Import Rules](cross-module-import-rules.md#6-method-body-deferred-imports) for detailed pattern documentation.
|
|
|
|
### New Service Methods Added
|
|
|
|
| Module | Method | Purpose |
|
|
|--------|--------|---------|
|
|
| `tenancy/platform_service` | `get_default_platform(db)` | Returns first platform |
|
|
| `tenancy/platform_service` | `get_primary_platform_id_for_store(db, store_id)` | Primary platform ID for a store |
|
|
| `tenancy/store_service` | `list_all_stores(db, active_only)` | All stores (with optional active filter) |
|
|
| `tenancy/store_service` | `is_letzshop_slug_claimed(db, slug)` | Check if Letzshop slug is claimed |
|
|
| `tenancy/store_service` | `is_store_code_taken(db, code)` | Check store code uniqueness |
|
|
| `tenancy/store_service` | `is_subdomain_taken(db, subdomain)` | Check subdomain uniqueness |
|
|
| `tenancy/admin_service` | `get_user_by_email(db, email)` | Lookup user by email |
|
|
| `tenancy/admin_service` | `get_user_by_username(db, username)` | Lookup user by username |
|
|
| `billing/subscription_service` | `get_all_active_subscriptions(db)` | All active/trial subscriptions |
|
|
| `catalog/product_service` | `get_products_with_gtin(db, store_id)` | Products that have GTINs |
|
|
| `inventory/inventory_service` | `delete_inventory_by_gtin(db, gtin)` | Delete inventory by GTIN |
|
|
| `inventory/inventory_service` | `get_inventory_by_gtin(db, gtin)` | Get inventory records by GTIN |
|
|
| `marketplace/import_job_service` | `get_import_job_stats(db)` | Import job statistics with processing/today counts |
|
|
|
|
### Files Migrated (by module)
|
|
|
|
**catalog/** (5 files): `catalog_metrics.py`, `catalog_features.py`, `product_service.py`, `product_media_service.py`, `store_product_service.py`
|
|
|
|
**orders/** (4 files): `order_metrics.py`, `order_features.py`, `order_item_exception_service.py`, `order_inventory_service.py`
|
|
|
|
**inventory/** (3 files): `inventory_metrics.py`, `inventory_service.py`, `inventory_import_service.py`
|
|
|
|
**marketplace/** (8 files): `marketplace_widgets.py`, `marketplace_product_service.py`, `marketplace_import_job_service.py`, `onboarding_service.py`, `platform_signup_service.py`, `letzshop_export_service.py`, `letzshop/order_service.py`, `letzshop/store_sync_service.py`
|
|
|
|
**monitoring/** (5 files): `admin_audit_service.py`, `audit_provider.py`, `background_tasks_service.py`, `capacity_forecast_service.py`, `log_service.py`, `platform_health_service.py`
|
|
|
|
**messaging/** (3 files): `email_service.py`, `store_email_settings_service.py`, `admin_notification_service.py`
|
|
|
|
**cms/** (3 files): `cms_metrics.py`, `store_theme_service.py`, `content_page_service.py`
|
|
|
|
**core/** (2 files): `admin_settings_service.py`, `platform_settings_service.py`
|
|
|
|
**customers/** (2 files): `customer_metrics.py`, `admin_customer_service.py`
|
|
|
|
**tenancy/** (1 file): `platform_service.py`
|
|
|
|
**cart/** (1 file): `cart_service.py`
|
|
|
|
---
|
|
|
|
## Cat 5: Move UserContext to Tenancy Module (74 files)
|
|
|
|
**Priority:** URGENT — mechanical, low risk, high impact
|
|
**Approach:** Move definition, update all imports in one batch
|
|
|
|
### What
|
|
|
|
`UserContext` is defined in `models/schema/auth.py` (legacy location). Per MOD-019, schemas belong in their module. UserContext is a tenancy concern (user identity, platform/store context).
|
|
|
|
### Steps
|
|
|
|
1. **Move `UserContext` class** from `models/schema/auth.py` to `app/modules/tenancy/schemas/auth.py`
|
|
- Keep all properties and methods intact
|
|
- Also move related schemas used alongside it: `UserLogin`, `LogoutResponse`, `StoreUserResponse`
|
|
|
|
2. **Add re-export in legacy location** (temporary backwards compat):
|
|
```python
|
|
# models/schema/auth.py
|
|
from app.modules.tenancy.schemas.auth import UserContext # noqa: F401
|
|
```
|
|
|
|
3. **Update all 74 import sites** from:
|
|
```python
|
|
from models.schema.auth import UserContext
|
|
```
|
|
to:
|
|
```python
|
|
from app.modules.tenancy.schemas.auth import UserContext
|
|
```
|
|
|
|
4. **Remove legacy re-export** once all imports are updated
|
|
|
|
### Files to Update (by module)
|
|
|
|
**app/api/** (1 file)
|
|
- `app/api/deps.py`
|
|
|
|
**app/modules/tenancy/** (15 files)
|
|
- `routes/api/admin_merchants.py`
|
|
- `routes/api/admin_module_config.py`
|
|
- `routes/api/admin_merchant_domains.py`
|
|
- `routes/api/admin_modules.py`
|
|
- `routes/api/admin_platforms.py`
|
|
- `routes/api/admin_store_domains.py`
|
|
- `routes/api/admin_stores.py`
|
|
- `routes/api/admin_users.py`
|
|
- `routes/api/merchant.py`
|
|
- `routes/api/store_auth.py` (also imports `LogoutResponse`, `StoreUserResponse`, `UserLogin`)
|
|
- `routes/api/store_profile.py`
|
|
- `routes/api/store_team.py`
|
|
- `routes/pages/merchant.py`
|
|
- `services/admin_platform_service.py`
|
|
- `tests/integration/test_merchant_routes.py`
|
|
|
|
**app/modules/core/** (12 files)
|
|
- `routes/api/admin_dashboard.py`
|
|
- `routes/api/admin_menu_config.py`
|
|
- `routes/api/admin_settings.py`
|
|
- `routes/api/merchant_menu.py`
|
|
- `routes/api/store_dashboard.py`
|
|
- `routes/api/store_menu.py`
|
|
- `routes/api/store_settings.py`
|
|
- `routes/pages/merchant.py`
|
|
- `tests/integration/test_merchant_dashboard_routes.py`
|
|
- `tests/integration/test_merchant_menu_routes.py`
|
|
- `tests/integration/test_store_dashboard_routes.py`
|
|
|
|
**app/modules/billing/** (9 files)
|
|
- `routes/api/admin.py`
|
|
- `routes/api/admin_features.py`
|
|
- `routes/api/store.py`
|
|
- `routes/api/store_addons.py`
|
|
- `routes/api/store_checkout.py`
|
|
- `routes/api/store_features.py`
|
|
- `routes/api/store_usage.py`
|
|
- `routes/pages/merchant.py`
|
|
- `tests/integration/test_merchant_routes.py`
|
|
|
|
**app/modules/marketplace/** (8 files)
|
|
- `routes/api/admin_letzshop.py`
|
|
- `routes/api/admin_marketplace.py`
|
|
- `routes/api/admin_products.py`
|
|
- `routes/api/store_letzshop.py`
|
|
- `routes/api/store_marketplace.py`
|
|
- `routes/api/store_onboarding.py`
|
|
- `tests/integration/test_store_page_routes.py`
|
|
- `tests/unit/test_store_page_routes.py`
|
|
|
|
**app/modules/messaging/** (7 files)
|
|
- `routes/api/admin_email_templates.py`
|
|
- `routes/api/admin_messages.py`
|
|
- `routes/api/admin_notifications.py`
|
|
- `routes/api/store_email_settings.py`
|
|
- `routes/api/store_email_templates.py`
|
|
- `routes/api/store_messages.py`
|
|
- `routes/api/store_notifications.py`
|
|
|
|
**app/modules/orders/** (6 files)
|
|
- `routes/api/admin.py`
|
|
- `routes/api/admin_exceptions.py`
|
|
- `routes/api/store.py`
|
|
- `routes/api/store_customer_orders.py`
|
|
- `routes/api/store_exceptions.py`
|
|
- `routes/api/store_invoices.py`
|
|
|
|
**app/modules/monitoring/** (6 files)
|
|
- `routes/api/admin_audit.py`
|
|
- `routes/api/admin_code_quality.py`
|
|
- `routes/api/admin_logs.py`
|
|
- `routes/api/admin_platform_health.py`
|
|
- `routes/api/admin_tasks.py`
|
|
- `routes/api/admin_tests.py`
|
|
|
|
**app/modules/cms/** (4 files)
|
|
- `routes/api/admin_images.py`
|
|
- `routes/api/admin_media.py`
|
|
- `routes/api/admin_store_themes.py`
|
|
- `routes/api/store_media.py`
|
|
|
|
**app/modules/catalog/** (2 files)
|
|
- `routes/api/admin.py`
|
|
- `routes/api/store.py`
|
|
|
|
**app/modules/customers/** (2 files)
|
|
- `routes/api/admin.py`
|
|
- `routes/api/store.py`
|
|
|
|
**app/modules/inventory/** (2 files)
|
|
- `routes/api/admin.py`
|
|
- `routes/api/store.py`
|
|
|
|
**app/modules/loyalty/** (2 files)
|
|
- `routes/pages/merchant.py`
|
|
- `tests/conftest.py`
|
|
|
|
**app/modules/payments/** (1 file)
|
|
- `routes/api/store.py`
|
|
|
|
**tests/** (1 file)
|
|
- `tests/unit/api/test_deps.py`
|
|
|
|
**docs/** (2 files — update code examples)
|
|
- `docs/architecture/user-context-pattern.md`
|
|
- `docs/proposals/decouple-modules.md`
|
|
|
|
---
|
|
|
|
## Cat 1: Direct Queries → Service Methods (~47 violations)
|
|
|
|
**Priority:** URGENT — requires creating new service methods first
|
|
**Approach:** Per-module, add service methods then migrate consumers
|
|
|
|
### Required New Service Methods
|
|
|
|
#### Tenancy Module (most consumed)
|
|
|
|
The tenancy module needs these public service methods for cross-module consumers:
|
|
|
|
```python
|
|
# app/modules/tenancy/services/merchant_service.py (new or extend existing)
|
|
class MerchantService:
|
|
def get_merchant_by_id(self, db, merchant_id) -> Merchant | None
|
|
def get_merchant_by_owner_id(self, db, owner_user_id) -> Merchant | None
|
|
def get_merchants_for_platform(self, db, platform_id, **filters) -> list[Merchant]
|
|
def get_merchant_count(self, db, platform_id=None) -> int
|
|
def search_merchants(self, db, query, platform_id=None) -> list[Merchant]
|
|
|
|
# app/modules/tenancy/services/store_service.py (extend existing)
|
|
class StoreService:
|
|
def get_store_by_id(self, db, store_id) -> Store | None
|
|
def get_stores_for_merchant(self, db, merchant_id) -> list[Store]
|
|
def get_stores_for_platform(self, db, platform_id, **filters) -> list[Store]
|
|
def get_store_count(self, db, merchant_id=None, platform_id=None) -> int
|
|
def get_active_store_count(self, db, platform_id) -> int
|
|
|
|
# app/modules/tenancy/services/platform_service.py (extend existing)
|
|
class PlatformService:
|
|
def get_platform_by_id(self, db, platform_id) -> Platform | None
|
|
def list_platforms(self, db) -> list[Platform]
|
|
|
|
# app/modules/tenancy/services/user_service.py (extend existing)
|
|
class UserService:
|
|
def get_user_by_id(self, db, user_id) -> User | None
|
|
def get_user_by_email(self, db, email) -> User | None
|
|
def get_store_users(self, db, store_id) -> list[StoreUser]
|
|
```
|
|
|
|
#### Catalog Module
|
|
|
|
```python
|
|
# app/modules/catalog/services/product_service.py (extend existing)
|
|
class ProductService:
|
|
def get_product_by_id(self, db, product_id) -> Product | None
|
|
def get_products_by_ids(self, db, product_ids) -> list[Product]
|
|
def get_product_count(self, db, store_id=None) -> int
|
|
```
|
|
|
|
#### Orders Module
|
|
|
|
```python
|
|
# app/modules/orders/services/order_service.py (extend existing)
|
|
class OrderService:
|
|
def get_order_by_id(self, db, order_id) -> Order | None
|
|
def get_order_count(self, db, store_id=None, **filters) -> int
|
|
def get_orders_for_store(self, db, store_id, **filters) -> list[Order]
|
|
```
|
|
|
|
### Migration Per Consuming Module
|
|
|
|
#### billing → tenancy (13 direct queries)
|
|
|
|
| File | What it queries | Replace with |
|
|
|------|----------------|--------------|
|
|
| `services/admin_subscription_service.py:279` | `db.query(Platform)` | `platform_service.get_platform_by_id()` |
|
|
| `services/admin_subscription_service.py:285` | `db.query(Store)` | `store_service.get_store_by_id()` |
|
|
| `services/admin_subscription_service.py:362` | `db.query(Store).filter()` | `store_service.get_stores_for_merchant()` |
|
|
| `services/billing_service.py:158` | `db.query(Store)` | `store_service.get_store_by_id()` |
|
|
| `services/billing_service.py:497` | `db.query(Merchant)` | `merchant_service.get_merchant_by_id()` |
|
|
| `services/feature_service.py:118,145` | `db.query(StorePlatform)` | `store_service.get_store_platform()` |
|
|
| `services/store_platform_sync_service.py:14` | `db.query(StorePlatform)` | `store_service` methods |
|
|
| `services/stripe_service.py:26,297,316` | `db.query(Merchant/Store)` | `merchant_service/store_service` |
|
|
| `services/subscription_service.py:56,74,178,191` | `db.query(Platform/Store)` | service methods |
|
|
| `services/usage_service.py:22` | `db.query(Store)` | `store_service` |
|
|
|
|
#### loyalty → tenancy (10 direct queries)
|
|
|
|
| File | What it queries | Replace with |
|
|
|------|----------------|--------------|
|
|
| `services/card_service.py` | `db.query(Store/User)` | `store_service/user_service` |
|
|
| `services/program_service.py` | `db.query(Merchant)` | `merchant_service` |
|
|
| `services/admin_loyalty_service.py` | `db.query(Merchant)` | `merchant_service` |
|
|
|
|
#### marketplace → tenancy (5 direct queries)
|
|
|
|
| File | What it queries | Replace with |
|
|
|------|----------------|--------------|
|
|
| `services/letzshop/order_service.py:76` | `db.query(Store)` | `store_service.get_store_by_id()` |
|
|
| `services/letzshop/order_service.py:100,110` | `db.query(Order)` | `order_service.get_order_count()` |
|
|
| `services/marketplace_metrics.py:203` | `db.query(StorePlatform)` | `store_service.get_store_count()` |
|
|
|
|
#### core → tenancy (3 direct queries)
|
|
|
|
| File | What it queries | Replace with |
|
|
|------|----------------|--------------|
|
|
| `services/auth_service.py:156` | `db.query(Merchant)` | `merchant_service.get_merchant_by_owner_id()` |
|
|
| `services/auth_service.py:25` | `db.query(Store)` | `store_service.get_store_by_id()` |
|
|
| `services/menu_service.py:351` | `db.query(Store).join()` | `store_service.get_stores_for_merchant()` |
|
|
|
|
#### analytics → tenancy/catalog (4 direct queries)
|
|
|
|
| File | What it queries | Replace with |
|
|
|------|----------------|--------------|
|
|
| `services/stats_service.py:69` | `db.query(Product)` | `product_service.get_product_count()` |
|
|
| `services/stats_service.py:75` | `db.query(Product)` | `product_service` methods |
|
|
| `services/stats_service.py:88-92` | `db.query(MarketplaceProduct)` | marketplace service |
|
|
| `services/stats_service.py:229` | `db.query(Product)` aggregation | `product_service` |
|
|
|
|
#### Other modules (various)
|
|
|
|
| Module | File | Replace with |
|
|
|--------|------|--------------|
|
|
| `inventory` | `services/inventory_transaction_service.py:236` | `order_service.get_order_by_id()` |
|
|
| `cms` | `services/cms_features.py:160` | `store_service.get_store_count()` |
|
|
| `customers` | `services/admin_customer_service.py:16` | `store_service.get_store_by_id()` |
|
|
|
|
---
|
|
|
|
## Cat 2: Model Creation Across Boundaries (~15 violations)
|
|
|
|
**Priority:** URGENT
|
|
**Approach:** Add create/factory methods to owning services
|
|
|
|
Most of these are in **test fixtures** (acceptable exception) but a few are in production code:
|
|
|
|
### Production Code Violations
|
|
|
|
| File | Creates | Replace with |
|
|
|------|---------|--------------|
|
|
| `contracts/audit.py:117` | `AdminAuditLog()` | Use audit provider `log_action()` |
|
|
| `billing/services/store_platform_sync_service.py` | `StorePlatform()` | `store_service.create_store_platform()` |
|
|
| `marketplace/services/letzshop/order_service.py` | `Order/OrderItem()` | `order_service.create_order()` |
|
|
|
|
### Test Fixtures (Acceptable — Document as Exception)
|
|
|
|
Test files creating models from other modules for integration test setup is acceptable but should be documented:
|
|
|
|
```python
|
|
# ACCEPTABLE in tests — document with comment:
|
|
# Test fixture: creates tenancy models for integration test setup
|
|
merchant = Merchant(name="Test", ...)
|
|
```
|
|
|
|
---
|
|
|
|
## Cat 3: Aggregation/Count Queries (~11 violations)
|
|
|
|
**Priority:** URGENT
|
|
**Approach:** Add count/stats methods to owning services
|
|
|
|
| File | Query | Replace with |
|
|
|------|-------|--------------|
|
|
| `marketplace/services/letzshop/order_service.py:100` | `func.count(Order.id)` | `order_service.get_order_count()` |
|
|
| `marketplace/services/letzshop/order_service.py:110` | `func.count(Order.id)` | `order_service.get_order_count()` |
|
|
| `catalog/services/catalog_features.py:92` | `StorePlatform` count | `store_service.get_active_store_count()` |
|
|
| `cms/services/cms_features.py:160` | `StorePlatform` count | `store_service.get_active_store_count()` |
|
|
| `marketplace/services/marketplace_metrics.py:203` | `StorePlatform` count | `store_service.get_active_store_count()` |
|
|
| `customers/services/customer_features.py` | Store count | `store_service.get_store_count()` |
|
|
| `inventory/services/inventory_features.py` | Store count | `store_service.get_store_count()` |
|
|
| `orders/services/order_features.py` | Store count | `store_service.get_store_count()` |
|
|
|
|
---
|
|
|
|
## Cat 4: Join Queries (~4 violations)
|
|
|
|
**Priority:** URGENT
|
|
**Approach:** Decompose into service calls or add service methods
|
|
|
|
| File | Join | Resolution |
|
|
|------|------|------------|
|
|
| `catalog/services/store_product_service.py:19` | `.join(Store)` | Use `store_service.get_store_by_id()` + own query |
|
|
| `core/services/menu_service.py:351` | `.join(Store)` | `store_service.get_stores_for_merchant()` |
|
|
| `customers/services/admin_customer_service.py:16` | `.join(Store)` | `store_service.get_store_by_id()` + own query |
|
|
| `messaging/services/messaging_service.py:653` | `.join(StoreUser)` | `user_service.get_store_users()` + filter |
|
|
|
|
---
|
|
|
|
## P5: Provider Pattern Gaps (Incremental)
|
|
|
|
**Priority:** MEDIUM — implement as each module is touched
|
|
|
|
### Widget Providers to Add
|
|
|
|
Currently only 2 modules (marketplace, tenancy) provide dashboard widgets. These modules have valuable dashboard data:
|
|
|
|
| Module | Widget Ideas | Implementation |
|
|
|--------|-------------|----------------|
|
|
| **orders** | Recent orders list, order status breakdown | `services/order_widgets.py` |
|
|
| **billing** | Subscription status, revenue trend | `services/billing_widgets.py` |
|
|
| **catalog** | Product summary, category breakdown | `services/catalog_widgets.py` |
|
|
| **inventory** | Stock summary, low-stock alerts | `services/inventory_widgets.py` |
|
|
| **loyalty** | Program stats, member engagement | `services/loyalty_widgets.py` |
|
|
| **customers** | Customer growth, segments | `services/customer_widgets.py` |
|
|
|
|
### Metrics Providers to Add
|
|
|
|
| Module | Metric Ideas | Implementation |
|
|
|--------|-------------|----------------|
|
|
| **loyalty** | Active programs, total enrollments, points issued | `services/loyalty_metrics.py` |
|
|
| **payments** | Transaction volume, success rate, gateway stats | `services/payment_metrics.py` |
|
|
| **analytics** | Report count, active dashboards | `services/analytics_metrics.py` |
|
|
|
|
### Implementation Template
|
|
|
|
```python
|
|
# app/modules/{module}/services/{module}_widgets.py
|
|
from app.modules.contracts.widgets import (
|
|
DashboardWidgetProviderProtocol,
|
|
ListWidget,
|
|
BreakdownWidget,
|
|
)
|
|
|
|
class {Module}WidgetProvider:
|
|
@property
|
|
def widget_category(self) -> str:
|
|
return "{module}"
|
|
|
|
def get_store_widgets(self, db, store_id, context=None):
|
|
# Return list of ListWidget/BreakdownWidget
|
|
...
|
|
|
|
def get_platform_widgets(self, db, platform_id, context=None):
|
|
...
|
|
|
|
{module}_widget_provider = {Module}WidgetProvider()
|
|
```
|
|
|
|
Register in `definition.py`:
|
|
```python
|
|
def _get_widget_provider():
|
|
from app.modules.{module}.services.{module}_widgets import {module}_widget_provider
|
|
return {module}_widget_provider
|
|
|
|
{module}_module = ModuleDefinition(
|
|
...
|
|
widget_provider=_get_widget_provider,
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## P6: Route Variable Naming — DONE (2026-02-27)
|
|
|
|
**Priority:** LOW — cosmetic, no functional impact
|
|
**Files Changed:** ~70 (25 route files + 40 definition/init files + architecture rules + docs)
|
|
|
|
All route files now export `router` instead of `admin_router`/`store_router`. Consumer code (definition.py, `__init__.py`) imports as `router as admin_router` where distinction is needed. The `ModuleDefinition` dataclass fields remain `admin_router`/`store_router`.
|
|
|
|
**Pattern:**
|
|
```python
|
|
# Route file (admin.py, store.py) — uses `router`
|
|
router = APIRouter(prefix="/billing", ...)
|
|
|
|
# definition.py — imports as `router`, assigns to distinct field
|
|
def _get_admin_router():
|
|
from app.modules.billing.routes.api.admin import router
|
|
return router
|
|
|
|
billing_module.admin_router = _get_admin_router()
|
|
```
|
|
|
|
---
|
|
|
|
## Execution Order
|
|
|
|
### Phase 1: Foundation (Do First) — DONE
|
|
1. ~~**Cat 5**: Move UserContext to `tenancy.schemas.auth`~~ — Pending (separate task)
|
|
2. **Add service methods to tenancy** — **DONE** (2026-02-27)
|
|
|
|
### Phase 2: High-Impact Migrations — DONE (2026-02-27)
|
|
3. **Cat 1 - billing→tenancy**: 13 violations — **DONE**
|
|
4. **Cat 1 - loyalty→tenancy**: 10 violations — **DONE**
|
|
5. **Cat 1 - marketplace→tenancy/catalog/orders**: 10 violations — **DONE**
|
|
6. **Cat 1 - core→tenancy**: 3 violations — **DONE**
|
|
7. **Cat 1 - analytics→tenancy/catalog**: 4 violations — **DONE**
|
|
|
|
### Phase 3: Remaining Migrations — DONE (2026-02-27)
|
|
8. **Cat 2**: Model creation violations — **DONE** (deferred imports in method bodies)
|
|
9. **Cat 3**: All aggregation queries — **DONE** (service calls + pre-query ID filtering)
|
|
10. **Cat 4**: All join queries — **DONE** (joinedload + service calls)
|
|
11. **Cat 1**: Remaining modules — **DONE** (all modules migrated)
|
|
|
|
### Phase 4: Provider Enrichment (Incremental)
|
|
12. **P5**: Add widget providers to orders, billing, catalog (highest value)
|
|
13. **P5**: Add metrics providers to loyalty, payments
|
|
14. **P5**: Add remaining widget providers as modules are touched
|
|
|
|
### Phase 5: Cleanup — DONE (2026-02-27)
|
|
15. ~~**Cat 5**: Move UserContext to `tenancy.schemas.auth` (74 files)~~ — **DONE** (commits 4aa6f76, e3a52f6)
|
|
16. ~~**P6**: Route variable naming standardization~~ — **DONE** (all route files export `router`)
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
For each migration:
|
|
1. Add/verify service method has a unit test
|
|
2. Run existing integration tests to confirm no regressions
|
|
3. Run `python scripts/validate/validate_architecture.py` to verify violation count decreases
|
|
|
|
## Related Documentation
|
|
|
|
- [Cross-Module Import Rules](cross-module-import-rules.md) — The rules being enforced
|
|
- [Architecture Violations Status](architecture-violations-status.md) — Violation tracking
|
|
- [Module System Architecture](module-system.md) — Module structure reference
|