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:
2026-01-29 22:23:00 +01:00
parent de83875d0a
commit 228163d920
19 changed files with 466 additions and 16 deletions

View File

@@ -54,7 +54,7 @@ from middleware.auth import AuthManager
from app.modules.cms.models import ContentPage
from models.database.admin import PlatformAlert
from models.database.company import Company
from models.database.customer import Customer, CustomerAddress
from app.modules.customers.models.customer import Customer, CustomerAddress
from models.database.marketplace_import_job import MarketplaceImportJob
from models.database.marketplace_product import MarketplaceProduct
from models.database.marketplace_product_translation import (

View File

@@ -1771,6 +1771,9 @@ class ArchitectureValidator:
# API-005: Check vendor_id scoping for vendor/shop endpoints
self._check_vendor_scoping(file_path, content, lines)
# API-007: Check for direct model imports
self._check_no_model_imports(file_path, content, lines)
def _check_pydantic_usage(self, file_path: Path, content: str, lines: list[str]):
"""API-001: Ensure endpoints use Pydantic models"""
rule = self._get_rule("API-001")
@@ -2061,6 +2064,59 @@ class ArchitectureValidator:
)
return # Only report once per file
def _check_no_model_imports(
self, file_path: Path, content: str, lines: list[str]
):
"""API-007: Check that API endpoints do NOT import database models directly.
Routes should follow layered architecture: Routes → Services → Models.
Database models should only be imported in services, not in API routes.
Allowed: schemas, services, deps, database session
Not allowed: models.database.*, app.modules.*.models.*
"""
rule = self._get_rule("API-007")
if not rule:
return
# Skip deps.py - it may need model access for queries
file_path_str = str(file_path)
if file_path_str.endswith("deps.py"):
return
# Check for noqa
if "noqa: api-007" in content.lower():
return
# Patterns that indicate direct model imports
model_import_patterns = [
(r"from models\.database\.", "Importing from legacy models.database location"),
(r"from app\.modules\.[a-z_]+\.models\.", "Importing from module models"),
]
for i, line in enumerate(lines, 1):
# Skip comments
stripped = line.strip()
if stripped.startswith("#"):
continue
# Check for noqa on this specific line
if "noqa: api-007" in line.lower():
continue
for pattern, message in model_import_patterns:
if re.search(pattern, line):
self._add_violation(
rule_id="API-007",
rule_name="API endpoints must NOT import database models directly",
severity=Severity.ERROR,
file_path=file_path,
line_number=i,
message=message,
context=line.strip()[:80],
suggestion="Import from services or schemas instead. Use CustomerContext schema for dependency injection.",
)
def _validate_service_layer(self, target_path: Path):
"""Validate service layer rules (SVC-001 to SVC-007)"""
print("🔧 Validating service layer...")