# Cross-Module Import Migration Plan **Created:** 2026-02-26 **Status:** In Progress **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 | Pending | | Cat 2 | Creating instances across module boundaries | ~15 | URGENT | Pending | | Cat 3 | Aggregation/count queries across boundaries | ~11 | URGENT | Pending | | Cat 4 | Join queries involving another module's models | ~4 | URGENT | Pending | | P5 | Provider pattern gaps (widgets, metrics) | ~8 modules | Incremental | Pending | | P6 | Route variable naming standardization | ~109 files | Low | Deferred | --- ## 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 (Deferred) **Priority:** LOW — cosmetic, no functional impact **Count:** ~109 files use `admin_router`/`store_router` instead of `router` Per MOD-010, route files should export a `router` variable. Many files use `admin_router` or `store_router` instead. The route discovery system currently handles both patterns. **Decision:** Defer to a future cleanup sprint. This is purely naming consistency and has no architectural impact. --- ## Execution Order ### Phase 1: Foundation (Do First) 1. **Cat 5**: Move UserContext to `tenancy.schemas.auth` — mechanical, enables clean imports 2. **Add service methods to tenancy** — most modules depend on tenancy, need methods first ### Phase 2: High-Impact Migrations (URGENT) 3. **Cat 1 - billing→tenancy**: 13 violations, highest count 4. **Cat 1 - loyalty→tenancy**: 10 violations 5. **Cat 1 - marketplace→tenancy/catalog/orders**: 10 violations 6. **Cat 1 - core→tenancy**: 3 violations 7. **Cat 1 - analytics→tenancy/catalog**: 4 violations ### Phase 3: Remaining Migrations (URGENT) 8. **Cat 2**: Model creation violations (3 production files) 9. **Cat 3**: All aggregation queries (11 files) 10. **Cat 4**: All join queries (4 files) 11. **Cat 1**: Remaining modules (cms, customers, inventory, messaging, monitoring) ### 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 (Deferred) 15. **P6**: Route variable naming standardization --- ## 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