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:
@@ -204,3 +204,53 @@ api_endpoint_rules:
|
||||
file_pattern: "app/api/v1/shop/**/*.py"
|
||||
discouraged_patterns:
|
||||
- "db.query(.*).all()"
|
||||
|
||||
- id: "API-007"
|
||||
name: "API endpoints must NOT import database models directly"
|
||||
severity: "error"
|
||||
description: |
|
||||
API endpoints must follow the layered architecture: Routes → Services → Models.
|
||||
Routes should NEVER import database models directly.
|
||||
|
||||
WHY THIS MATTERS:
|
||||
- Layered architecture: Each layer has clear responsibilities
|
||||
- Testability: Routes can be tested without database
|
||||
- Flexibility: Database schema changes don't affect route signatures
|
||||
- Type safety: Schemas define the API contract, not database structure
|
||||
|
||||
For dependency injection (e.g., current user/customer), use schemas instead
|
||||
of database models as return types.
|
||||
|
||||
WRONG:
|
||||
from models.database.customer import Customer
|
||||
from models.database.order import Order
|
||||
from app.modules.customers.models.customer import Customer
|
||||
|
||||
@router.get("/orders")
|
||||
def get_orders(customer: Customer = Depends(get_current_customer)):
|
||||
...
|
||||
|
||||
RIGHT:
|
||||
from app.modules.customers.schemas import CustomerContext
|
||||
from app.modules.orders.services import order_service
|
||||
|
||||
@router.get("/orders")
|
||||
def get_orders(customer: CustomerContext = Depends(get_current_customer)):
|
||||
return order_service.get_orders(db, customer.id, customer.vendor_id)
|
||||
|
||||
ALLOWED IMPORTS IN API ROUTES:
|
||||
- Schemas: from models.schema.* or from app.modules.*.schemas
|
||||
- Services: from app.services.* or from app.modules.*.services
|
||||
- Dependencies: from app.api.deps
|
||||
- Database session: from app.core.database import get_db
|
||||
|
||||
NOT ALLOWED:
|
||||
- from models.database.*
|
||||
- from app.modules.*.models.*
|
||||
pattern:
|
||||
file_pattern: "app/api/**/*.py"
|
||||
anti_patterns:
|
||||
- "from models\\.database\\."
|
||||
- "from app\\.modules\\.[a-z_]+\\.models\\."
|
||||
exclude_files:
|
||||
- "app/api/deps.py" # Dependencies may need model access for queries
|
||||
|
||||
Reference in New Issue
Block a user