Chapter 26 — Building an Advanced Architecture Review Skill
ทำไมต้องชื่อนี้และเรียนไปเพื่ออะไร?
ใน Day 25 เราได้สร้าง pr_synthesizer เพื่อวิเคราะห์ Blast Radius และประกอบร่าง PR Description แต่ในกระบวนการส่งมอบระดับ Enterprise โค้ดที่ "เทสผ่าน" และ "รู้ขอบเขตผลกระทบ" ยังไม่เพียงพอ หากโค้ดนั้นละเมิดสถาปัตยกรรม (Architectural Violation), ทิ้ง Code Smells, หรือเปิดช่องโหว่ด้านความปลอดภัย (Security Vulnerabilities)
วัตถุประสงค์ของ Day 26: พัฒนา Skill Cartridge ตัวที่สามชื่อ architecture_reviewer เพื่อทำหน้าที่เป็น Automated Architecture Gatekeeper — 1) ตรวจสอบการละเมิดกฎ Clean Architecture / Layered Dependency โดยใช้ Call Graph + AST Import Analysis, 2) สแกนหา Security Antipatterns & Smells, 3) ส่งออกรายงาน Architectural Conformance Report พร้อมข้อเสนอแนะเชิงโครงสร้าง เพื่อยับยั้งการ Merge โค้ดที่สร้าง Technical Debt
โครงสร้างโปรเจกต์สำหรับ Day 26 โดยเพิ่มตลับสกิล skills/architecture_reviewer/ เข้าสู่ Skill Registry ตามมาตรฐาน Two-Stage Progressive Disclosure:
สารบัญย่อ (Stage 1) โหลดเข้า Context Window เพื่อดักจับ Intent เช่น "ตรวจ architecture", "check clean architecture", "code smells", "security audit"
กฎเกณฑ์เชิงโครงสร้าง (Stage 2) กำหนดนิยาม Dependency Rules (Domain → Service → Controller/Infra) และเกณฑ์ Severity (BLOCKER, CRITICAL, WARNING)
Logic ตรวจจับการฝ่าฝืนเลเยอร์จาก Import Statement / AST Node และตรวจจับ Regex Patterns ของความเสี่ยงด้านความปลอดภัย
Integration Test จำลองการเขียนโค้ดที่ผิดสถาปัตยกรรม (เช่น Domain Entity แอบ import Repository/DB Driver) แล้วทดสอบว่า Skill จับได้แม่นยำหรือไม่
┌────────────────────────────────────────────────────────┐
│ Target Codebase in Sandbox │
│ (Source Files & Git Diff Scope) │
└──────────────────────────┬─────────────────────────────┘
│
┌──────────────────────┴──────────────────────┐
│ │
▼ ▼
┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐
│ AST Import & Call Site Extraction │ │ Security & Smell Scanner │
│ - สกัด package, imports, type refs │ │ - Secret Leaks (AWS, JWT, Tokens) │
│ - ตรวจ Dependency Direction │ │ - Insecure DB Queries / Logging │
└──────────────────┬───────────────────┘ └──────────────────┬───────────────────┘
│ │
│ Layer Violations │ Vulnerability Findings
└──────────────────────┬──────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Architecture Conformance Engine │
│ แมป Layer Boundary: Domain -> Service -> Infra │
│ จัดกลุ่ม Severity: BLOCKER | CRITICAL | WARNING │
└──────────────────────────┬─────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Synthesis (qwen2.5-coder:7b) │
│ สรุปผลพร้อมแนวทาง Refactor (Clean Code Advice) │
└────────────────────────────────────────────────────────┘
คลิกเลือกดูซอร์สโค้ดของแต่ละไฟล์ที่ต้องสร้างขึ้นในวันนี้:
{
"name": "architecture_reviewer",
"version": "1.0.0",
"description": "Reviews codebase against Clean Architecture rules, detects layer boundary violations, circular dependencies, and security code smells.",
"triggers": [
"review architecture",
"architecture check",
"clean architecture",
"code smell",
"security audit",
"check boundaries"
],
"required_tools": [
"verify_layer_dependencies",
"scan_security_smells",
"generate_architecture_report"
]
}
# Architecture Reviewer - Enterprise Operational Guidelines
## Role & Mission
คุณทำหน้าที่เป็น Principal Software Architect และ Security Lead ทำหน้าที่บังคับใช้มาตรฐานโครงสร้างซอฟต์แวร์ (Architectural Governance) และความปลอดภัยของซอร์สโค้ด
## Layer Dependency Invariant (Clean Architecture)
กฎทิศทางการอ้างอิงที่ห้ามละเมิดโดยเด็ดขาด:
1. **Core Domain (`domain/**`)**:
- ต้องเป็น Pure Business Logic
- **ห้าม** Import หรือมี Dependency ใดๆ ไปยัง Controller, Web, Infrastructure, หรือ Database Libraries (เช่น Spring Data, JPA, JDBC) โดยตรง
2. **Application / Service Layer (`service/**`, `usecase/**`)**:
- อ้างอิง Domain ได้
- สื่อสารกับ Infrastructure ผ่าน Interface/Port เท่านั้น
3. **Infrastructure / Controller Layer (`controller/**`, `infra/**`)**:
- อยู่รอบนอกสุด สามารถ Import เลเยอร์ภายในได้ แต่เลเยอร์ภายในห้ามเรียกกลับ (Inversion of Control)
## Violation Severity Matrix
- **BLOCKER**: มีการรั่วไหลของ Secret/Credential, SQL Injection, หรือ Circular Dependency ระหว่างโมดูล
- **CRITICAL**: Layer Boundary Inversion (เช่น Domain Model อ้างอิง Infrastructure Repository โดยตรง)
- **WARNING**: God Class (>500 lines), Method parameter ล้นหลาม (>5 params), Missing interface abstractions
import re
from pathlib import Path
from typing import Dict, List, Any
# กฎทิศทางต้องห้าม (Source Layer Pattern -> Banned Target Pattern)
BOUNDARY_RULES = [
{
"source": r".*/domain/.*",
"banned": [r".*\.controller\..*", r".*\.infra\..*", r".*\.repository\..*", r"java\.sql\..*"],
"reason": "Domain entity must not depend on external layers, controllers, or database drivers."
},
{
"source": r".*/service/.*",
"banned": [r".*\.controller\..*"],
"reason": "Application services must not have upward dependencies to presentation/controller layers."
}
]
# กฎตรวจจับ Security Smells เบื้องต้น
SECURITY_PATTERNS = [
(r"(?i)(api[_-]?key|secret|password|bearer)\s*=\s*['\"][A-Za-z0-9_\\-]{8,}['\"]", "Hardcoded Credentials Detected", "BLOCKER"),
(r"SELECT\s+.*\s+FROM\s+.*\s*\+\s*[a-zA-Z0-9_]+", "Possible Dynamic SQL String Concatenation (SQLi Risk)", "BLOCKER"),
(r"System\.out\.print", "Production Code using stdout instead of Structured Logger", "WARNING")
]
def verify_layer_dependencies(files_with_imports: Dict[str, List[str]]) -> List[Dict[str, str]]:
"""
ตรวจสอบว่าแต่ละไฟล์มีการ import ฝ่าฝืน Layer Boundary หรือไม่
files_with_imports: { "src/domain/Order.java": ["com.app.infra.OrderRepository", "java.util.List"] }
"""
violations = []
for filepath, imports in files_with_imports.items():
for rule in BOUNDARY_RULES:
if re.match(rule["source"], filepath):
for imp in imports:
for banned_pattern in rule["banned"]:
if re.match(banned_pattern, imp):
violations.append({
"file": filepath,
"violating_import": imp,
"reason": rule["reason"],
"severity": "CRITICAL"
})
return violations
def scan_security_smells(file_contents: Dict[str, str]) -> List[Dict[str, str]]:
"""สแกนหาข้อบกพร่องด้านความปลอดภัยและ Code Smells ในเนื้อไฟล์"""
findings = []
for filepath, content in file_contents.items():
lines = content.splitlines()
for idx, line in enumerate(lines, 1):
for pattern, desc, severity in SECURITY_PATTERNS:
if re.search(pattern, line):
findings.append({
"file": filepath,
"line": idx,
"finding": desc,
"severity": severity,
"snippet": line.strip()
})
return findings
def generate_architecture_report(violations: List[Dict[str, str]], smells: List[Dict[str, str]]) -> str:
"""รวบรวมข้อค้นพบทั้งหมดเป็นรายงาน Markdown เชิงวิศวกรรม"""
status = "PASSED" if not violations and not any(s["severity"] == "BLOCKER" for s in smells) else "FAILED"
violation_lines = []
for v in violations:
violation_lines.append(f"- **[{v['severity']}]** `{v['file']}`: Imports forbidden `{v['violating_import']}`\n *Rationale: {v['reason']}*")
violations_block = "\n".join(violation_lines) if violation_lines else "- No layer boundary violations found."
smell_lines = []
for s in smells:
smell_lines.append(f"- **[{s['severity']}]** `{s['file']}:{s['line']}`: {s['finding']}\n `{s['snippet']}`")
smells_block = "\n".join(smell_lines) if smell_lines else "- No security risks or code smells detected."
return f"""# Architecture & Quality Conformance Report
**Gate Status**: `[{status}]`
### 1. Layer Boundary Violations
{violations_block}
### 2. Security Findings & Code Smells
{smells_block}
### 3. Recommendation & Next Steps
{"- Clean up layer inversions and remove critical smells prior to merging." if status == "FAILED" else "- Code architecture adheres to enterprise clean architecture standards. Approved."}
"""
from skills.architecture_reviewer.tools import (
verify_layer_dependencies,
scan_security_smells,
generate_architecture_report
)
def test_architecture_review_pipeline():
print("=== Testing Day 26: Advanced Architecture Review Skill ===")
# 1. จำลองโค้ดที่มีทั้งการละเมิด Layer และ Security Smells
mock_files_imports = {
"src/main/java/com/domain/Order.java": [
"java.util.UUID",
"com.app.infra.OrderRepository" # ละเมิด: Domain แอบ import Infra
],
"src/main/java/com/service/PaymentService.java": [
"com.domain.Order",
"java.util.List"
]
}
mock_file_contents = {
"src/main/java/com/domain/Order.java": """
package com.domain;
import com.app.infra.OrderRepository;
public class Order {
private String apiKey = "secret_ak_live_994829104812"; // BLOCKER: Hardcoded secret
public void save() {
System.out.println("Saving order..."); // WARNING: stdout
}
}
"""
}
# 2. ทำการตรวจสอบ Boundary และ Code Smells
violations = verify_layer_dependencies(mock_files_imports)
smells = scan_security_smells(mock_file_contents)
# 3. ตรวจสอบ Assertions
assert len(violations) == 1
assert violations[0]["severity"] == "CRITICAL"
assert "OrderRepository" in violations[0]["violating_import"]
blocker_smells = [s for s in smells if s["severity"] == "BLOCKER"]
assert len(blocker_smells) == 1
assert "Hardcoded Credentials" in blocker_smells[0]["finding"]
# 4. สังเคราะห์รายงาน Architecture Conformance
report = generate_architecture_report(violations, smells)
print("\n--- Architecture Conformance Report Preview ---")
print(report)
assert "[FAILED]" in report
print("\nVerification Successful: Architecture Review Skill correctly flagged violations!")
if __name__ == "__main__":
test_architecture_review_pipeline()
เปิด Terminal บน MacBook M3 แล้วทำตามขั้นตอนเพื่อติดตั้งและทดสอบ Architecture Reviewer:
mkdir -p skills/architecture_reviewer
- นำโค้ดใน ส่วนที่ 4 (metadata.json, instructions.md, tools.py) ไปวางภายใต้ skills/architecture_reviewer/
- นำโค้ดใน ส่วนที่ 4 (test_arch_review.py) ไปวางที่ root ของโปรเจกต์
python test_arch_review.py
ผลลัพธ์ที่คาดหวัง: ตรวจจับ com.app.infra.OrderRepository ใน Domain เป็น CRITICAL, Hardcoded secret เป็น BLOCKER, และสรุป Gate Status เป็น [FAILED]
ข้อที่ 1: ทำไมการให้ Domain Model อ้างอิง Infrastructure หรือ DB Drivers โดยตรงจึงถือเป็นข้อผิดพลาดร้ายแรง (CRITICAL)?
ข้อที่ 2: ในสถาปัตยกรรม Multi-Skill Agent ของเรา อะไรเป็นตัวป้องกันไม่ให้ architecture_reviewer และ unit_test_synthesizer ตีกันใน Context Window?
🏁 สรุปหมุดเชื่อมโยง (Chapter Wrap-up)
เนื้อหาใน Day 26 — Building an Advanced Architecture Review Skill เติมตลับสกิล architecture_reviewer เป็น Gatekeeper ก่อน Merge: จับ Layer Violation, Security Smell และสรุปเป็นรายงาน Conformance พร้อมก้าวต่อไปยัง Day 27: End-to-End Enterprise Simulation