feat(arch): add API-007 rule to enforce layered architecture
Add architecture rule that detects when API routes import database models directly, enforcing Routes → Services → Models pattern. Changes: - Add API-007 rule to .architecture-rules/api.yaml - Add _check_no_model_imports() validation to validator script - Update customer imports to use canonical module location - Add storefront module restructure implementation plan The validator now detects 81 violations across 67 API files where database models are imported directly instead of going through services. This is Phase 1 of the storefront restructure plan. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
344
docs/proposals/PLAN_storefront-module-restructure.md
Normal file
344
docs/proposals/PLAN_storefront-module-restructure.md
Normal file
@@ -0,0 +1,344 @@
|
||||
# Implementation Plan: Storefront Module Restructure
|
||||
|
||||
## Overview
|
||||
|
||||
Restructure the platform to properly separate **core platform concerns** (customer auth/identity) from **e-commerce concerns** (cart, checkout, catalog), and rename "shop" to "storefront" throughout.
|
||||
|
||||
## Current Issues
|
||||
|
||||
1. **API routes import database models directly** - violates layered architecture
|
||||
2. **No architecture rule enforces this** - validator doesn't catch it
|
||||
3. **"shop" naming is misleading** - not all platforms sell items
|
||||
4. **Customer auth is bundled with e-commerce** - should be core/platform concern
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Add Missing Architecture Rule
|
||||
|
||||
### Task 1.1: Add API-007 rule to prevent direct model imports
|
||||
|
||||
**File:** `.architecture-rules/api.yaml`
|
||||
|
||||
Add new rule:
|
||||
```yaml
|
||||
- id: "API-007"
|
||||
name: "API endpoints must NOT import database models directly"
|
||||
severity: "error"
|
||||
description: |
|
||||
API endpoints should import from services, not directly from database models.
|
||||
This enforces the layered architecture: Routes → Services → Models.
|
||||
|
||||
The only exception is for type hints in FastAPI dependency injection,
|
||||
which should use schemas or services that return the appropriate types.
|
||||
|
||||
WRONG:
|
||||
from models.database.customer import Customer
|
||||
from models.database.order import Order
|
||||
|
||||
RIGHT:
|
||||
from app.modules.customers.services import customer_service
|
||||
from models.schema.customer import CustomerResponse
|
||||
pattern:
|
||||
file_pattern: "app/api/**/*.py"
|
||||
anti_patterns:
|
||||
- "from models\\.database\\."
|
||||
- "from app\\.modules\\..*\\.models\\."
|
||||
```
|
||||
|
||||
### Task 1.2: Update validator to check this rule
|
||||
|
||||
**File:** `scripts/validate_architecture.py`
|
||||
|
||||
Add validation for API-007.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Rename "shop" to "storefront"
|
||||
|
||||
### Task 2.1: Rename API directory
|
||||
```
|
||||
app/api/v1/shop/ → app/api/v1/storefront/
|
||||
```
|
||||
|
||||
### Task 2.2: Update all imports referencing shop
|
||||
- Update router registrations in `app/api/v1/__init__.py`
|
||||
- Update any references in middleware
|
||||
- Update documentation
|
||||
|
||||
### Task 2.3: Rename routes/pages if applicable
|
||||
```
|
||||
app/routes/shop_pages.py → app/routes/storefront_pages.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Create New E-commerce Modules
|
||||
|
||||
### Task 3.1: Create `cart` module
|
||||
```
|
||||
app/modules/cart/
|
||||
├── __init__.py
|
||||
├── definition.py
|
||||
├── config.py
|
||||
├── exceptions.py
|
||||
├── models/
|
||||
│ ├── __init__.py
|
||||
│ └── cart.py # Cart, CartItem models (if persistent)
|
||||
├── schemas/
|
||||
│ ├── __init__.py
|
||||
│ └── cart.py # Move from models/schema/cart.py
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ └── cart_service.py # Move from app/services/cart_service.py
|
||||
└── routes/
|
||||
└── api/
|
||||
├── __init__.py
|
||||
└── storefront.py # Cart endpoints for customers
|
||||
```
|
||||
|
||||
### Task 3.2: Create `checkout` module
|
||||
```
|
||||
app/modules/checkout/
|
||||
├── __init__.py
|
||||
├── definition.py
|
||||
├── config.py
|
||||
├── exceptions.py
|
||||
├── schemas/
|
||||
│ ├── __init__.py
|
||||
│ └── checkout.py # CheckoutRequest, CheckoutResponse
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ └── checkout_service.py # Orchestrates cart → order conversion
|
||||
└── routes/
|
||||
└── api/
|
||||
├── __init__.py
|
||||
└── storefront.py # Place order endpoint
|
||||
```
|
||||
|
||||
### Task 3.3: Create `catalog` module
|
||||
```
|
||||
app/modules/catalog/
|
||||
├── __init__.py
|
||||
├── definition.py
|
||||
├── config.py
|
||||
├── schemas/
|
||||
│ ├── __init__.py
|
||||
│ └── catalog.py # ProductListResponse, ProductDetailResponse
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ └── catalog_service.py # Customer-facing product queries
|
||||
└── routes/
|
||||
└── api/
|
||||
├── __init__.py
|
||||
└── storefront.py # Product browsing endpoints
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Move Storefront Routes to Modules
|
||||
|
||||
### Task 4.1: Move customer auth routes to `customers` module
|
||||
|
||||
**From:** `app/api/v1/storefront/auth.py`
|
||||
**To:** `app/modules/customers/routes/api/storefront.py`
|
||||
|
||||
Endpoints:
|
||||
- `POST /auth/register` - Customer registration
|
||||
- `POST /auth/login` - Customer login
|
||||
- `POST /auth/logout` - Customer logout
|
||||
- `POST /auth/password-reset/request` - Request password reset
|
||||
- `POST /auth/password-reset/confirm` - Confirm password reset
|
||||
|
||||
### Task 4.2: Move customer profile routes to `customers` module
|
||||
|
||||
**From:** `app/api/v1/storefront/profile.py`
|
||||
**To:** `app/modules/customers/routes/api/storefront.py` (append)
|
||||
|
||||
Endpoints:
|
||||
- `GET /profile` - Get customer profile
|
||||
- `PUT /profile` - Update customer profile
|
||||
|
||||
### Task 4.3: Move customer address routes to `customers` module
|
||||
|
||||
**From:** `app/api/v1/storefront/addresses.py`
|
||||
**To:** `app/modules/customers/routes/api/storefront.py` (append)
|
||||
|
||||
Endpoints:
|
||||
- `GET /addresses` - List customer addresses
|
||||
- `POST /addresses` - Create address
|
||||
- `PUT /addresses/{id}` - Update address
|
||||
- `DELETE /addresses/{id}` - Delete address
|
||||
|
||||
### Task 4.4: Move cart routes to `cart` module
|
||||
|
||||
**From:** `app/api/v1/storefront/carts.py`
|
||||
**To:** `app/modules/cart/routes/api/storefront.py`
|
||||
|
||||
### Task 4.5: Move order placement to `checkout` module
|
||||
|
||||
**From:** `app/api/v1/storefront/orders.py` (POST /orders only)
|
||||
**To:** `app/modules/checkout/routes/api/storefront.py`
|
||||
|
||||
### Task 4.6: Move order viewing to `orders` module
|
||||
|
||||
**From:** `app/api/v1/storefront/orders.py` (GET endpoints)
|
||||
**To:** `app/modules/orders/routes/api/storefront.py`
|
||||
|
||||
### Task 4.7: Move product browsing to `catalog` module
|
||||
|
||||
**From:** `app/api/v1/storefront/products.py`
|
||||
**To:** `app/modules/catalog/routes/api/storefront.py`
|
||||
|
||||
### Task 4.8: Move messaging routes to `messaging` module
|
||||
|
||||
**From:** `app/api/v1/storefront/messages.py`
|
||||
**To:** `app/modules/messaging/routes/api/storefront.py`
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Fix Direct Model Imports
|
||||
|
||||
### Task 5.1: Update dependency injection pattern
|
||||
|
||||
**Current pattern (violates layered architecture):**
|
||||
```python
|
||||
from models.database.customer import Customer
|
||||
|
||||
@router.get("/orders")
|
||||
def get_orders(customer: Customer = Depends(get_current_customer_api)):
|
||||
...
|
||||
```
|
||||
|
||||
**New pattern (proper layered architecture):**
|
||||
```python
|
||||
from app.modules.customers.schemas import CustomerContext
|
||||
|
||||
@router.get("/orders")
|
||||
def get_orders(customer: CustomerContext = Depends(get_current_customer_api)):
|
||||
...
|
||||
```
|
||||
|
||||
### Task 5.2: Create `CustomerContext` schema
|
||||
|
||||
**File:** `app/modules/customers/schemas/context.py`
|
||||
```python
|
||||
class CustomerContext(BaseModel):
|
||||
"""Customer context for dependency injection in storefront routes."""
|
||||
id: int
|
||||
vendor_id: int
|
||||
email: str
|
||||
first_name: str | None
|
||||
last_name: str | None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
```
|
||||
|
||||
### Task 5.3: Update `get_current_customer_api` dependency
|
||||
|
||||
**File:** `app/api/deps.py`
|
||||
|
||||
Change return type from `Customer` (database model) to `CustomerContext` (schema).
|
||||
|
||||
### Task 5.4: Update all storefront routes
|
||||
|
||||
Replace all `Customer` type hints with `CustomerContext`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Delete Legacy Files
|
||||
|
||||
### Task 6.1: Delete `models/database/customer.py`
|
||||
|
||||
After all imports are updated to use `app.modules.customers.models.customer`.
|
||||
|
||||
### Task 6.2: Delete `app/api/v1/storefront/` directory
|
||||
|
||||
After all routes are moved to their respective modules.
|
||||
|
||||
### Task 6.3: Delete `app/services/cart_service.py`
|
||||
|
||||
After migrated to `app/modules/cart/services/cart_service.py`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Update Documentation
|
||||
|
||||
### Task 7.1: Update API documentation
|
||||
- Rename all "shop" references to "storefront"
|
||||
- Update endpoint paths
|
||||
|
||||
### Task 7.2: Update architecture documentation
|
||||
- Document the layered architecture rule
|
||||
- Document module structure
|
||||
|
||||
---
|
||||
|
||||
## Module Dependency Graph (Final State)
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ core │
|
||||
│ (tenancy) │
|
||||
└──────┬──────┘
|
||||
│
|
||||
┌────────────┼────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│customers │ │ payments │ │messaging │
|
||||
│ (auth) │ │ │ │ │
|
||||
└────┬─────┘ └────┬─────┘ └──────────┘
|
||||
│ │
|
||||
│ ┌────────┴────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐
|
||||
│ cart │ │ orders │
|
||||
│ │ │ │
|
||||
└────┬─────┘ └────┬─────┘
|
||||
│ │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────┐
|
||||
│ checkout │
|
||||
│ │
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution Order
|
||||
|
||||
1. **Phase 1** - Add architecture rule (enables detection)
|
||||
2. **Phase 2** - Rename shop → storefront (terminology)
|
||||
3. **Phase 3** - Create new modules (cart, checkout, catalog)
|
||||
4. **Phase 4** - Move routes to modules
|
||||
5. **Phase 5** - Fix direct model imports
|
||||
6. **Phase 6** - Delete legacy files
|
||||
7. **Phase 7** - Update documentation
|
||||
|
||||
---
|
||||
|
||||
## Files Modified Summary
|
||||
|
||||
### New Files
|
||||
- `.architecture-rules/api.yaml` (modified - add API-007)
|
||||
- `app/modules/cart/**` (new module)
|
||||
- `app/modules/checkout/**` (new module)
|
||||
- `app/modules/catalog/**` (new module)
|
||||
- `app/modules/customers/routes/api/storefront.py`
|
||||
- `app/modules/customers/schemas/context.py`
|
||||
- `app/modules/orders/routes/api/storefront.py`
|
||||
- `app/modules/messaging/routes/api/storefront.py`
|
||||
|
||||
### Deleted Files
|
||||
- `app/api/v1/shop/` (entire directory)
|
||||
- `app/routes/shop_pages.py`
|
||||
- `models/database/customer.py`
|
||||
- `app/services/cart_service.py`
|
||||
|
||||
### Modified Files
|
||||
- `app/api/deps.py` (CustomerContext return type)
|
||||
- `scripts/validate_architecture.py` (add API-007 check)
|
||||
- All files currently importing from legacy locations
|
||||
Reference in New Issue
Block a user