docs: add comprehensive architecture rules reference documentation
Created detailed documentation for all 51 architectural rules: New Documentation: - docs/development/architecture-rules.md (comprehensive reference) - Added to mkdocs.yml navigation Documentation Includes: - Overview and usage instructions - Severity level explanations - All 51 rules organized by category: * Backend Rules (API, Service, Model, Exception) * Frontend Rules (JavaScript, Templates, Styling) * Naming Convention Rules * Security & Multi-Tenancy Rules * Code Quality Rules * Middleware Rules For Each Rule: - Rule ID and name - Severity level - Detailed description - Good and bad code examples - Anti-patterns detected - File patterns affected Additional Sections: - Quick reference tables - Pre-commit checklist - Common violations and fixes - Ignored patterns explanation - Summary statistics (51 rules, 35 errors, 16 warnings) This documentation complements: - .architecture-rules.yaml (rule definitions) - scripts/validate_architecture.py (enforcement) - Code Quality guide - Contributing guide Documentation builds successfully with mkdocs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
796
docs/development/architecture-rules.md
Normal file
796
docs/development/architecture-rules.md
Normal file
@@ -0,0 +1,796 @@
|
||||
# Architecture Rules Reference
|
||||
|
||||
This document provides a comprehensive reference for all architectural rules enforced by the `scripts/validate_architecture.py` validator.
|
||||
|
||||
## Overview
|
||||
|
||||
The architecture validator ensures consistent patterns, separation of concerns, and best practices across the entire codebase. Rules are organized by category and severity level.
|
||||
|
||||
**Version:** 2.0
|
||||
**Total Rules:** 50+
|
||||
**Configuration File:** `.architecture-rules.yaml`
|
||||
|
||||
## Running the Validator
|
||||
|
||||
```bash
|
||||
# Check all files
|
||||
python scripts/validate_architecture.py
|
||||
|
||||
# Check specific directory
|
||||
python scripts/validate_architecture.py app/api/
|
||||
|
||||
# Verbose output
|
||||
python scripts/validate_architecture.py --verbose
|
||||
|
||||
# Auto-fix where possible
|
||||
python scripts/validate_architecture.py --fix
|
||||
```
|
||||
|
||||
## Severity Levels
|
||||
|
||||
| Severity | Description | Exit Code | Action Required |
|
||||
|----------|-------------|-----------|-----------------|
|
||||
| **Error** | Critical architectural violation | 1 | Must fix before committing |
|
||||
| **Warning** | Pattern deviation | 0 | Should fix, doesn't block |
|
||||
| **Info** | Suggestion for improvement | 0 | Optional improvement |
|
||||
|
||||
---
|
||||
|
||||
## Core Architectural Principles
|
||||
|
||||
1. **Separation of Concerns** - API endpoints handle HTTP, services handle business logic
|
||||
2. **Layered Architecture** - Routes → Services → Models
|
||||
3. **Type Safety** - Pydantic for API, SQLAlchemy for database
|
||||
4. **Proper Exception Handling** - Domain exceptions in services, HTTPException in routes
|
||||
5. **Multi-Tenancy** - All queries scoped to vendor_id
|
||||
6. **Consistent Naming** - API files: plural, Services: singular+service, Models: singular
|
||||
|
||||
---
|
||||
|
||||
## Backend Rules
|
||||
|
||||
### API Endpoint Rules (app/api/v1/**/*.py)
|
||||
|
||||
#### API-001: Use Pydantic Models for Request/Response
|
||||
**Severity:** Error
|
||||
|
||||
All API endpoints must use Pydantic models (BaseModel) for request bodies and response models. Never use raw dicts or SQLAlchemy models directly.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
@router.post("/vendors", response_model=VendorResponse)
|
||||
async def create_vendor(vendor: VendorCreate):
|
||||
return vendor_service.create_vendor(db, vendor)
|
||||
|
||||
# ❌ Bad
|
||||
@router.post("/vendors")
|
||||
async def create_vendor(data: dict):
|
||||
return {"name": data["name"]} # No validation!
|
||||
```
|
||||
|
||||
#### API-002: No Business Logic in Endpoints
|
||||
**Severity:** Error
|
||||
|
||||
API endpoints should only handle HTTP concerns (validation, auth, response formatting). All business logic must be delegated to service layer.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
@router.post("/vendors")
|
||||
async def create_vendor(vendor: VendorCreate, db: Session = Depends(get_db)):
|
||||
result = vendor_service.create_vendor(db, vendor)
|
||||
return result
|
||||
|
||||
# ❌ Bad
|
||||
@router.post("/vendors")
|
||||
async def create_vendor(vendor: VendorCreate, db: Session = Depends(get_db)):
|
||||
db_vendor = Vendor(name=vendor.name)
|
||||
db.add(db_vendor) # Business logic in endpoint!
|
||||
db.commit()
|
||||
return db_vendor
|
||||
```
|
||||
|
||||
**Anti-patterns detected:**
|
||||
- `db.add(`
|
||||
- `db.commit()`
|
||||
- `db.query(`
|
||||
|
||||
#### API-003: Catch Service Exceptions
|
||||
**Severity:** Error
|
||||
|
||||
API endpoints must catch domain exceptions from services and convert them to appropriate HTTPException with proper status codes.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
@router.post("/vendors")
|
||||
async def create_vendor(vendor: VendorCreate, db: Session = Depends(get_db)):
|
||||
try:
|
||||
result = vendor_service.create_vendor(db, vendor)
|
||||
return result
|
||||
except VendorAlreadyExistsError as e:
|
||||
raise HTTPException(status_code=409, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error: {e}")
|
||||
raise HTTPException(status_code=500, detail="Internal server error")
|
||||
```
|
||||
|
||||
#### API-004: Proper Authentication
|
||||
**Severity:** Warning
|
||||
|
||||
Protected endpoints must use Depends() for authentication.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
@router.post("/vendors")
|
||||
async def create_vendor(
|
||||
vendor: VendorCreate,
|
||||
current_user: User = Depends(get_current_admin),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
#### API-005: Multi-Tenant Scoping
|
||||
**Severity:** Error
|
||||
|
||||
All queries in vendor/shop contexts must filter by vendor_id.
|
||||
|
||||
---
|
||||
|
||||
### Service Layer Rules (app/services/**/*.py)
|
||||
|
||||
#### SVC-001: No HTTPException in Services
|
||||
**Severity:** Error
|
||||
|
||||
Services are business logic layer - they should NOT know about HTTP. Raise domain-specific exceptions instead.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
class VendorService:
|
||||
def create_vendor(self, db: Session, vendor_data):
|
||||
if self._vendor_exists(db, vendor_data.subdomain):
|
||||
raise VendorAlreadyExistsError(f"Vendor {vendor_data.subdomain} exists")
|
||||
|
||||
# ❌ Bad
|
||||
class VendorService:
|
||||
def create_vendor(self, db: Session, vendor_data):
|
||||
if self._vendor_exists(db, vendor_data.subdomain):
|
||||
raise HTTPException(status_code=409, detail="Vendor exists") # BAD!
|
||||
```
|
||||
|
||||
#### SVC-002: Use Proper Exception Handling
|
||||
**Severity:** Error
|
||||
|
||||
Services should raise meaningful domain exceptions, not generic Exception.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
class VendorAlreadyExistsError(Exception):
|
||||
pass
|
||||
|
||||
def create_vendor(self, db: Session, vendor_data):
|
||||
if self._vendor_exists(db, vendor_data.subdomain):
|
||||
raise VendorAlreadyExistsError(f"Subdomain {vendor_data.subdomain} taken")
|
||||
|
||||
# ❌ Bad
|
||||
def create_vendor(self, db: Session, vendor_data):
|
||||
if self._vendor_exists(db, vendor_data.subdomain):
|
||||
raise Exception("Vendor exists") # Too generic!
|
||||
```
|
||||
|
||||
#### SVC-003: Accept DB Session as Parameter
|
||||
**Severity:** Error
|
||||
|
||||
Service methods should receive database session as a parameter for testability and transaction control.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
def create_vendor(self, db: Session, vendor_data: VendorCreate):
|
||||
vendor = Vendor(**vendor_data.dict())
|
||||
db.add(vendor)
|
||||
db.commit()
|
||||
return vendor
|
||||
|
||||
# ❌ Bad
|
||||
def create_vendor(self, vendor_data: VendorCreate):
|
||||
db = SessionLocal() # Don't create session inside!
|
||||
vendor = Vendor(**vendor_data.dict())
|
||||
db.add(vendor)
|
||||
db.commit()
|
||||
```
|
||||
|
||||
#### SVC-004: Use Pydantic Models for Input
|
||||
**Severity:** Warning
|
||||
|
||||
Service methods should accept Pydantic models for complex inputs to ensure type safety.
|
||||
|
||||
#### SVC-005: Scope Queries to vendor_id
|
||||
**Severity:** Error
|
||||
|
||||
All database queries must be scoped to vendor_id to prevent cross-tenant data access.
|
||||
|
||||
---
|
||||
|
||||
### Model Rules (models/**/*.py)
|
||||
|
||||
#### MDL-001: Database Models Use SQLAlchemy Base
|
||||
**Severity:** Error
|
||||
|
||||
All database models must inherit from SQLAlchemy Base.
|
||||
|
||||
#### MDL-002: Never Mix SQLAlchemy and Pydantic
|
||||
**Severity:** Error
|
||||
|
||||
```python
|
||||
# ❌ Bad
|
||||
class Product(Base, BaseModel): # NEVER DO THIS!
|
||||
pass
|
||||
```
|
||||
|
||||
Keep them separate:
|
||||
- `models/database/product.py` - SQLAlchemy models
|
||||
- `models/schema/product.py` - Pydantic models
|
||||
|
||||
#### MDL-003: Use from_attributes in Pydantic
|
||||
**Severity:** Error
|
||||
|
||||
Pydantic response models must enable `from_attributes` to work with SQLAlchemy models.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
class ProductResponse(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
```
|
||||
|
||||
#### MDL-004: Use Singular Table Names
|
||||
**Severity:** Warning
|
||||
|
||||
Database table names should be singular lowercase (e.g., 'vendor' not 'vendors').
|
||||
|
||||
---
|
||||
|
||||
### Exception Handling Rules
|
||||
|
||||
#### EXC-001: Define Custom Exceptions
|
||||
**Severity:** Warning
|
||||
|
||||
Create domain-specific exceptions in `app/exceptions/` for better error handling.
|
||||
|
||||
```python
|
||||
# ✅ Good - app/exceptions/vendor.py
|
||||
class VendorError(Exception):
|
||||
"""Base exception for vendor-related errors"""
|
||||
pass
|
||||
|
||||
class VendorNotFoundError(VendorError):
|
||||
pass
|
||||
|
||||
class VendorAlreadyExistsError(VendorError):
|
||||
pass
|
||||
```
|
||||
|
||||
#### EXC-002: Never Use Bare Except
|
||||
**Severity:** Error
|
||||
|
||||
```python
|
||||
# ❌ Bad
|
||||
try:
|
||||
result = service.do_something()
|
||||
except: # Too broad!
|
||||
pass
|
||||
|
||||
# ✅ Good
|
||||
try:
|
||||
result = service.do_something()
|
||||
except ValueError as e:
|
||||
logger.error(f"Validation error: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error: {e}")
|
||||
```
|
||||
|
||||
#### EXC-003: Log Exceptions with Context
|
||||
**Severity:** Warning
|
||||
|
||||
When catching exceptions, log them with context and stack trace.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
try:
|
||||
result = service.process()
|
||||
except Exception as e:
|
||||
logger.error(f"Processing failed: {e}", exc_info=True)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Naming Convention Rules
|
||||
|
||||
#### NAM-001: API Files Use PLURAL Names
|
||||
**Severity:** Error
|
||||
|
||||
```
|
||||
✅ app/api/v1/admin/vendors.py
|
||||
✅ app/api/v1/admin/products.py
|
||||
❌ app/api/v1/admin/vendor.py
|
||||
```
|
||||
|
||||
Exceptions: `__init__.py`, `auth.py`, `health.py`
|
||||
|
||||
#### NAM-002: Service Files Use SINGULAR + service
|
||||
**Severity:** Error
|
||||
|
||||
```
|
||||
✅ app/services/vendor_service.py
|
||||
✅ app/services/product_service.py
|
||||
❌ app/services/vendors_service.py
|
||||
```
|
||||
|
||||
#### NAM-003: Model Files Use SINGULAR Names
|
||||
**Severity:** Error
|
||||
|
||||
```
|
||||
✅ models/database/product.py
|
||||
✅ models/schema/vendor.py
|
||||
❌ models/database/products.py
|
||||
```
|
||||
|
||||
#### NAM-004: Use 'vendor' not 'shop'
|
||||
**Severity:** Warning
|
||||
|
||||
Use consistent terminology: 'vendor' for shop owners, 'shop' only for customer-facing frontend.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
vendor_id
|
||||
vendor_service
|
||||
|
||||
# ❌ Bad
|
||||
shop_id
|
||||
shop_service
|
||||
```
|
||||
|
||||
#### NAM-005: Use 'inventory' not 'stock'
|
||||
**Severity:** Warning
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
inventory_service
|
||||
inventory_level
|
||||
|
||||
# ❌ Bad
|
||||
stock_service
|
||||
stock_level
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend Rules
|
||||
|
||||
### JavaScript Rules (static/**/js/**/*.js)
|
||||
|
||||
#### JS-001: Use Centralized Logger
|
||||
**Severity:** Error
|
||||
|
||||
Never use console.log, console.error, console.warn directly. Use window.LogConfig.createLogger().
|
||||
|
||||
```javascript
|
||||
// ✅ Good
|
||||
const pageLog = window.LogConfig.createLogger('dashboard');
|
||||
pageLog.info('Dashboard loaded');
|
||||
pageLog.error('Failed to load data', error);
|
||||
|
||||
// ❌ Bad
|
||||
console.log('Dashboard loaded');
|
||||
console.error('Failed to load data', error);
|
||||
```
|
||||
|
||||
Exceptions: Bootstrap messages with `console.log('✅'` allowed.
|
||||
|
||||
#### JS-002: Use Lowercase apiClient
|
||||
**Severity:** Error
|
||||
|
||||
```javascript
|
||||
// ✅ Good
|
||||
await apiClient.get('/api/v1/vendors');
|
||||
await apiClient.post('/api/v1/products', data);
|
||||
|
||||
// ❌ Bad
|
||||
await ApiClient.get('/api/v1/vendors');
|
||||
await API_CLIENT.post('/api/v1/products', data);
|
||||
```
|
||||
|
||||
#### JS-003: Alpine Components Must Inherit ...data()
|
||||
**Severity:** Error
|
||||
|
||||
All Alpine.js components must inherit base layout data using spread operator.
|
||||
|
||||
```javascript
|
||||
// ✅ Good
|
||||
function adminDashboard() {
|
||||
return {
|
||||
...data(), // Inherit base layout data
|
||||
currentPage: 'dashboard',
|
||||
// ... component-specific data
|
||||
};
|
||||
}
|
||||
|
||||
// ❌ Bad
|
||||
function adminDashboard() {
|
||||
return {
|
||||
currentPage: 'dashboard',
|
||||
// Missing ...data()!
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### JS-004: Set currentPage in Components
|
||||
**Severity:** Error
|
||||
|
||||
All Alpine.js page components must set a currentPage identifier.
|
||||
|
||||
```javascript
|
||||
// ✅ Good
|
||||
return {
|
||||
...data(),
|
||||
currentPage: 'dashboard', // Required!
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
#### JS-005: Initialization Guards
|
||||
**Severity:** Error
|
||||
|
||||
Init methods should prevent duplicate initialization.
|
||||
|
||||
```javascript
|
||||
// ✅ Good
|
||||
init() {
|
||||
if (window._pageInitialized) return;
|
||||
window._pageInitialized = true;
|
||||
|
||||
// Initialization logic...
|
||||
}
|
||||
```
|
||||
|
||||
#### JS-006: Error Handling for Async Operations
|
||||
**Severity:** Error
|
||||
|
||||
All API calls and async operations must have error handling.
|
||||
|
||||
```javascript
|
||||
// ✅ Good
|
||||
async loadData() {
|
||||
try {
|
||||
const response = await apiClient.get('/api/v1/data');
|
||||
this.items = response.data;
|
||||
} catch (error) {
|
||||
window.LogConfig.logError(error, 'Load Data');
|
||||
Utils.showToast('Failed to load data', 'error');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### JS-007: Set Loading State
|
||||
**Severity:** Warning
|
||||
|
||||
Loading state should be set before and cleared after async operations.
|
||||
|
||||
```javascript
|
||||
// ✅ Good
|
||||
async loadData() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await apiClient.get('/api/v1/data');
|
||||
this.items = response.data;
|
||||
} catch (error) {
|
||||
// Error handling
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Template Rules (app/templates/**/*.html)
|
||||
|
||||
#### TPL-001: Admin Templates Extend admin/base.html
|
||||
**Severity:** Error
|
||||
|
||||
```jinja
|
||||
✅ {% extends "admin/base.html" %}
|
||||
❌ No extends directive
|
||||
```
|
||||
|
||||
Exceptions: `base.html` itself, files in `partials/`
|
||||
|
||||
#### TPL-002: Vendor Templates Extend vendor/base.html
|
||||
**Severity:** Error
|
||||
|
||||
```jinja
|
||||
✅ {% extends "vendor/base.html" %}
|
||||
```
|
||||
|
||||
#### TPL-003: Shop Templates Extend shop/base.html
|
||||
**Severity:** Error
|
||||
|
||||
```jinja
|
||||
✅ {% extends "shop/base.html" %}
|
||||
```
|
||||
|
||||
#### TPL-004: Use x-text for Dynamic Content
|
||||
**Severity:** Warning
|
||||
|
||||
Use x-text directive for dynamic content to prevent XSS vulnerabilities.
|
||||
|
||||
```html
|
||||
✅ <p x-text="item.name"></p>
|
||||
❌ <p>{{ item.name }}</p>
|
||||
```
|
||||
|
||||
#### TPL-005: Use x-html ONLY for Safe Content
|
||||
**Severity:** Error
|
||||
|
||||
Use x-html only for trusted content like icons, never for user-generated content.
|
||||
|
||||
```html
|
||||
✅ <span x-html="$icon('home', 'w-5 h-5')"></span>
|
||||
❌ <div x-html="userComment"></div> <!-- XSS vulnerability! -->
|
||||
```
|
||||
|
||||
#### TPL-006: Implement Loading State
|
||||
**Severity:** Warning
|
||||
|
||||
All templates that load data should show loading state.
|
||||
|
||||
```html
|
||||
✅ <div x-show="loading">Loading...</div>
|
||||
```
|
||||
|
||||
#### TPL-007: Implement Empty State
|
||||
**Severity:** Warning
|
||||
|
||||
Show empty state when lists have no items.
|
||||
|
||||
```html
|
||||
✅ <template x-if="items.length === 0">
|
||||
<p>No items found</p>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Styling Rules (app/templates/**/*.html)
|
||||
|
||||
#### CSS-001: Use Tailwind Utility Classes
|
||||
**Severity:** Warning
|
||||
|
||||
Prefer Tailwind utility classes over custom CSS.
|
||||
|
||||
#### CSS-002: Support Dark Mode
|
||||
**Severity:** Warning
|
||||
|
||||
All color classes should include dark mode variants.
|
||||
|
||||
```html
|
||||
✅ class="bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
||||
❌ class="bg-white text-gray-900"
|
||||
```
|
||||
|
||||
#### CSS-003: Shop Templates Use CSS Variables
|
||||
**Severity:** Error
|
||||
|
||||
Shop templates must use CSS variables for vendor-specific theming.
|
||||
|
||||
```html
|
||||
✅ <button style="background-color: var(--color-primary)">Buy Now</button>
|
||||
❌ <button class="bg-blue-600">Buy Now</button>
|
||||
```
|
||||
|
||||
#### CSS-004: Mobile-First Responsive Design
|
||||
**Severity:** Warning
|
||||
|
||||
Use mobile-first responsive classes.
|
||||
|
||||
```html
|
||||
✅ class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4"
|
||||
❌ class="grid grid-cols-4"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security & Multi-Tenancy Rules
|
||||
|
||||
### Multi-Tenancy Rules
|
||||
|
||||
#### MT-001: Scope All Queries to vendor_id
|
||||
**Severity:** Error
|
||||
|
||||
In vendor/shop contexts, all database queries must filter by vendor_id.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
def get_products(self, db: Session, vendor_id: int):
|
||||
return db.query(Product).filter(Product.vendor_id == vendor_id).all()
|
||||
|
||||
# ❌ Bad
|
||||
def get_products(self, db: Session):
|
||||
return db.query(Product).all() # Cross-tenant data leak!
|
||||
```
|
||||
|
||||
#### MT-002: No Cross-Vendor Data Access
|
||||
**Severity:** Error
|
||||
|
||||
Queries must never access data from other vendors.
|
||||
|
||||
---
|
||||
|
||||
### Authentication & Authorization Rules
|
||||
|
||||
#### AUTH-001: Use JWT Tokens
|
||||
**Severity:** Error
|
||||
|
||||
Authentication must use JWT tokens in Authorization: Bearer header.
|
||||
|
||||
#### AUTH-002: Role-Based Access Control
|
||||
**Severity:** Error
|
||||
|
||||
Use Depends(get_current_admin/vendor/customer) for role checks.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
@router.get("/admin/users")
|
||||
async def list_users(current_user: User = Depends(get_current_admin)):
|
||||
pass
|
||||
```
|
||||
|
||||
#### AUTH-003: Never Store Plain Passwords
|
||||
**Severity:** Error
|
||||
|
||||
Always hash passwords with bcrypt before storing.
|
||||
|
||||
---
|
||||
|
||||
## Middleware Rules
|
||||
|
||||
#### MDW-001: Middleware Naming
|
||||
**Severity:** Warning
|
||||
|
||||
Middleware files should be named with simple nouns (auth.py, not auth_middleware.py).
|
||||
|
||||
```
|
||||
✅ middleware/auth.py
|
||||
✅ middleware/context.py
|
||||
❌ middleware/auth_middleware.py
|
||||
```
|
||||
|
||||
#### MDW-002: Vendor Context Injection
|
||||
**Severity:** Error
|
||||
|
||||
Vendor context middleware must set `request.state.vendor_id` and `request.state.vendor`.
|
||||
|
||||
---
|
||||
|
||||
## Code Quality Rules
|
||||
|
||||
#### QUAL-001: Format with Ruff
|
||||
**Severity:** Error
|
||||
|
||||
All code must be formatted with Ruff before committing.
|
||||
|
||||
```bash
|
||||
make format
|
||||
```
|
||||
|
||||
#### QUAL-002: Pass Ruff Linting
|
||||
**Severity:** Error
|
||||
|
||||
All code must pass Ruff linting before committing.
|
||||
|
||||
```bash
|
||||
make lint
|
||||
```
|
||||
|
||||
#### QUAL-003: Use Type Hints
|
||||
**Severity:** Warning
|
||||
|
||||
Add type hints to function parameters and return types.
|
||||
|
||||
```python
|
||||
# ✅ Good
|
||||
def create_product(self, db: Session, data: ProductCreate) -> Product:
|
||||
pass
|
||||
|
||||
# Better
|
||||
from typing import Optional
|
||||
|
||||
def get_product(self, product_id: int) -> Optional[Product]:
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ignored Patterns
|
||||
|
||||
The validator ignores these files/patterns:
|
||||
|
||||
- Test files: `**/*_test.py`, `**/test_*.py`
|
||||
- Cache: `**/__pycache__/**`
|
||||
- Migrations: `**/alembic/versions/**`
|
||||
- Dependencies: `**/node_modules/**`, `**/.venv/**`, `**/venv/**`
|
||||
- Build artifacts: `**/build/**`, `**/dist/**`
|
||||
|
||||
Special exceptions:
|
||||
- `app/core/exceptions.py` - Allowed to use HTTPException
|
||||
- `app/exceptions/handler.py` - Converts to HTTPException
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Pre-Commit Checklist
|
||||
|
||||
Before committing code:
|
||||
|
||||
- [ ] Run `make format` (Ruff formatting)
|
||||
- [ ] Run `make lint` (Ruff + mypy)
|
||||
- [ ] Run `python scripts/validate_architecture.py`
|
||||
- [ ] Fix all **Error** level violations
|
||||
- [ ] Review **Warning** level violations
|
||||
- [ ] Run relevant tests
|
||||
|
||||
### Common Violations and Fixes
|
||||
|
||||
| Violation | Quick Fix |
|
||||
|-----------|-----------|
|
||||
| HTTPException in service | Create custom exception in `app/exceptions/` |
|
||||
| Business logic in endpoint | Move to service layer |
|
||||
| No exception handling | Add try/except, convert to HTTPException |
|
||||
| console.log in JS | Use `window.LogConfig.createLogger()` |
|
||||
| Missing ...data() | Add spread operator in component return |
|
||||
| Bare except clause | Specify exception type |
|
||||
| Raw dict return | Create Pydantic response model |
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
All rules are defined in `.architecture-rules.yaml`. To modify rules:
|
||||
|
||||
1. Edit `.architecture-rules.yaml`
|
||||
2. Update `scripts/validate_architecture.py` if implementing new checks
|
||||
3. Run validator to test changes
|
||||
4. Update this documentation
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Code Quality Guide](code-quality.md)
|
||||
- [Contributing Guide](contributing.md)
|
||||
- [Architecture Overview](../architecture/overview.md)
|
||||
- [Backend Development](../backend/overview.md)
|
||||
- [Frontend Development](../frontend/overview.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary Statistics
|
||||
|
||||
| Category | Rules | Errors | Warnings |
|
||||
|----------|-------|--------|----------|
|
||||
| Backend | 20 | 15 | 5 |
|
||||
| Frontend JS | 7 | 6 | 1 |
|
||||
| Frontend Templates | 7 | 3 | 4 |
|
||||
| Frontend Styling | 4 | 1 | 3 |
|
||||
| Naming | 5 | 3 | 2 |
|
||||
| Security | 5 | 5 | 0 |
|
||||
| Quality | 3 | 2 | 1 |
|
||||
| **Total** | **51** | **35** | **16** |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-11-28
|
||||
**Version:** 2.0
|
||||
Reference in New Issue
Block a user