refactor: move letzshop endpoints to marketplace module and add vendor service tests
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>
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
# 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=True`
|
||||
- `app/modules/payments/definition.py` - `is_core=True`
|
||||
- `app/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.py`
|
||||
- `app/modules/marketplace/schemas/letzshop.py`
|
||||
- `app/modules/tenancy/routes/api/admin_vendors.py`
|
||||
- `app/modules/tenancy/schemas/vendor.py`
|
||||
- Tests and documentation updated
|
||||
|
||||
### 3. Documentation Updated
|
||||
- `docs/architecture/module-system.md`
|
||||
- `docs/proposals/decouple-modules.md`
|
||||
- `docs/testing/admin-frontend-features.md`
|
||||
- `docs/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)
|
||||
```python
|
||||
# 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)
|
||||
```python
|
||||
# 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)
|
||||
```python
|
||||
# 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)
|
||||
```python
|
||||
# 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):**
|
||||
```python
|
||||
# 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):**
|
||||
```python
|
||||
# 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
|
||||
|
||||
1. **What belongs where?**
|
||||
- Does product creation in `tenancy/vendor_service.py` belong in catalog?
|
||||
- Should `MarketplaceImportJob` relationships stay on User/Vendor or move entirely to marketplace?
|
||||
|
||||
2. **Provider patterns needed:**
|
||||
- `MetricsProvider` (already proposed) - for dashboard stats
|
||||
- `ProductCountProvider` - for billing tier limits
|
||||
- `ImportJobProvider` - for marketplace import jobs?
|
||||
|
||||
3. **Migration strategy:**
|
||||
- Move code first, or create protocols first?
|
||||
- How to handle the User/Vendor ↔ MarketplaceImportJob relationship?
|
||||
|
||||
4. **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
|
||||
|
||||
1. `app/modules/tenancy/services/vendor_service.py` - Product creation code
|
||||
2. `app/modules/tenancy/models/__init__.py` - MarketplaceImportJob import
|
||||
3. `app/modules/billing/services/subscription_service.py` - Product count queries
|
||||
4. `app/modules/contracts/` - Existing protocols to extend
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Design the provider protocols needed
|
||||
2. Decide what code moves where
|
||||
3. Implement the provider pattern for one case (e.g., ProductCountProvider)
|
||||
4. Test that core modules can load without optional modules
|
||||
5. Apply pattern to remaining violations
|
||||
Reference in New Issue
Block a user