Move letzshop-related functionality from tenancy to marketplace module: - Move admin letzshop routes to marketplace/routes/api/admin_letzshop.py - Move letzshop schemas to marketplace/schemas/letzshop.py - Remove letzshop code from tenancy module (admin_vendors, vendor_service) - Update model exports and imports Add comprehensive unit tests for vendor services: - test_company_service.py: Company management operations - test_platform_service.py: Platform management operations - test_vendor_domain_service.py: Vendor domain operations - test_vendor_team_service.py: Vendor team management Update module definitions: - billing, messaging, payments: Minor definition updates Add architecture proposals documentation: - Module dependency redesign session notes - Decouple modules implementation plan - Module decoupling proposal Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.7 KiB
8.7 KiB
Session Note: Module Dependency Redesign
Date: 2026-02-03 Status: To be continued Priority: High - Architecture blocker
Summary
We discovered that while billing, payments, and messaging have been correctly reclassified as core modules, the app would still crash if optional modules are removed due to hard imports from core → optional modules.
The fundamental issue is flawed architecture: core modules should never depend on optional modules. The dependency direction is backwards.
Work Completed Today
1. Module Reclassification
Made billing, payments, and messaging core modules:
app/modules/billing/definition.py-is_core=Trueapp/modules/payments/definition.py-is_core=Trueapp/modules/messaging/definition.py-is_core=True
2. Letzshop Export Routes Moved (T5b)
Moved export routes from tenancy to marketplace where they belong:
- Old:
GET/POST /api/v1/admin/vendors/{id}/export/letzshop - New:
GET/POST /api/v1/admin/letzshop/vendors/{id}/export
Files changed:
app/modules/marketplace/routes/api/admin_letzshop.pyapp/modules/marketplace/schemas/letzshop.pyapp/modules/tenancy/routes/api/admin_vendors.pyapp/modules/tenancy/schemas/vendor.py- Tests and documentation updated
3. Documentation Updated
docs/architecture/module-system.mddocs/proposals/decouple-modules.mddocs/testing/admin-frontend-features.mddocs/guides/letzshop-admin-management.md
Critical Issue: Core → Optional Dependencies
Current State (BROKEN)
The app crashes on startup with ImportError if optional modules are removed.
tenancy (core) → analytics (optional)
# tenancy/routes/api/admin_vendors.py (lines 20, 23)
from app.modules.analytics.services.stats_service import stats_service # TOP-LEVEL
from app.modules.analytics.schemas import VendorStatsResponse # TOP-LEVEL
tenancy (core) → marketplace (optional)
# tenancy/models/__init__.py (line 22)
from app.modules.marketplace.models.marketplace_import_job import MarketplaceImportJob # TOP-LEVEL
# tenancy/services/vendor_service.py (lines 19, 26)
from app.modules.marketplace.exceptions import MarketplaceProductNotFoundException
from app.modules.marketplace.models import MarketplaceProduct
tenancy (core) → catalog (optional)
# tenancy/services/vendor_service.py (lines 18, 27, 30)
from app.modules.catalog.exceptions import ProductAlreadyExistsException
from app.modules.catalog.models import Product
from app.modules.catalog.schemas import ProductCreate
billing (core) → catalog (optional)
# billing/services/subscription_service.py (line 48)
from app.modules.catalog.models import Product
# billing/services/admin_subscription_service.py (line 30)
from app.modules.catalog.models import Product
# billing/services/capacity_forecast_service.py (line 19)
from app.modules.catalog.models import Product
The Design Flaw
Current (Wrong)
Core Modules ──imports──> Optional Modules
↓
CRASH if optional removed
Correct Design
Optional Modules ──extends/provides to──> Core Modules
↓
Graceful degradation if optional removed
Root Cause Analysis
| Violation | Why It's Wrong | What Should Happen |
|---|---|---|
tenancy imports stats_service |
Core shouldn't know analytics exists | Analytics registers a MetricsProvider; core discovers it |
tenancy imports MarketplaceImportJob |
Core shouldn't know marketplace exists | Marketplace owns its relationships entirely |
| tenancy's vendor_service creates products | Product creation is catalog's domain | Move this code to catalog module |
billing imports Product to count |
Billing shouldn't query catalog tables | Catalog provides count via ProductCountProvider protocol |
Proposed Solution: Provider Pattern
Architecture
┌─────────────────────────────────────────────────────────┐
│ CORE MODULES │
│ (Define protocols/hooks, never import from optional) │
│ │
│ contracts: Define protocols (MetricsProvider, etc.) │
│ core: Discover and aggregate providers │
│ tenancy: Vendor management (no product knowledge) │
│ billing: Tier limits (ask "count?" via protocol) │
└─────────────────────────────────────────────────────────┘
▲
│ implements/provides
│
┌─────────────────────────────────────────────────────────┐
│ OPTIONAL MODULES │
│ (Extend core by implementing protocols/hooks) │
│ │
│ catalog: Implements ProductCountProvider │
│ analytics: Implements MetricsProvider │
│ marketplace: Owns import jobs, provides to tenancy │
└─────────────────────────────────────────────────────────┘
Example: Billing Needs Product Count
Current (wrong):
# billing/services/subscription_service.py
from app.modules.catalog.models import Product # CRASH if catalog removed
def get_product_count(vendor_id):
return db.query(Product).filter(Product.vendor_id == vendor_id).count()
Proposed (correct):
# contracts/capacity.py
class ProductCountProvider(Protocol):
def get_product_count(self, db: Session, vendor_id: int) -> int: ...
# catalog/services/product_count_provider.py
class CatalogProductCountProvider:
def get_product_count(self, db, vendor_id):
return db.query(Product).filter(...).count()
# Register in catalog/definition.py
catalog_module = ModuleDefinition(
product_count_provider=_get_product_count_provider,
...
)
# billing/services/subscription_service.py
def get_product_count(db, vendor_id, platform_id):
provider = get_product_count_provider(db, platform_id) # Discovers from enabled modules
if provider:
return provider.get_product_count(db, vendor_id)
return 0 # Graceful fallback
Questions to Resolve Tomorrow
-
What belongs where?
- Does product creation in
tenancy/vendor_service.pybelong in catalog? - Should
MarketplaceImportJobrelationships stay on User/Vendor or move entirely to marketplace?
- Does product creation in
-
Provider patterns needed:
MetricsProvider(already proposed) - for dashboard statsProductCountProvider- for billing tier limitsImportJobProvider- for marketplace import jobs?
-
Migration strategy:
- Move code first, or create protocols first?
- How to handle the User/Vendor ↔ MarketplaceImportJob relationship?
-
Testing:
- How to verify the app runs with optional modules removed?
- Integration test that imports only core modules?
Module Classification Reference
Core Modules (8)
| Module | Purpose |
|---|---|
| contracts | Protocol definitions |
| core | Dashboard, settings |
| tenancy | Platform, company, vendor, user management |
| cms | Content pages, media, themes |
| customers | Customer database |
| billing | Subscriptions, tier limits |
| payments | Payment gateways |
| messaging | Email, notifications |
Optional Modules (8)
| Module | Purpose |
|---|---|
| analytics | Reports, dashboards |
| cart | Shopping cart |
| catalog | Product catalog |
| checkout | Order placement |
| inventory | Stock management |
| loyalty | Loyalty programs |
| marketplace | Letzshop integration |
| orders | Order management |
Internal Modules (2)
| Module | Purpose |
|---|---|
| dev-tools | Component library |
| monitoring | Logs, tasks |
Files to Review Tomorrow
app/modules/tenancy/services/vendor_service.py- Product creation codeapp/modules/tenancy/models/__init__.py- MarketplaceImportJob importapp/modules/billing/services/subscription_service.py- Product count queriesapp/modules/contracts/- Existing protocols to extend
Next Steps
- Design the provider protocols needed
- Decide what code moves where
- Implement the provider pattern for one case (e.g., ProductCountProvider)
- Test that core modules can load without optional modules
- Apply pattern to remaining violations