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:
@@ -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 (
|
||||
|
||||
@@ -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...")
|
||||
|
||||
Reference in New Issue
Block a user