# Architecture Rules Configuration # This file defines the key architectural decisions and patterns that must be followed # across the application. The validator script uses these rules to check compliance. version: "2.0" project: "letzshop-product-import" description: "Comprehensive architectural rules for multi-tenant e-commerce platform" # ============================================================================ # CORE ARCHITECTURAL PRINCIPLES # ============================================================================ principles: - name: "Separation of Concerns" description: "API endpoints should only handle HTTP concerns. Business logic belongs in services." - name: "Layered Architecture" description: "Routes → Services → Models. Each layer has specific responsibilities." - name: "Type Safety" description: "Use Pydantic models for request/response validation. Use SQLAlchemy models for database." - name: "Proper Exception Handling" description: "Services throw domain exceptions. Routes catch and convert to HTTPException." - name: "Multi-Tenancy" description: "All queries must be scoped to vendor_id. No cross-vendor data access." - name: "Consistent Naming" description: "API files: plural, Services: singular+service, Models: singular" # ============================================================================ # API ENDPOINT RULES (app/api/v1/**/*.py) # ============================================================================ api_endpoint_rules: - id: "API-001" name: "Endpoint must use Pydantic models for request/response" severity: "error" description: | All API endpoints must use Pydantic models (BaseModel) for request bodies and response models. Never use raw dicts or SQLAlchemy models directly. pattern: file_pattern: "app/api/v1/**/*.py" anti_patterns: - "return dict" - "-> dict" - "return db_object" example_good: | @router.post("/vendors", response_model=VendorResponse) async def create_vendor(vendor: VendorCreate): return vendor_service.create_vendor(db, vendor) - id: "API-002" name: "Endpoint must NOT contain business logic" severity: "error" description: | API endpoints should only handle HTTP concerns (validation, auth, response formatting). All business logic must be delegated to service layer. pattern: file_pattern: "app/api/v1/**/*.py" anti_patterns: - "db.add(" - "db.commit()" - "db.query(" - id: "API-003" name: "Endpoint must catch service exceptions and convert to HTTPException" severity: "error" description: | API endpoints must catch domain exceptions from services and convert them to appropriate HTTPException with proper status codes. pattern: file_pattern: "app/api/v1/**/*.py" check: "exception_handling" - id: "API-004" name: "Endpoint must have proper authentication/authorization" severity: "warning" description: | Protected endpoints must use Depends() for authentication. Use get_current_user, get_current_admin, etc. pattern: file_pattern: "app/api/v1/**/*.py" required_if_not_public: - "Depends(get_current_" - id: "API-005" name: "Multi-tenant endpoints must scope queries to vendor_id" severity: "error" description: | All queries in vendor/shop contexts must filter by vendor_id. Use request.state.vendor_id from middleware. pattern: file_pattern: "app/api/v1/vendor/**/*.py" file_pattern: "app/api/v1/shop/**/*.py" discouraged_patterns: - "db.query(.*).all()" # Without vendor filter # ============================================================================ # SERVICE LAYER RULES (app/services/**/*.py) # ============================================================================ service_layer_rules: - id: "SVC-001" name: "Service must NOT raise HTTPException" severity: "error" description: | Services are business logic layer - they should NOT know about HTTP. Raise domain-specific exceptions instead (ValueError, custom exceptions). pattern: file_pattern: "app/services/**/*.py" anti_patterns: - "raise HTTPException" - "from fastapi import HTTPException" - id: "SVC-002" name: "Service must use proper exception handling" severity: "error" description: | Services should raise meaningful domain exceptions, not generic Exception. Create custom exception classes for business rule violations. pattern: file_pattern: "app/services/**/*.py" discouraged_patterns: - "raise Exception\\(" - id: "SVC-003" name: "Service methods must accept db session as parameter" severity: "error" description: | Service methods should receive database session as a parameter for testability and transaction control. Never create session inside service. pattern: file_pattern: "app/services/**/*.py" required_in_method_signature: - "db: Session" anti_patterns: - "SessionLocal()" - "get_db()" - id: "SVC-004" name: "Service must use Pydantic models for input validation" severity: "warning" description: | Service methods should accept Pydantic models for complex inputs to ensure type safety and validation. pattern: file_pattern: "app/services/**/*.py" encouraged_patterns: - "BaseModel" - id: "SVC-005" name: "Service must scope queries to vendor_id in multi-tenant contexts" severity: "error" description: | All database queries must be scoped to vendor_id to prevent cross-tenant data access. pattern: file_pattern: "app/services/**/*.py" check: "vendor_scoping" # ============================================================================ # MODEL RULES (models/database/*.py, models/schema/*.py) # ============================================================================ model_rules: - id: "MDL-001" name: "Database models must use SQLAlchemy Base" severity: "error" description: | All database models must inherit from SQLAlchemy Base and use proper column definitions with types and constraints. pattern: file_pattern: "models/database/**/*.py" required_patterns: - "class.*\\(Base\\):" - id: "MDL-002" name: "Use Pydantic models separately from SQLAlchemy models" severity: "error" description: | Never mix SQLAlchemy and Pydantic in the same model. SQLAlchemy = database schema, Pydantic = API validation/serialization. pattern: file_pattern: "models/**/*.py" anti_patterns: - "class.*\\(Base, BaseModel\\):" - id: "MDL-003" name: "Pydantic models must use from_attributes for ORM mode" severity: "error" description: | Pydantic response models must enable from_attributes to work with SQLAlchemy models. pattern: file_pattern: "models/schema/**/*.py" required_in_response_models: - "from_attributes = True" - id: "MDL-004" name: "Database models use singular table names" severity: "warning" description: | Database table names should be singular lowercase (e.g., 'vendor' not 'vendors'). pattern: file_pattern: "models/database/**/*.py" check: "table_naming" # ============================================================================ # EXCEPTION HANDLING RULES # ============================================================================ exception_rules: - id: "EXC-001" name: "Define custom exceptions in exceptions module" severity: "warning" description: | Create domain-specific exceptions in app/exceptions/ for better error handling and clarity. pattern: file_pattern: "app/exceptions/**/*.py" encouraged_structure: | class VendorError(Exception): """Base exception for vendor-related errors""" pass - id: "EXC-002" name: "Never use bare except" severity: "error" description: | Always specify exception types. Bare except catches everything including KeyboardInterrupt and SystemExit. pattern: file_pattern: "**/*.py" anti_patterns: - "except:" - "except\\s*:" - id: "EXC-003" name: "Log all exceptions with context" severity: "warning" description: | When catching exceptions, log them with context and stack trace. pattern: file_pattern: "app/services/**/*.py" encouraged_patterns: - "logger.error" - "exc_info=True" # ============================================================================ # NAMING CONVENTION RULES # ============================================================================ naming_rules: - id: "NAM-001" name: "API files use PLURAL names" severity: "error" description: | API endpoint files should use plural names (vendors.py, products.py) pattern: file_pattern: "app/api/v1/**/*.py" check: "plural_naming" exceptions: - "__init__.py" - "auth.py" - "health.py" - id: "NAM-002" name: "Service files use SINGULAR + 'service' suffix" severity: "error" description: | Service files should use singular name + _service (vendor_service.py) pattern: file_pattern: "app/services/**/*.py" check: "service_naming" - id: "NAM-003" name: "Model files use SINGULAR names" severity: "error" description: | Both database and schema model files use singular names (product.py) pattern: file_pattern: "models/**/*.py" check: "singular_naming" - id: "NAM-004" name: "Use consistent terminology: vendor not shop" severity: "warning" description: | Use 'vendor' consistently, not 'shop' (except for shop frontend) pattern: file_pattern: "app/**/*.py" discouraged_terms: - "shop_id" # Use vendor_id - "shop_service" # Use vendor_service - id: "NAM-005" name: "Use consistent terminology: inventory not stock" severity: "warning" description: | Use 'inventory' consistently, not 'stock' pattern: file_pattern: "app/**/*.py" discouraged_terms: - "stock_service" # Use inventory_service # ============================================================================ # JAVASCRIPT ARCHITECTURE RULES (Frontend) # ============================================================================ javascript_rules: - id: "JS-001" name: "Use centralized logger, not console" severity: "error" description: | Use window.LogConfig.createLogger() for consistent logging. Never use console.log, console.error, console.warn directly. pattern: file_pattern: "static/**/js/**/*.js" anti_patterns: - "console\\.log" - "console\\.error" - "console\\.warn" exceptions: - "// eslint-disable" - "console.log('✅" # Bootstrap messages allowed - id: "JS-002" name: "Use lowercase apiClient for API calls" severity: "error" description: | Use lowercase 'apiClient' consistently, not 'ApiClient' or 'API_CLIENT' pattern: file_pattern: "static/**/js/**/*.js" anti_patterns: - "ApiClient\\." - "API_CLIENT\\." required_pattern: "apiClient\\." - id: "JS-003" name: "Alpine components must spread ...data()" severity: "error" description: | All Alpine.js components must inherit base layout data using spread operator pattern: file_pattern: "static/**/js/**/*.js" required_in_alpine_components: - "\\.\\.\\.data\\(\\)" - id: "JS-004" name: "Alpine components must set currentPage" severity: "error" description: | All Alpine.js page components must set a currentPage identifier pattern: file_pattern: "static/**/js/**/*.js" required_in_alpine_components: - "currentPage:" - id: "JS-005" name: "Initialization methods must include guard" severity: "error" description: | Init methods should prevent duplicate initialization with guard pattern: file_pattern: "static/**/js/**/*.js" recommended_pattern: | if (window._pageInitialized) return; window._pageInitialized = true; - id: "JS-006" name: "All async operations must have try/catch with error logging" severity: "error" description: | All API calls and async operations must have error handling pattern: file_pattern: "static/**/js/**/*.js" check: "async_error_handling" - id: "JS-007" name: "Set loading state before async operations" severity: "warning" description: | Loading state should be set before and cleared after async operations pattern: file_pattern: "static/**/js/**/*.js" recommended_pattern: | loading = true; try { // operation } finally { loading = false; } # ============================================================================ # TEMPLATE RULES (Jinja2) # ============================================================================ template_rules: - id: "TPL-001" name: "Admin templates must extend admin/base.html" severity: "error" description: "All admin templates must extend the base template for consistency" pattern: file_pattern: "app/templates/admin/**/*.html" required_patterns: - "{% extends ['\"]admin/base\\.html['\"] %}" exceptions: - "base.html" - "partials/" - id: "TPL-002" name: "Vendor templates must extend vendor/base.html" severity: "error" description: "All vendor templates must extend the base template" pattern: file_pattern: "app/templates/vendor/**/*.html" required_patterns: - "{% extends ['\"]vendor/base\\.html['\"] %}" exceptions: - "base.html" - "partials/" - id: "TPL-003" name: "Shop templates must extend shop/base.html" severity: "error" description: "All shop templates must extend the base template" pattern: file_pattern: "app/templates/shop/**/*.html" required_patterns: - "{% extends ['\"]shop/base\\.html['\"] %}" exceptions: - "base.html" - "partials/" - id: "TPL-004" name: "Use x-text for dynamic text content (prevents XSS)" severity: "warning" description: | Use x-text directive for dynamic content to prevent XSS vulnerabilities pattern: file_pattern: "app/templates/**/*.html" recommended_pattern: '

' - id: "TPL-005" name: "Use x-html ONLY for safe content" severity: "error" description: | Use x-html only for trusted content like icons, never for user-generated content pattern: file_pattern: "app/templates/**/*.html" safe_usage: - 'x-html="\\$icon\\(' - id: "TPL-006" name: "Implement loading state for data loads" severity: "warning" description: | All templates that load data should show loading state pattern: file_pattern: "app/templates/**/*.html" recommended_pattern: '
Loading...
' - id: "TPL-007" name: "Implement empty state when no data" severity: "warning" description: | Show empty state when lists have no items pattern: file_pattern: "app/templates/**/*.html" recommended_pattern: '' # ============================================================================ # FRONTEND STYLING RULES # ============================================================================ styling_rules: - id: "CSS-001" name: "Use Tailwind utility classes" severity: "warning" description: | Prefer Tailwind utility classes over custom CSS pattern: file_pattern: "app/templates/**/*.html" encouraged: true - id: "CSS-002" name: "Support dark mode with dark: prefix" severity: "warning" description: | All color classes should include dark mode variants pattern: file_pattern: "app/templates/**/*.html" recommended_pattern: 'class="bg-white dark:bg-gray-800"' - id: "CSS-003" name: "Shop templates use vendor theme CSS variables" severity: "error" description: | Shop templates must use CSS variables for vendor-specific theming pattern: file_pattern: "app/templates/shop/**/*.html" required_pattern: 'var\\(--color-primary\\)' - id: "CSS-004" name: "Mobile-first responsive design" severity: "warning" description: | Use mobile-first responsive classes pattern: file_pattern: "app/templates/**/*.html" recommended_pattern: 'class="grid-cols-1 md:grid-cols-2 lg:grid-cols-4"' # ============================================================================ # MIDDLEWARE RULES # ============================================================================ middleware_rules: - id: "MDW-001" name: "Middleware files use simple nouns without _middleware suffix" severity: "warning" description: | Middleware files should be named with simple nouns (auth.py, not auth_middleware.py) pattern: file_pattern: "middleware/**/*.py" check: "middleware_naming" - id: "MDW-002" name: "Vendor context must be injected for vendor/shop routes" severity: "error" description: | Vendor context middleware must set request.state.vendor_id and request.state.vendor pattern: file_pattern: "middleware/vendor_context.py" required: true # ============================================================================ # MULTI-TENANCY RULES # ============================================================================ multi_tenancy_rules: - id: "MT-001" name: "All queries must be scoped to vendor_id" severity: "error" description: | In vendor/shop contexts, all database queries must filter by vendor_id pattern: file_pattern: "app/services/**/*.py" context: "vendor_shop" required_pattern: ".filter\\(.*vendor_id.*\\)" - id: "MT-002" name: "No cross-vendor data access" severity: "error" description: | Queries must never access data from other vendors pattern: file_pattern: "app/services/**/*.py" enforcement: "database_query_level" # ============================================================================ # AUTHENTICATION & AUTHORIZATION RULES # ============================================================================ auth_rules: - id: "AUTH-001" name: "Use JWT tokens in Authorization header" severity: "error" description: | Authentication must use JWT tokens in Authorization: Bearer header pattern: file_pattern: "app/api/**/*.py" enforcement: "middleware" - id: "AUTH-002" name: "Role-based access control with Depends" severity: "error" description: | Use Depends(get_current_admin/vendor/customer) for role checks pattern: file_pattern: "app/api/v1/**/*.py" required: "Depends\\(get_current_" - id: "AUTH-003" name: "Never store plain passwords" severity: "error" description: | Always hash passwords with bcrypt before storing pattern: file_pattern: "app/services/auth_service.py" required: "bcrypt" # ============================================================================ # CODE QUALITY RULES # ============================================================================ code_quality_rules: - id: "QUAL-001" name: "All code must be formatted with Ruff" severity: "error" description: | Run 'make format' before committing enforcement: "pre_commit" - id: "QUAL-002" name: "All code must pass Ruff linting" severity: "error" description: | Run 'make lint' before committing enforcement: "pre_commit" - id: "QUAL-003" name: "Type hints recommended for functions" severity: "warning" description: | Add type hints to function parameters and return types pattern: file_pattern: "app/**/*.py" encouraged: true # ============================================================================ # VALIDATION SEVERITY LEVELS # ============================================================================ severity_levels: error: description: "Critical architectural violation - must be fixed" exit_code: 1 warning: description: "Pattern deviation - should be fixed" exit_code: 0 info: description: "Suggestion for improvement" exit_code: 0 # ============================================================================ # IGNORED PATTERNS # ============================================================================ ignore: files: - "**/*_test.py" - "**/test_*.py" - "**/__pycache__/**" - "**/migrations/**" - "**/alembic/versions/**" - "**/node_modules/**" - "**/.venv/**" - "**/venv/**" - ".venv/**" - "venv/**" - "**/build/**" - "**/dist/**" patterns: # Allow HTTPException in specific files - file: "app/core/exceptions.py" pattern: "HTTPException" reason: "Exception handling utilities" - file: "app/exceptions/handler.py" pattern: "HTTPException" reason: "Exception handler converts to HTTP" # ============================================================================ # DOCUMENTATION # ============================================================================ documentation: architecture: "docs/architecture/overview.md" backend: "docs/backend/overview.md" frontend: "docs/frontend/overview.md" contributing: "docs/development/contributing.md" code_quality: "docs/development/code-quality.md"