refactor(arch): clarify transaction control pattern (API-002, SVC-006)

API-002 updated:
- Remove db.commit() from anti-patterns (allowed at endpoint level)
- Add db.delete() to anti-patterns (business logic)
- Clarify that transaction control != business logic

SVC-006 added (new rule):
- Services should NOT call db.commit()
- Transaction control belongs at endpoint level
- Exception: log_service.py for audit log commits
- Severity: warning (to allow gradual migration)

This aligns with industry standard:
- One request = one transaction
- Services do work, endpoints control commits
- Enables composing multiple service calls in single transaction

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-03 21:54:23 +01:00
parent a33cf1e95d
commit fde55d8c2b
2 changed files with 73 additions and 4 deletions

View File

@@ -467,9 +467,12 @@ class ArchitectureValidator:
if not rule:
return
# NOTE: db.commit() is intentionally NOT included here
# Transaction control (commit) is allowed at endpoint level
# Only business logic operations are flagged
anti_patterns = [
(r"db\.add\(", "Database operations should be in service layer"),
(r"db\.commit\(\)", "Database commits should be in service layer"),
(r"db\.add\(", "Creating entities should be in service layer"),
(r"db\.delete\(", "Deleting entities should be in service layer"),
(r"db\.query\(", "Database queries should be in service layer"),
(r"db\.execute\(", "Database operations should be in service layer"),
]
@@ -565,7 +568,7 @@ class ArchitectureValidator:
)
def _validate_service_layer(self, target_path: Path):
"""Validate service layer rules (SVC-001, SVC-002, SVC-003, SVC-004)"""
"""Validate service layer rules (SVC-001, SVC-002, SVC-003, SVC-004, SVC-006)"""
print("🔧 Validating service layer...")
service_files = list(target_path.glob("app/services/**/*.py"))
@@ -587,6 +590,9 @@ class ArchitectureValidator:
# SVC-003: DB session as parameter
self._check_db_session_parameter(file_path, content, lines)
# SVC-006: No db.commit() in services
self._check_no_commit_in_services(file_path, content, lines)
def _check_no_http_exception_in_services(
self, file_path: Path, content: str, lines: list[str]
):
@@ -667,6 +673,40 @@ class ArchitectureValidator:
suggestion="Accept db: Session as method parameter instead",
)
def _check_no_commit_in_services(
self, file_path: Path, content: str, lines: list[str]
):
"""SVC-006: Services should NOT call db.commit()
Transaction control belongs at the API endpoint level.
Exception: log_service.py may need immediate commits for audit logs.
"""
rule = self._get_rule("SVC-006")
if not rule:
return
# Exception: log_service.py is allowed to commit (audit logs)
if "log_service.py" in str(file_path):
return
for i, line in enumerate(lines, 1):
if "db.commit()" in line:
# Skip if it's a comment
stripped = line.strip()
if stripped.startswith("#"):
continue
self._add_violation(
rule_id="SVC-006",
rule_name=rule["name"],
severity=Severity.WARNING,
file_path=file_path,
line_number=i,
message="Service calls db.commit() - transaction control should be at endpoint level",
context=stripped,
suggestion="Remove db.commit() from service; let endpoint handle transaction",
)
def _validate_models(self, target_path: Path):
"""Validate model rules"""
print("📦 Validating models...")