---
title: "Architecture Patterns"
description: "Architecture validation and patterns for clean architecture, backend structure enforcement, project structure validation, test standards, and context-aware sizing. Use when designing system boundaries, enforcing layered architecture, validating project structure, defining test standards, or choosing the right architecture tier for project scope."
canonical: "https://orchestkit.yonyon.ai/docs/reference/skills/architecture-patterns"
---

# Architecture Patterns

Architecture validation and patterns for clean architecture, backend structure enforcement, project structure validation, test standards, and context-aware sizing. Use when designing system boundaries, enforcing layered architecture, validating project structure, defining test standards, or choosing the right architecture tier for project scope.

<span className="badge badge-gray">Reference</span> <span className="badge badge-orange">high</span>

> **Auto-activated** — this skill loads automatically when Claude detects matching context.

<ContextualSkillSidebar slug="architecture-patterns" />

> **Architecture Patterns** Architecture validation and patterns for clean architecture, backend structure enforcement, project structure validation, test standards, and context-aware sizing. Use when designing system boundaries, enforcing layered architecture, validating project structure, defining test standards, or choosing the right architecture tier for project scope.


&lt;!-- directive-density: intentional (teaches anti-patterns; NEVER markers describe real layering violations, not aspirational guidance) --&gt;

# Architecture Patterns

Consolidated architecture validation and enforcement patterns covering clean architecture, backend layer separation, project structure conventions, and test standards. Each category has individual rule files in `rules/` loaded on-demand. House scars and dated decisions rescued from retired reference tutorials live in `references/ork-delta.md`; the tutorials themselves are upstream's job (see "Upstream coverage" below).

## Quick Reference

| Category | Rules | Impact | When to Use |
|----------|-------|--------|-------------|
| [Clean Architecture](#clean-architecture) | 3 | HIGH | SOLID principles, hexagonal architecture, ports & adapters, DDD |
| [Project Structure](#project-structure) | 2 | HIGH | Folder conventions, nesting depth, import direction, barrel files |
| [Backend Layers](#backend-layers) | 3 | HIGH | Router/service/repository separation, DI, file naming |
| [Test Standards](#test-standards) | 3 | MEDIUM | AAA pattern, naming conventions, coverage thresholds |
| [Right-Sizing](#right-sizing) | 2 | HIGH | Architecture tier selection, over-engineering prevention, context-aware enforcement |

**Total: 13 rules across 5 categories**

## Quick Start

```python
# Clean Architecture: Dependency Inversion via Protocol
class IUserRepository(Protocol):
    async def get_by_id(self, id: str) -> User | None: ...

class UserService:
    def __init__(self, repo: IUserRepository):
        self._repo = repo  # Depends on abstraction, not concretion

# FastAPI DI chain: DB -> Repository -> Service
def get_user_service(db: AsyncSession = Depends(get_db)) -> UserService:
    return UserService(PostgresUserRepository(db))
```

```
# Project Structure: Unidirectional Import Architecture
shared/lib  ->  components  ->  features  ->  app
(lowest)                                    (highest)

# Backend Layers: Strict Separation
Routers (HTTP) -> Services (Business Logic) -> Repositories (Data Access)
```

## Clean Architecture

SOLID principles, hexagonal architecture, ports and adapters, and DDD tactical patterns for maintainable backends.

| Rule | File | Key Pattern |
|------|------|-------------|
| Hexagonal Architecture | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/clean-hexagonal.md` | Driving/driven ports, adapter implementations, layer structure |
| SOLID & Dependency Rule | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/clean-dependency-rule.md` | Protocol-based interfaces, dependency inversion, FastAPI DI |
| DDD Tactical Patterns | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/clean-ports-adapters.md` | Entities, value objects, aggregate roots, domain events |

Design review checklist: `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/checklists/solid-checklist.md`. Domain entity scaffold: `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/scripts/domain-entity-template.py`.

### Key Decisions

| Decision | Recommendation |
|----------|----------------|
| Protocol vs ABC | Protocol (structural typing) |
| Dataclass vs Pydantic | Dataclass for domain, Pydantic for API |
| Repository granularity | One per aggregate root |
| Transaction boundary | Service layer, not repository |
| Event publishing | Collect in aggregate, publish after commit |

## Project Structure

Feature-based organization, max nesting depth, unidirectional imports, and barrel file prevention.

| Rule | File | Key Pattern |
|------|------|-------------|
| Folder Structure & Nesting | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/structure-folders.md` | React/Next.js and FastAPI layouts, 4-level max nesting, barrel file rules |
| Import Direction & Location | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/references/structure-import-direction.md` | Unidirectional imports, cross-feature prevention, component/hook placement |

### Blocking Rules

| Rule | Check |
|------|-------|
| Max Nesting | Max 4 levels from src/ or app/ |
| No Barrel Files | No index.ts re-exports (tree-shaking issues) |
| Component Location | React components in components/ or features/ only |
| Hook Location | Custom hooks in hooks/ or features/*/hooks/ only |
| Import Direction | Unidirectional: shared -> components -> features -> app |

## Backend Layers

FastAPI Clean Architecture with router/service/repository layer separation and blocking validation.

| Rule | File | Key Pattern |
|------|------|-------------|
| Layer Separation | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/backend-layers.md` | Router/service/repository boundaries, forbidden patterns, async rules |
| Dependency Injection | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/backend-di.md` | Depends() chains, blocked DI patterns, violation detection |
| File Naming & Exceptions | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/backend-repository.md` | Naming conventions, async rules, domain exceptions |

House scars for this category (exception-to-HTTP status map, import-level violation greps, DI override teardown): `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/references/ork-delta.md`.

### Layer Boundaries

| Layer | Responsibility | Forbidden |
|-------|---------------|-----------|
| Routers | HTTP concerns, request parsing, auth checks | Database operations, business logic |
| Services | Business logic, validation, orchestration | HTTPException, Request objects |
| Repositories | Data access, queries, persistence | HTTP concerns, business logic |

## Test Standards

Testing best practices with AAA pattern, naming conventions, isolation, and coverage thresholds.

| Rule | File | Key Pattern |
|------|------|-------------|
| AAA Pattern & Isolation | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/testing-aaa.md` | Arrange-Act-Assert, test isolation, parameterized tests |
| Naming Conventions | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/references/testing-naming-conventions.md` | Descriptive behavior-focused names for Python and TypeScript |
| Coverage & Location | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/testing-coverage.md` | Coverage thresholds, fixture scopes, and (per `references/ork-delta.md`) the no-co-location rule |

### Coverage Requirements

| Area | Minimum | Target |
|------|---------|--------|
| Overall | 80% | 90% |
| Business Logic | 90% | 100% |
| Critical Paths | 95% | 100% |
| New Code | 100% | 100% |

## Right-Sizing

Context-aware backend architecture enforcement. Rules adjust strictness based on project tier detected by `scope-appropriate-architecture`.

**Enforcement procedure:**
1. Read project tier from `scope-appropriate-architecture` context (set during brainstorm/implement Step 0)
2. If no tier set, auto-detect using signals in `Read("$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/right-sizing-tiers.md")`
3. Apply tier-based enforcement matrix — skip rules marked OFF for detected tier
4. **Security rules are tier-independent** — always enforce SQL parameterization, input validation, auth checks

| Rule | File | Key Pattern |
|------|------|-------------|
| Architecture Sizing Tiers | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/right-sizing-tiers.md` | Interview/MVP/production/enterprise sizing matrix, LOC estimates, detection signals |
| Right-Sizing Decision Guide | `$\{CLAUDE_PLUGIN_ROOT\}/skills/architecture-patterns/rules/right-sizing-decision.md` | ORM, auth, error handling, testing recommendations per tier, over-engineering tax |

### Tier-Based Rule Enforcement

| Rule | Interview | MVP | Production | Enterprise |
|------|-----------|-----|------------|------------|
| Layer separation | OFF | WARN | BLOCK | BLOCK |
| Repository pattern | OFF | OFF | WARN | BLOCK |
| Domain exceptions | OFF | OFF | BLOCK | BLOCK |
| Dependency injection | OFF | WARN | BLOCK | BLOCK |
| OpenAPI documentation | OFF | OFF | WARN | BLOCK |

**Manual override:** User can set tier explicitly to bypass auto-detection (e.g., "I want enterprise patterns for this take-home to demonstrate skill").

### Decision Flowchart

```
Is this a take-home or hackathon?
  YES --> Flat architecture. Single file or 3-5 files. Done.
  NO  -->

Is this a prototype or MVP with < 3 months runway?
  YES --> Simple layered. Routes + services + models. No abstractions.
  NO  -->

Do you have > 5 engineers or complex domain rules?
  YES --> Clean architecture with ports/adapters.
  NO  --> Layered architecture. Add abstractions only when pain appears.
```

## When NOT to Use

Not every project needs architecture patterns. Match complexity to project tier:

| Pattern | Interview | Hackathon | MVP | Growth | Enterprise | Simpler Alternative |
|---------|-----------|-----------|-----|--------|------------|---------------------|
| Repository pattern | OVERKILL (~200 LOC) | OVERKILL | BORDERLINE | APPROPRIATE | REQUIRED | Direct ORM calls in service (~20 LOC) |
| DI containers | OVERKILL (~150 LOC) | OVERKILL | LIGHT ONLY | APPROPRIATE | REQUIRED | Constructor params or module-level singletons (~10 LOC) |
| Event-driven arch | OVERKILL (~300 LOC) | OVERKILL | OVERKILL | SELECTIVE | APPROPRIATE | Direct function calls between services (~30 LOC) |
| Hexagonal architecture | OVERKILL (~400 LOC) | OVERKILL | OVERKILL | BORDERLINE | APPROPRIATE | Flat modules with imports (~50 LOC) |
| Strict layer separation | OVERKILL (~250 LOC) | OVERKILL | WARN | BLOCK | BLOCK | Routes + models in same file (~40 LOC) |
| Domain exceptions | OVERKILL (~100 LOC) | OVERKILL | OVERKILL | BLOCK | BLOCK | Built-in ValueError/HTTPException (~5 LOC) |

**Rule of thumb:** If a pattern shows OVERKILL for the detected tier, do NOT use it. Use the simpler alternative. A take-home with hexagonal architecture signals over-engineering, not skill.

## Anti-Patterns (FORBIDDEN)

```python
# CLEAN ARCHITECTURE
# NEVER import infrastructure in domain layer
from app.infrastructure.database import engine  # In domain layer!

# NEVER leak ORM models to API layer
@router.get("/users/{id}")
async def get_user(id: str, db: Session) -> UserModel:  # Returns ORM model!

# NEVER have domain depend on framework
from fastapi import HTTPException
class UserService:
    def get(self, id: str):
        raise HTTPException(404)  # Framework in domain!

# PROJECT STRUCTURE
# NEVER create files deeper than 4 levels from src/
# NEVER create barrel files (index.ts re-exports)
# NEVER import from higher layers (features importing from app)
# NEVER import across features (use shared/ for common code)

# BACKEND LAYERS
# NEVER use database operations in routers
# NEVER raise HTTPException in services
# NEVER instantiate services without Depends()

# TEST STANDARDS
# NEVER mix test files with source code
# NEVER use non-descriptive test names (test1, test, works)
# NEVER share mutable state between tests without reset
```

## Upstream coverage (do not restate)

Long-form tutorials on these topics were removed from this skill (2026-07-31 wrap-plus-delta campaign). Read them at the first-party source; only floors, scars, and house decisions belong here (see `references/ork-delta.md`).

| Topic | First-party source |
|-------|--------------------|
| Hexagonal architecture, ports and adapters walkthrough | Alistair Cockburn, https://alistair.cockburn.us/hexagonal-architecture/ and Architecture Patterns with Python, https://www.cosmicpython.com/ |
| SOLID principles tutorial (Protocol-based) | Architecture Patterns with Python, https://www.cosmicpython.com/ and Python Protocol spec, https://typing.python.org/en/latest/spec/protocol.html |
| DDD tactical patterns (entities, value objects, aggregates, domain events) | Architecture Patterns with Python, https://www.cosmicpython.com/ |
| FastAPI dependency injection, auth dependencies, DI test overrides | FastAPI docs (context7: /tiangolo/fastapi), https://fastapi.tiangolo.com/tutorial/dependencies/ and skill ork:python-backend |
| Router/service/repository layer walkthrough | FastAPI bigger applications, https://fastapi.tiangolo.com/tutorial/bigger-applications/ and skill ork:python-backend |
| Full FastAPI clean-architecture example app | FastAPI full-stack template, https://github.com/fastapi/full-stack-fastapi-template |
| Next.js folder layout and structure-violation catalog | Next.js project structure docs, https://nextjs.org/docs/app/getting-started/project-structure (skill vercel:nextjs) |
| AAA pattern, isolation, parameterized tests, fixture scoping, coverage config | Skill ork:testing-unit; pytest docs, https://docs.pytest.org/en/stable/ and Vitest coverage, https://vitest.dev/config/#coverage |

## Related Skills

- `ork:scope-appropriate-architecture` - Project tier detection that drives right-sizing enforcement
- `ork:quality-gates` - YAGNI gate uses tier context to validate complexity
- `ork:distributed-systems` - Distributed locking, resilience, idempotency patterns
- `ork:api-design` - REST API design, versioning, error handling
- `ork:testing-unit` - Unit testing: AAA pattern, fixtures, mocking, factories
- `ork:testing-e2e` - E2E testing: Playwright, page objects, visual regression
- `ork:testing-integration` - Integration testing: API endpoints, database, contracts
- `ork:python-backend` - FastAPI, SQLAlchemy, asyncio patterns
- `ork:database-patterns` - Schema design, query optimization, migrations


---

## Rules (13)

### Apply dependency injection to ensure testable code and prevent tight coupling between layers — HIGH


# Dependency Injection

## Dependency Chain

```python
# deps.py - Dependency providers
def get_user_repository(
    db: AsyncSession = Depends(get_db),
) -> UserRepository:
    return UserRepository(db)

def get_user_service(
    repo: UserRepository = Depends(get_user_repository),
) -> UserService:
    return UserService(repo)

# router_users.py - Usage
@router.get("/{user_id}")
async def get_user(
    user_id: int,
    service: UserService = Depends(get_user_service),
):
    return await service.get_user(user_id)
```

## Blocked DI Patterns

```python
# BLOCKED - Direct instantiation
service = UserService()

# BLOCKED - Global instance
user_service = UserService()

# BLOCKED - Missing Depends()
async def get_users(db: AsyncSession):  # Missing Depends()
```

## Common Violations

| Violation | Detection | Fix |
|-----------|-----------|-----|
| DB in router | db.add, db.execute in routers/ | Move to repository |
| HTTPException in service | raise HTTPException in services/ | Use domain exceptions |
| Direct instantiation | Service() without Depends | Use Depends(get_service) |
| Missing await | Sync calls in async | Add await or use executor |

**Incorrect — direct service instantiation in router:**
```typescript
@router.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)) {
    service = UserService(db);  // Direct instantiation, untestable
    return await service.get_user(user_id);
}
```

**Correct — dependency injection with Depends:**
```typescript
@router.get("/users/{user_id}")
async def get_user(
    user_id: int,
    service: UserService = Depends(get_user_service)  // Injected, testable
) {
    return await service.get_user(user_id);
}
```


### Separate backend layers to prevent coupling between HTTP, business logic, and data access — HIGH


# Backend Layer Separation

## Architecture Overview

```
+-------------------------------------------------------------------+
|                        ROUTERS LAYER                               |
|  HTTP concerns only: request parsing, response formatting          |
+-------------------------------------------------------------------+
|                        SERVICES LAYER                              |
|  Business logic: orchestration, validation, transformations        |
+-------------------------------------------------------------------+
|                      REPOSITORIES LAYER                            |
|  Data access: database queries, external API calls                 |
+-------------------------------------------------------------------+
|                        MODELS LAYER                                |
|  Data structures: SQLAlchemy models, Pydantic schemas             |
+-------------------------------------------------------------------+
```

## Validation Rules (BLOCKING)

| Rule | Check | Layer |
|------|-------|-------|
| No DB in Routers | Database operations blocked | routers/ |
| No HTTP in Services | HTTPException blocked | services/ |
| No Business Logic in Routers | Complex logic blocked | routers/ |
| Use Depends() | Direct instantiation blocked | routers/ |
| Async Consistency | Sync calls in async blocked | all |

## Exception Pattern

```python
# Domain exceptions (services/repositories)
class UserNotFoundError(DomainException):
    def __init__(self, user_id: int):
        super().__init__(f"User {user_id} not found")

# Router converts to HTTP
@router.get("/{user_id}")
async def get_user(user_id: int, service: UserService = Depends(get_user_service)):
    try:
        return await service.get_user(user_id)
    except UserNotFoundError:
        raise HTTPException(404, "User not found")
```

**Incorrect — database logic in router layer:**
```typescript
@router.post("/users")
async def create_user(data: UserCreate, db: AsyncSession = Depends(get_db)) {
    user = User(**data.dict());  // Business logic in router
    db.add(user);  // Database access in router
    await db.commit();
    return user;
}
```

**Correct — router delegates to service layer:**
```typescript
@router.post("/users")
async def create_user(
    data: UserCreate,
    service: UserService = Depends(get_user_service)
) {
    return await service.create_user(data);  // Service handles logic
}
```


### Follow consistent file naming conventions and exception patterns for discoverable code — HIGH


# File Naming & Exceptions

## File Naming Conventions

| Layer | Allowed Patterns | Blocked Patterns |
|-------|-----------------|------------------|
| Routers | router_*.py, routes_*.py, api_*.py, deps.py | users.py, UserRouter.py |
| Services | *_service.py | users.py, UserService.py, service_*.py |
| Repositories | *_repository.py, *_repo.py | users.py, repository_*.py |
| Schemas | *_schema.py, *_dto.py, *_request.py, *_response.py | users.py, UserSchema.py |
| Models | *_model.py, *_entity.py, *_orm.py, base.py | users.py, UserModel.py |

## Async Rules

```python
# GOOD - Async all the way
result = await db.execute(select(User))

# BLOCKED - Sync in async function
result = db.execute(select(User))  # Missing await

# For sync code, use executor
await loop.run_in_executor(None, sync_function)
```

## Key Principles

- Use snake_case with suffixes for Python files
- Routers prefix with `router_`, services suffix with `_service`
- Domain exceptions in domain layer, HTTP conversion in routers only
- All database operations must use `await`

**Incorrect — missing await on async database operation:**
```typescript
async function getUser(db: AsyncSession, userId: number) {
    result = db.execute(select(User).where(User.id === userId));  // Missing await
    return result.scalar_one_or_none();
}
```

**Correct — properly awaiting async database calls:**
```typescript
async function getUser(db: AsyncSession, userId: number) {
    result = await db.execute(select(User).where(User.id === userId));
    return result.scalar_one_or_none();
}
```


### Apply SOLID principles and dependency inversion for maintainable testable abstractions — HIGH


# SOLID Principles in Python

## S - Single Responsibility

```python
# GOOD: Separate responsibilities
class UserService:
    def create_user(self, data: UserCreate) -> User: ...

class EmailService:
    def send_welcome(self, user: User) -> None: ...

class ReportService:
    def generate_user_report(self, users: list[User]) -> Report: ...
```

## O - Open/Closed (Protocol-based)

```python
from typing import Protocol

class PaymentProcessor(Protocol):
    async def process(self, amount: Decimal) -> PaymentResult: ...

class StripeProcessor:
    async def process(self, amount: Decimal) -> PaymentResult: ...

class PayPalProcessor:
    async def process(self, amount: Decimal) -> PaymentResult: ...
```

## I - Interface Segregation

```python
# GOOD: Segregated interfaces
class IReader(Protocol):
    async def get(self, id: str) -> T | None: ...

class IWriter(Protocol):
    async def save(self, entity: T) -> T: ...

class ISearchable(Protocol):
    async def search(self, query: str) -> list[T]: ...
```

## D - Dependency Inversion

```python
class IAnalysisRepository(Protocol):
    async def get_by_id(self, id: str) -> Analysis | None: ...

class AnalysisService:
    def __init__(self, repo: IAnalysisRepository):
        self._repo = repo  # Depends on abstraction

def get_analysis_service(db: AsyncSession = Depends(get_db)) -> AnalysisService:
    repo = PostgresAnalysisRepository(db)
    return AnalysisService(repo)
```

## Key Decisions

| Decision | Recommendation |
|----------|----------------|
| Protocol vs ABC | Protocol (structural typing) |
| Dataclass vs Pydantic | Dataclass for domain, Pydantic for API |

**Incorrect — service directly depends on concrete implementation:**
```typescript
class AnalysisService {
    constructor() {
        this._repo = new PostgresAnalysisRepository();  // Tight coupling
    }
}
```

**Correct — service depends on abstraction via protocol:**
```typescript
class IAnalysisRepository(Protocol):
    async def get_by_id(self, id: str) -> Analysis | None: ...

class AnalysisService:
    def __init__(self, repo: IAnalysisRepository):  // Depends on abstraction
        self._repo = repo
```


### Decouple domain logic from infrastructure with hexagonal architecture for testability — HIGH


# Hexagonal Architecture (Ports & Adapters)

```
+-------------------------------------------------------------------+
|                      DRIVING ADAPTERS                               |
|  FastAPI Routes  |  CLI Commands  |  Celery Tasks  |  Tests/Mocks  |
|       |                |                |                |          |
|       v                v                v                v          |
|  +===============================================================+ |
|  |                    INPUT PORTS                                 | |
|  |  AnalysisService (Use Cases)  |  UserService (Use Cases)      | |
|  +===============================================================+ |
|  |                      DOMAIN                                    | |
|  |  Entities  |  Value Objects  |  Domain Events                  | |
|  +===============================================================+ |
|  |                   OUTPUT PORTS                                 | |
|  |  IAnalysisRepo (Protocol)  |  INotificationService (Protocol) | |
|  +===============================================================+ |
|       |                                        |                    |
|       v                                        v                    |
|  PostgresRepo (SQLAlchemy)     EmailNotificationService (SMTP)      |
|                      DRIVEN ADAPTERS                                |
+-------------------------------------------------------------------+
```

## Directory Structure

```
backend/app/
├── api/v1/              # Driving adapters (FastAPI routes)
├── domains/
│   └── analysis/
│       ├── entities.py      # Domain entities
│       ├── value_objects.py  # Value objects
│       ├── services.py      # Domain services (use cases)
│       ├── repositories.py  # Output port protocols
│       └── events.py        # Domain events
├── infrastructure/
│   ├── repositories/    # Driven adapters (PostgreSQL)
│   ├── services/        # External service adapters
│   └── messaging/       # Event publishers
└── core/
    ├── dependencies.py  # FastAPI DI configuration
    └── protocols.py     # Shared protocols
```

## Key Principles

- Domain layer has **zero** external dependencies
- Input ports define use cases (service interfaces)
- Output ports define infrastructure needs (repository protocols)
- Driving adapters call inward (routes -> services)
- Driven adapters are called outward (services -> repositories)

**Incorrect — domain layer importing infrastructure:**
```typescript
// In domains/analysis/services.py
from infrastructure.repositories.postgres import PostgresAnalysisRepository  // Violates hex arch

class AnalysisService:
    def __init__(self):
        self.repo = PostgresAnalysisRepository()
```

**Correct — domain depends only on ports:**
```typescript
// In domains/analysis/repositories.py (port)
class IAnalysisRepository(Protocol):
    async def get_by_id(self, id: str) -> Analysis | None: ...

// In domains/analysis/services.py
class AnalysisService:
    def __init__(self, repo: IAnalysisRepository):  // Depends on port only
        self._repo = repo
```


### Model complex domains with DDD tactical patterns using clear boundaries and rich logic — HIGH


# DDD Tactical Patterns

## Entity (Identity-based)

```python
from dataclasses import dataclass, field
from uuid import UUID, uuid4

@dataclass
class Analysis:
    id: UUID = field(default_factory=uuid4)
    source_url: str
    status: AnalysisStatus
    created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Analysis):
            return False
        return self.id == other.id  # Identity equality
```

## Value Object (Structural equality)

```python
@dataclass(frozen=True)  # Immutable
class AnalysisType:
    category: str
    depth: int

    def __post_init__(self):
        if self.depth < 1 or self.depth > 3:
            raise ValueError("Depth must be 1-3")
```

## Aggregate Root

```python
class AnalysisAggregate:
    def __init__(self, analysis: Analysis, artifacts: list[Artifact]):
        self._analysis = analysis
        self._artifacts = artifacts
        self._events: list[DomainEvent] = []

    def complete(self, summary: str) -> None:
        self._analysis.status = AnalysisStatus.COMPLETED
        self._analysis.summary = summary
        self._events.append(AnalysisCompleted(self._analysis.id))

    def collect_events(self) -> list[DomainEvent]:
        events = self._events.copy()
        self._events.clear()
        return events
```

## Key Decisions

| Decision | Recommendation |
|----------|----------------|
| Repository granularity | One per aggregate root |
| Transaction boundary | Service layer, not repository |
| Event publishing | Collect in aggregate, publish after commit |

**Incorrect — mutable value object violates immutability:**
```typescript
@dataclass
class AnalysisType:  // Mutable by default
    category: str
    depth: int

analysis_type = AnalysisType("security", 2)
analysis_type.depth = 5  // Can mutate, breaks value object contract
```

**Correct — frozen dataclass ensures immutability:**
```typescript
@dataclass(frozen=True)  // Immutable
class AnalysisType:
    category: str
    depth: int

analysis_type = AnalysisType("security", 2)
analysis_type.depth = 5  // FrozenInstanceError
```


### Choose the right ORM, auth, and error handling per tier to avoid unnecessary abstraction — HIGH


## Right-Sizing Decision Guide

Context-aware recommendations for ORM, auth, error handling, and testing by project tier.

**Incorrect — rolling custom auth for an MVP:**
```python
# MVP with 0 users, building custom JWT from scratch
import jwt
from datetime import datetime, timedelta, UTC
from argon2 import PasswordHasher  # argon2-cffi, defaults to Argon2id

pwd_hasher = PasswordHasher()

def create_access_token(user_id: str) -> str:
    expire = datetime.now(UTC) + timedelta(minutes=15)
    return jwt.encode({"sub": user_id, "exp": expire}, SECRET_KEY)

def create_refresh_token(user_id: str) -> str:
    expire = datetime.now(UTC) + timedelta(days=7)
    return jwt.encode({"sub": user_id, "exp": expire, "type": "refresh"}, SECRET_KEY)

# +200 LOC for token rotation, revocation, middleware...
```

**Correct — managed auth for MVP, custom for production:**
```python
# MVP: Use managed auth (Supabase/Clerk/Auth0)
from supabase import create_client
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)

# Auth is a solved problem — build your differentiator

# Production: JWT with proper refresh rotation
# (justified at this scale)
```

**ORM approach by tier:**

| Context | Recommendation | Anti-Pattern |
|---------|---------------|--------------|
| Interview | Raw SQL or SQLModel | Repository + Unit of Work |
| MVP | Simple ORM, models near routes | Abstract repository protocol |
| Production | ORM with repository per aggregate | Every table gets its own repo |
| Enterprise | Full repository + Unit of Work | Over-abstracting simple lookups |

**Authentication by tier:**

| Context | Recommendation | Anti-Pattern |
|---------|---------------|--------------|
| Interview | Session cookies or hardcoded key | Full OAuth2 + PKCE |
| MVP | Supabase Auth / Clerk / Auth0 | Rolling your own JWT |
| Production | JWT (15min access + 7d refresh) | No refresh tokens |
| Enterprise | OAuth2.1 + PKCE + SSO + MFA | Skipping SSO |

**Over-engineering tax (LOC overhead when applied unnecessarily):**

| Pattern | LOC Overhead | Justified When |
|---------|-------------|----------------|
| Repository pattern | +150-300/entity | 3+ consumers of same data |
| Domain exceptions | +50-100 | Multiple transports |
| Generic base repository | +100-200 | 5+ repos with shared queries |
| Unit of Work | +150-250 | Cross-aggregate transactions |
| Event sourcing | +500-2000 | Audit trail mandated |
| CQRS | +300-800 | Read/write models diverge 50%+ |

**Key rules:**
- MVP auth should use managed services (Supabase, Clerk, Auth0) — auth is a solved problem
- Interview testing needs only 3-5 smoke tests proving it works, not 80% coverage
- Error handling for interviews: try/except with clear HTTP codes, not RFC 9457
- Add abstractions only when pain appears, not preemptively


### Select the correct architecture sizing tier to avoid over-engineering or missing foundations — HIGH


## Architecture Sizing Tiers

Match architecture complexity to project scope using concrete signals. Read the project tier from `scope-appropriate-architecture` context (set during brainstorm/implement Step 0). If no tier is set, auto-detect using the signals below.

**Enforcement rule:** When reviewing or generating code, check the detected tier FIRST. If a pattern is marked OFF for the current tier, do not suggest or enforce it. If marked WARN, mention the concern but don't block. If marked BLOCK, enforce strictly.

**Incorrect — enterprise patterns for a take-home:**
```python
# 4-hour interview take-home with full hexagonal architecture
# app/domain/repositories/user_repository.py
class IUserRepository(Protocol):
    async def get_by_id(self, id: UUID) -> User | None: ...
    async def save(self, user: User) -> User: ...

# app/infrastructure/repositories/postgres_user_repository.py
class PostgresUserRepository:
    def __init__(self, session: AsyncSession): ...
    # +300 LOC for a single CRUD entity
```

**Correct — right-sized for context:**
```python
# Interview: flat, 3-5 files, 300-600 LOC total
# main.py — everything in one file
from fastapi import FastAPI
from sqlmodel import SQLModel, Field, Session, create_engine

app = FastAPI()
engine = create_engine("sqlite:///db.sqlite3")

class Todo(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    title: str
    done: bool = False

@app.get("/todos")
def list_todos():
    with Session(engine) as session:
        return session.query(Todo).all()
```

**Sizing matrix:**

| Signal | Flat/Simple | Layered | Clean/Hexagonal |
|--------|-------------|---------|-----------------|
| Timeline | Hours to days | Weeks to months | Months to years |
| Team size | 1 developer | 2-5 developers | 5+ developers |
| Lifespan | Disposable / demo | 1-3 years | 3+ years |
| Domain complexity | CRUD, single entity | 3-10 entities | Complex invariants |
| Users | &lt; 100 | 100-10,000 | 10,000+ |
| LOC estimate | 200-800 | 1,000-10,000 | 10,000+ |

**Tier detection signals:**

| Signal | Interview | MVP | Production | Enterprise |
|--------|-----------|-----|------------|------------|
| README mentions take-home | Yes | — | — | — |
| File count &lt; 10 | Yes | — | — | — |
| No CI config | — | Yes | — | — |
| File count &lt; 50 | — | Yes | — | — |
| Has k8s/terraform | — | — | — | Yes |
| Has monorepo (packages/) | — | — | — | Yes |

**Tier-based rule enforcement:**

| Rule | Interview | MVP | Production | Enterprise |
|------|-----------|-----|------------|------------|
| Layer separation | OFF | WARN | BLOCK | BLOCK |
| Repository pattern | OFF | OFF | WARN | BLOCK |
| Domain exceptions | OFF | OFF | BLOCK | BLOCK |
| Dependency injection | OFF | WARN | BLOCK | BLOCK |
| OpenAPI documentation | OFF | OFF | WARN | BLOCK |

**Key rules:**
- Default to layered architecture — 80% of projects need layered, not hexagonal
- Interview threshold is &lt; 10 files — demonstrate thinking, not scaffolding
- Add repository pattern only when 3+ query consumers exist
- Add CQRS only when read/write models differ by 50%+
- Security patterns (SQL parameterization, input validation, auth) are ALWAYS enforced regardless of tier
- User can override detected tier explicitly — respect manual overrides


### Enforce unidirectional imports to prevent circular dependencies and maintain clean architecture — HIGH


# Import Direction & Conventions

## Unidirectional Architecture

```
shared/lib  ->  components  ->  features  ->  app
(lowest)                                    (highest)
```

| Layer | Can Import From |
|-------|-----------------|
| shared/, lib/ | Nothing (base layer) |
| components/ | shared/, lib/, utils/ |
| features/ | shared/, lib/, components/, utils/ |
| app/ | Everything above |

## Blocked Imports

```typescript
// BLOCKED: shared/ importing from features/
import { authConfig } from '@/features/auth/config';

// BLOCKED: features/ importing from app/
import { RootLayout } from '@/app/layout';

// BLOCKED: Cross-feature imports
import { DashboardContext } from '@/features/dashboard/context';
// Fix: Extract to shared/ if needed by multiple features
```

## Type-Only Exception

```typescript
// ALLOWED: Type-only import from another feature
import type { User } from '@/features/users/types';
```

## Component Location Rules

```
ALLOWED: src/components/Button.tsx, src/features/auth/components/LoginForm.tsx
BLOCKED: src/utils/Button.tsx, src/services/Modal.tsx

ALLOWED: src/hooks/useAuth.ts, src/features/auth/hooks/useLogin.ts
BLOCKED: src/components/useAuth.ts, src/utils/useDebounce.ts
```

## Python File Locations

```
ALLOWED: app/routers/router_users.py, app/services/user_service.py
BLOCKED: app/user_service.py (not in services/), app/services/router_users.py (router in services/)
```

**Incorrect — feature importing from app layer:**
```typescript
// In src/features/auth/components/LoginForm.tsx
import { RootLayout } from '@/app/layout';  // Violates unidirectional flow
```

**Correct — feature imports from shared/components only:**
```typescript
// In src/features/auth/components/LoginForm.tsx
import { Button } from '@/components/ui/Button';  // Correct direction
import { useAuth } from '@/hooks/useAuth';
```


### Organize folders consistently to reduce cognitive load and improve codebase navigability — HIGH


# Folder Organization

## React/Next.js (Frontend)

```
src/
├── app/              # Next.js App Router
│   ├── (auth)/       # Route groups
│   ├── api/          # API routes
│   └── layout.tsx
├── components/       # Reusable UI components
│   ├── ui/           # Primitive components
│   └── forms/        # Form components
├── features/         # Feature modules (self-contained)
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   └── types.ts
│   └── dashboard/
├── hooks/            # Global custom hooks
├── lib/              # Third-party integrations
├── services/         # API clients
├── types/            # Global TypeScript types
└── utils/            # Pure utility functions
```

## FastAPI (Backend)

```
app/
├── routers/          # API route handlers
├── services/         # Business logic layer
├── repositories/     # Data access layer
├── schemas/          # Pydantic models
├── models/           # SQLAlchemy models
├── core/             # Config, security, deps
└── utils/            # Utility functions
```

## Nesting Depth (Max 4 levels)

```
ALLOWED (4 levels):
  src/features/auth/components/LoginForm.tsx

BLOCKED (5+ levels):
  src/features/dashboard/widgets/charts/line/LineChart.tsx
  -> Flatten to: src/features/dashboard/charts/LineChart.tsx
```

## No Barrel Files

```typescript
// BLOCKED: src/components/index.ts
export { Button } from './Button';

// GOOD: Import directly
import { Button } from '@/components/Button';
```

Barrel files break tree-shaking, cause circular dependencies, and slow builds.

**Incorrect — excessive nesting depth:**
```typescript
// 6 levels deep
src/features/dashboard/widgets/analytics/charts/line/LineChart.tsx
```

**Correct — flattened to maximum 4 levels:**
```typescript
// 4 levels, clear hierarchy
src/features/dashboard/charts/LineChart.tsx
```


### Structure tests with Arrange-Act-Assert pattern for reliable and maintainable test suites — MEDIUM


# AAA Pattern & Test Isolation

## TypeScript AAA

```typescript
describe('calculateDiscount', () => {
  test('should apply 10% discount for orders over $100', () => {
    // Arrange
    const order = createOrder({ total: 150 });
    const calculator = new DiscountCalculator();

    // Act
    const discount = calculator.calculate(order);

    // Assert
    expect(discount).toBe(15);
  });
});
```

## Python AAA

```python
class TestCalculateDiscount:
    def test_applies_10_percent_discount_over_threshold(self):
        # Arrange
        order = Order(total=150)
        calculator = DiscountCalculator()

        # Act
        discount = calculator.calculate(order)

        # Assert
        assert discount == 15
```

## Test Isolation

```typescript
// GOOD - Reset state in beforeEach
describe('ItemList', () => {
  let items: string[];
  beforeEach(() => { items = []; });

  test('adds item', () => {
    items.push('a');
    expect(items).toHaveLength(1);
  });

  test('starts empty', () => {
    expect(items).toHaveLength(0);
  });
});
```

## Parameterized Tests

```python
@pytest.mark.parametrize("email,expected", [
    ("user@example.com", True),
    ("invalid", False),
    ("@missing.com", False),
])
def test_email_validation(self, email: str, expected: bool):
    assert is_valid_email(email) == expected
```

**Incorrect — missing AAA structure, unclear test logic:**
```typescript
test('discount works', () => {
    expect(new DiscountCalculator().calculate(createOrder({ total: 150 }))).toBe(15);
});
```

**Correct — clear AAA sections make test readable:**
```typescript
test('should apply 10% discount for orders over $100', () => {
    // Arrange
    const order = createOrder({ total: 150 });
    const calculator = new DiscountCalculator();

    // Act
    const discount = calculator.calculate(order);

    // Assert
    expect(discount).toBe(15);
});
```


### Set coverage thresholds to ensure critical code paths are tested before deployment — MEDIUM


# Coverage & Fixtures

## Coverage Requirements

| Area | Minimum | Target |
|------|---------|--------|
| Overall | 80% | 90% |
| Business Logic | 90% | 100% |
| Critical Paths | 95% | 100% |
| New Code | 100% | 100% |

## Running Coverage

```bash
# TypeScript (Vitest/Jest)
npm test -- --coverage
npx vitest --coverage

# Python (pytest)
pytest --cov=app --cov-report=json
```

## Fixture Best Practices (Python)

```python
# Function scope (default) - Fresh each test
@pytest.fixture
def db_session():
    session = create_session()
    yield session
    session.rollback()

# Module scope - Shared across file
@pytest.fixture(scope="module")
def expensive_model():
    return load_ml_model()

# Session scope - Shared across all tests
@pytest.fixture(scope="session")
def db_engine():
    engine = create_engine(TEST_DB_URL)
    yield engine
    engine.dispose()
```

## Key Principles

- Enforce minimum 80% coverage before merge
- Use function scope for mutable state, session scope for expensive setup
- Include cleanup via `yield` in fixtures
- 100% coverage required for all new code

**Incorrect — fixture without cleanup leaks resources:**
```python
@pytest.fixture
def db_session():
    session = create_session()
    return session  # No cleanup, connection leak
```

**Correct — yield ensures cleanup runs:**
```python
@pytest.fixture
def db_session():
    session = create_session()
    yield session
    session.rollback()  # Always runs after test
    session.close()
```


### Name tests descriptively so they serve as documentation and aid debugging — MEDIUM


# Test Naming Conventions

## TypeScript/JavaScript

```typescript
// GOOD - Descriptive, behavior-focused
test('should return empty array when no items exist', () => {});
test('throws ValidationError when email is invalid', () => {});
it('renders loading spinner while fetching', () => {});

// BLOCKED - Too short, not descriptive
test('test1', () => {});
test('works', () => {});
it('test', () => {});
```

## Python

```python
# GOOD - snake_case, descriptive
def test_should_return_user_when_id_exists():
def test_raises_not_found_when_user_missing():

# BLOCKED - Not descriptive, wrong case
def testUser():      # camelCase
def test_1():        # Not descriptive
```

## File Location Rules

```
ALLOWED:
  tests/unit/user.test.ts
  tests/integration/api.test.ts
  __tests__/components/Button.test.tsx
  app/tests/test_users.py

BLOCKED:
  src/utils/helper.test.ts      # Tests in src/
  components/Button.test.tsx    # Tests outside test dir
  app/routers/test_routes.py    # Tests mixed with source
```

## Key Principles

- Test names describe **behavior**, not implementation
- Names should read as specifications
- Use "should" or "when" patterns for clarity
- Place all tests in dedicated test directories

**Incorrect — vague test name provides no context:**
```typescript
test('works', () => {
    const result = calculateTotal([10, 20]);
    expect(result).toBe(30);
});
```

**Correct — descriptive name documents behavior:**
```typescript
test('should sum all item prices when calculating order total', () => {
    const result = calculateTotal([10, 20]);
    expect(result).toBe(30);
});
```



---

## References (4)

### Naming Conventions

# Test Naming Conventions

Descriptive test names that document expected behavior.

## Implementation

### Python (pytest)

```python
# Pattern: test_<action>_<condition>_<expected_result>

class TestUserRegistration:
    def test_creates_user_when_valid_email_provided(self):
        """Register with valid email succeeds."""
        ...

    def test_raises_validation_error_when_email_already_exists(self):
        """Duplicate email registration fails."""
        ...

    def test_sends_welcome_email_after_successful_registration(self):
        """New user receives welcome email."""
        ...

    def test_returns_none_when_user_not_found_by_id(self):
        """Missing user returns None, not exception."""
        ...


class TestOrderCalculation:
    def test_applies_bulk_discount_when_quantity_exceeds_threshold(self):
        ...

    def test_skips_discount_when_quantity_below_minimum(self):
        ...

    def test_calculates_tax_after_discount_applied(self):
        ...
```

### TypeScript (Vitest/Jest)

```typescript
describe('UserService', () => {
  // Pattern: should <expected_behavior> when <condition>
  test('should create user when valid email provided', () => {});
  test('should throw ValidationError when email already exists', () => {});
  test('should send welcome email after successful registration', () => {});
  test('should return null when user not found by id', () => {});
});

describe('OrderCalculation', () => {
  test('should apply bulk discount when quantity exceeds threshold', () => {});
  test('should skip discount when quantity below minimum', () => {});
  test('should calculate tax after discount applied', () => {});
});
```

## Anti-Patterns (Blocked)

```python
# BLOCKED - Not descriptive
def test_user():
def test_1():
def test_it_works():
def testUser():  # Wrong case

# BLOCKED - Tests implementation, not behavior
def test_calls_repository_save_method():
def test_uses_cache():
```

## Checklist

- [ ] Test name describes expected behavior, not implementation
- [ ] Condition/scenario is clear from the name
- [ ] Expected outcome is explicit
- [ ] Use snake_case for Python, camelCase for TypeScript
- [ ] Names are 3-10 words (not too short, not too long)
- [ ] Avoid generic words: test, check, verify (alone)

### Ork Delta

# ork delta: architecture-patterns

House decisions and scars rescued when the vendor-restatement reference files were
retired from src/skills/architecture-patterns (2026-07-31 wrap-plus-delta campaign).
Generic tutorials on these topics are upstream's job; see the "Upstream coverage
(do not restate)" table in SKILL.md. Only the rules below are ours.

## Convert domain exceptions to HTTP responses only at the router boundary
Why: House decision from consolidating backend-architecture-enforcer into architecture-patterns v2.0 (enforcer-skill consolidation; exact PR untraced). The fixed status map is EntityNotFoundError 404, UserAlreadyExistsError 409, InvalidStateError 422, BusinessRuleViolation 400, AuthorizationError 403, anything unmapped 500, registered once via `app.add_exception_handler(DomainException, domain_exception_handler)`. Keeping the map in one handler is what makes the "HTTPException blocked in services/" rule in `rules/backend-layers.md` mechanically enforceable.
Upstream: FastAPI error handling docs, https://fastapi.tiangolo.com/tutorial/handling-errors/ (context7: /tiangolo/fastapi)

## Flag the HTTPException import in services/, not just the raise
Why: Detection rule inherited from backend-architecture-enforcer (enforcer consolidation; PR untraced). Grepping only for `raise HTTPException` misses helper functions that wrap it; `from fastapi import HTTPException` inside services/ is already the violation. Companion greps: `db.add` / `db.execute` / `db.commit` / `db.query` / `session.add` in routers/, and `Service()` / `Repository()` instantiation inside route handlers. The blocking table lives in `rules/backend-layers.md`.
Upstream: FastAPI dependency docs, https://fastapi.tiangolo.com/tutorial/dependencies/ (context7: /tiangolo/fastapi)

## Clear FastAPI dependency overrides in fixture teardown, every time
Why: House testing convention from the enforcer era (enforcer consolidation; PR untraced): the test client fixture yields, then runs `app.dependency_overrides.clear()`. A leaked override silently rewires every later test in the session, which passes file-by-file locally and fails only as a full suite.
Upstream: FastAPI testing dependencies docs, https://fastapi.tiangolo.com/advanced/testing-dependencies/

## Enforce the barrel-file ban with ESLint, not review comments
Why: House blocking rule from project-structure-enforcer (enforcer consolidation; PR untraced). The working config is `no-restricted-imports` with `patterns: ['**/index']`; before the lint gate existed the ban regressed repeatedly in review. The rationale (tree-shaking failure, HMR slowdown, hidden import cycles) is summarized in `rules/structure-folders.md`.
Upstream: ESLint no-restricted-imports rule, https://eslint.org/docs/latest/rules/no-restricted-imports

## Keep test files under tests/ (or __tests__/), never co-located with source
Why: House decision from test-standards-enforcer (enforcer consolidation; PR untraced). Vendor defaults (Vitest, Jest, pytest) all permit co-location; OrchestKit blocks it so coverage omit globs and CI test discovery stay one-pattern simple. `src/**/*.test.ts` and `app/**/test_*.py` are violations; move them to `tests/unit/`, `tests/integration/`, or `__tests__/`.
Upstream: skill ork:testing-unit; pytest good practices, https://docs.pytest.org/en/stable/explanation/goodpractices.html


### Project Structure: Import Direction & Component Location — HIGH


# Import Direction & Component Location

Unidirectional import architecture, cross-feature prevention, and component/hook placement rules.

## Unidirectional Import Architecture

Code must flow in ONE direction. Lower layers never import from higher layers.

```
shared/lib  ->  components  ->  features  ->  app
(lowest)                                    (highest)
```

### Allowed Imports

| Layer | Can Import From |
|-------|-----------------|
| `shared/`, `lib/` | Nothing (base layer) |
| `utils/` | `shared/`, `lib/` |
| `components/` | `shared/`, `lib/`, `utils/` |
| `features/` | `shared/`, `lib/`, `components/`, `utils/` |
| `app/` | Everything above |

### Blocked Import Directions

```typescript
// BLOCKED: shared/ importing from features/
// File: src/shared/utils.ts
import { authConfig } from '@/features/auth/config';  // VIOLATION!

// BLOCKED: features/ importing from app/
// File: src/features/auth/useAuth.ts
import { RootLayout } from '@/app/layout';  // VIOLATION!

// BLOCKED: components/ importing from features/
// File: src/components/ui/UserAvatar.tsx
import { useCurrentUser } from '@/features/auth/hooks/useCurrentUser'; // VIOLATION!
```

### Cross-Feature Import Prevention

Features must not import from each other. Extract shared code to `shared/` or `lib/`.

```typescript
// BLOCKED: Cross-feature import
// File: src/features/auth/useAuth.ts
import { DashboardContext } from '@/features/dashboard/context';  // VIOLATION!
import { useCart } from '@/features/cart/hooks/useCart';          // VIOLATION!

// FIX: Extract to shared
// Move to: src/shared/types/user.ts
// Both features import from shared/
```

### Type-Only Import Exception

Type-only imports across features are allowed since they are erased at compile time:

```typescript
// ALLOWED: Type-only import from another feature
import type { User } from '@/features/users/types';
```

## Component Location Rules

### React Components (PascalCase .tsx)

```
ALLOWED:
  src/components/Button.tsx
  src/components/ui/Card.tsx
  src/features/auth/components/LoginForm.tsx
  src/app/dashboard/page.tsx

BLOCKED:
  src/utils/Button.tsx       # Components not in utils/
  src/services/Modal.tsx     # Components not in services/
  src/hooks/Dropdown.tsx     # Components not in hooks/
  src/lib/Avatar.tsx         # Components not in lib/
```

### Custom Hooks (useX pattern)

```
ALLOWED:
  src/hooks/useAuth.ts
  src/hooks/useLocalStorage.ts
  src/features/auth/hooks/useLogin.ts

BLOCKED:
  src/components/useAuth.ts   # Hooks not in components/
  src/utils/useDebounce.ts    # Hooks not in utils/
  src/services/useFetch.ts    # Hooks not in services/
```

## Import Direction Quick Reference

```
ALLOWED DIRECTIONS:
  shared/ -> (nothing)
  lib/    -> shared/
  utils/  -> shared/, lib/
  components/ -> shared/, lib/, utils/
  features/   -> shared/, lib/, utils/, components/
  app/        -> shared/, lib/, utils/, components/, features/

BLOCKED DIRECTIONS:
  shared/ -> components/, features/, app/
  lib/    -> components/, features/, app/
  components/ -> features/, app/
  features/ -> app/, other features/
```

## Fixing Import Direction Violations

### shared/ importing from features/

Extract the needed code to `shared/` where it belongs:

```typescript
// Before: shared/utils.ts imports features/auth/config
// After: Move config to shared/config/auth.ts
```

### features/ importing from app/

App layer should not export utilities. Move shared logic to appropriate lower layer:

```typescript
// Before: features/auth imports from app/layout
// After: Extract shared layout types to shared/types/layout.ts
```

### Cross-feature imports

Extract shared types and utilities:

```typescript
// Before: features/auth imports from features/users
// After:
//   1. Create src/shared/types/user.ts
//   2. Both features import from shared/
```

### components/ importing from features/

Component should receive data as props, not fetch it directly:

```typescript
// Before: components/UserAvatar imports useCurrentUser from features/auth
// After: UserAvatar receives user as prop, feature component provides it
```

## Why This Matters

- **Circular dependencies**: Bi-directional imports create runtime errors
- **Build failures**: Bundlers cannot resolve circular module graphs
- **Code splitting**: Circular deps prevent effective code splitting
- **Maintainability**: Tangled dependencies make refactoring impossible
- **Testing**: Cannot test components in isolation with circular deps


### Test Standards: Naming Conventions — MEDIUM


# Test Naming Conventions

Descriptive test names that document expected behavior for both Python and TypeScript.

## Python Naming Pattern

Pattern: `test_&lt;action&gt;_&lt;condition&gt;_&lt;expected_result&gt;`

```python
class TestUserRegistration:
    def test_creates_user_when_valid_email_provided(self):
        """Register with valid email succeeds."""
        ...

    def test_raises_validation_error_when_email_already_exists(self):
        """Duplicate email registration fails."""
        ...

    def test_sends_welcome_email_after_successful_registration(self):
        """New user receives welcome email."""
        ...

    def test_returns_none_when_user_not_found_by_id(self):
        """Missing user returns None, not exception."""
        ...


class TestOrderCalculation:
    def test_applies_bulk_discount_when_quantity_exceeds_threshold(self):
        ...

    def test_skips_discount_when_quantity_below_minimum(self):
        ...

    def test_calculates_tax_after_discount_applied(self):
        ...
```

## TypeScript Naming Pattern

Pattern: `should &lt;expected_behavior&gt; when &lt;condition&gt;`

```typescript
describe('UserService', () => {
  test('should create user when valid email provided', () => {});
  test('should throw ValidationError when email already exists', () => {});
  test('should send welcome email after successful registration', () => {});
  test('should return null when user not found by id', () => {});
});

describe('OrderCalculation', () => {
  test('should apply bulk discount when quantity exceeds threshold', () => {});
  test('should skip discount when quantity below minimum', () => {});
  test('should calculate tax after discount applied', () => {});
});
```

## Blocked Naming Patterns

```python
# BLOCKED - Not descriptive
def test_user():           # What about user?
def test_1():              # Meaningless number
def test_it_works():       # What works?
def testUser():            # Wrong case (camelCase)

# BLOCKED - Tests implementation, not behavior
def test_calls_repository_save_method():   # Implementation detail
def test_uses_cache():                     # Implementation detail
```

```typescript
// BLOCKED - Not descriptive
test('test1', () => {});           // Meaningless
test('works', () => {});           // What works?
test('test', () => {});            // Says nothing
it('test', () => {});              // Says nothing
```

## Naming Checklist

- Test name describes expected behavior, not implementation
- Condition/scenario is clear from the name
- Expected outcome is explicit
- Use `snake_case` for Python, descriptive strings for TypeScript
- Names are 3-10 words (not too short, not too long)
- Avoid generic words alone: test, check, verify

## Describe Block Naming (TypeScript)

```typescript
// GOOD - Named after the unit being tested
describe('UserService', () => {
  describe('createUser', () => {
    test('should hash password before saving', () => {});
    test('should throw when email is taken', () => {});
  });

  describe('deleteUser', () => {
    test('should soft delete by setting is_active to false', () => {});
    test('should throw when user not found', () => {});
  });
});

// BAD - Vague describe blocks
describe('tests', () => {
  describe('user', () => {
    test('test1', () => {});
    test('test2', () => {});
  });
});
```

## Test Class Naming (Python)

```python
# GOOD - Named after the feature/unit
class TestUserRegistration:
    ...

class TestOrderCalculation:
    ...

class TestPaymentProcessing:
    ...

# BAD - Vague class names
class TestUtils:
    ...

class TestMisc:
    ...
```



---

## Checklists (1)

### Solid Checklist

# SOLID Principles Checklist

Use this checklist when designing or reviewing code architecture.

## Single Responsibility Principle (SRP)

- [ ] Each class has only ONE reason to change
- [ ] Class names clearly describe their single purpose
- [ ] Methods within a class are cohesive (all relate to same responsibility)
- [ ] No "Manager", "Handler", "Processor" suffix (often indicates multiple responsibilities)
- [ ] Services don't mix business logic with infrastructure concerns

**Red Flags:**
- Class imports from many unrelated modules
- Methods that don't use most class attributes
- Class has > 200 lines (usually)
- Changes to unrelated features require modifying same class

## Open/Closed Principle (OCP)

- [ ] New behavior added via new classes, not modifying existing ones
- [ ] Using Protocols/ABCs for extension points
- [ ] Strategy pattern for varying algorithms
- [ ] No switch statements on type (use polymorphism)
- [ ] Configuration over code for variation

**Red Flags:**
- Growing if/elif chains checking types
- Methods that need modification for each new feature
- Direct instantiation of concrete classes in business logic

## Liskov Substitution Principle (LSP)

- [ ] Subclasses don't strengthen preconditions (method requirements)
- [ ] Subclasses don't weaken postconditions (what method guarantees)
- [ ] Subclasses don't throw unexpected exceptions
- [ ] All Protocol methods implemented with compatible signatures
- [ ] Tests pass with any implementation of a Protocol

**Red Flags:**
- Subclass overrides method to throw NotImplementedError
- Subclass returns different types than base
- Code checks isinstance before calling methods
- Subclass ignores/overrides parent behavior unexpectedly

## Interface Segregation Principle (ISP)

- [ ] Protocols are small and focused (3-5 methods max)
- [ ] Clients don't depend on methods they don't use
- [ ] No "god interfaces" with many unrelated methods
- [ ] Role-based interfaces (IReadable, IWritable) vs. object-based
- [ ] Composition of small interfaces over large monolithic ones

**Red Flags:**
- Implementations that stub out methods with `pass` or `raise`
- Protocols with > 10 methods
- Classes implement interface but use only subset of methods
- Interface named after implementation, not capability

## Dependency Inversion Principle (DIP)

- [ ] High-level modules don't import from low-level modules
- [ ] Both depend on abstractions (Protocols)
- [ ] Abstractions don't depend on details
- [ ] Dependencies injected, not created internally
- [ ] Domain layer has zero infrastructure imports

**Red Flags:**
- `import` from infrastructure in domain/application layer
- Direct instantiation with `SomeService()` in business logic
- Hardcoded database connections, file paths, URLs
- Tests require actual database/network

## Architecture Review Checklist

### Layer Independence

- [ ] Domain layer: Zero imports from other layers
- [ ] Application layer: Imports only from Domain
- [ ] Infrastructure layer: Implements ports from Application
- [ ] API layer: Translates DTOs ↔ Domain objects

### Dependency Injection

- [ ] All dependencies passed via constructor
- [ ] No global state or singletons in business logic
- [ ] FastAPI `Depends()` used for wiring
- [ ] Test doubles easily substitutable

### Domain Purity

- [ ] Entities use dataclasses, not ORM models
- [ ] No framework imports in domain
- [ ] Value objects are immutable (`frozen=True`)
- [ ] Domain logic has no side effects (I/O)

### Testability

- [ ] Unit tests need no mocks (domain layer)
- [ ] Integration tests mock only external boundaries
- [ ] No database needed for domain logic tests
- [ ] Fast test execution (&lt; 1 second for unit tests)

## Quick Reference

| Principle | Ask Yourself |
|-----------|--------------|
| SRP | "What is the ONE thing this class does?" |
| OCP | "Can I add new behavior without changing this code?" |
| LSP | "Can any implementation replace another safely?" |
| ISP | "Does this interface expose only what clients need?" |
| DIP | "Does this module depend on abstractions?" |

## When to Refactor

1. **Adding feature requires modifying core classes** → Extract interface, use OCP
2. **Test setup is complex** → Apply DIP, inject dependencies
3. **Class is growing large** → Apply SRP, extract classes
4. **Subclass behaves differently** → Check LSP, maybe use composition
5. **Implementing interface partially** → Apply ISP, split interface
