---
name: clean-architecture-rules
description: Clean Architecture layer rules, dependency injection patterns, and violation checklist for Python/FastAPI backend projects
---

# Clean Architecture Rules

이 Skill은 Clean Architecture 준수 여부를 검증하기 위한 체크리스트입니다.

## Layer Dependencies (MUST FOLLOW)

```
API Layer → Service Layer → Repository Layer → Database
```

- 역방향 의존성 **절대 금지**
- 각 레이어는 바로 아래 레이어만 호출 가능

---

## API Layer Rules (`src/**/api/**/*.py`)

### ✅ Must Do
- Services injected via `Depends()`, not manually instantiated
- Return type wrapped in `StandardResponse`
- Proper HTTP exception handling
- Input validation via Pydantic schemas

### ❌ Must NOT Do
- NO Repository imports or direct calls
- NO business logic (calculations, validations, transformations)
- NO direct database access
- NO hardcoded values

### Example Violation
```python
# ❌ BAD: Business logic in API layer
@router.post("/users")
async def create_user(data: UserCreate):
    if data.age < 18:  # Business logic!
        raise HTTPException(400, "Must be 18+")
    
# ✅ GOOD: Delegate to service
@router.post("/users")
async def create_user(
    data: UserCreate,
    service: UserService = Depends(get_user_service)
):
    return await service.create_user(data)
```

---

## Service Layer Rules (`src/**/services/**/*.py`)

### ✅ Must Do
- Public methods return Pydantic BaseModel or SQLAlchemy models
- Business logic properly encapsulated
- Other services injected via DI, not manual instantiation
- Transaction management

### ❌ Must NOT Do
- NEVER return `Dict[str, Any]` from public methods
- NO direct API/HTTP concerns
- NO raw SQL queries (use Repository)

### Example Violation
```python
# ❌ BAD: Returning Dict
async def get_user_stats(self, user_id: int) -> Dict[str, Any]:
    return {"total": 10, "active": 5}

# ✅ GOOD: Return Pydantic model
async def get_user_stats(self, user_id: int) -> UserStatsResponse:
    return UserStatsResponse(total=10, active=5)
```

---

## Repository Layer Rules (`src/**/repositories/**/*.py`)

### ✅ Must Do
- CRUD operations only
- Return SQLAlchemy models only
- Use parameterized queries

### ❌ Must NOT Do
- NO business logic
- NO Service layer imports
- NO calls to other repositories
- NO data transformation

---

## Models & Schemas

### Models (`src/**/models/**/*.py`)
- SQLAlchemy 2.0+: `Mapped` types, `mapped_column`
- No business logic
- Use `TYPE_CHECKING` for circular imports

### Schemas (`src/**/schemas/**/*.py`)
- Pydantic v2: `ConfigDict`, `field_validator`, `model_validate`
- Response schemas have `from_attributes=True`
- No business logic

---

## Dependency Injection Pattern

```python
# ✅ Correct DI pattern
def get_user_service(
    repo: UserRepository = Depends(get_user_repository),
    cache: CacheService = Depends(get_cache_service)
) -> UserService:
    return UserService(repo, cache)

@router.get("/users/{id}")
async def get_user(
    id: int,
    service: UserService = Depends(get_user_service)
):
    return await service.get_by_id(id)
```

---

## Severity Levels

| Violation | Severity |
|-----------|----------|
| API layer imports Repository | 🔴 CRITICAL |
| Service returns Dict | 🔴 CRITICAL |
| Business logic in API layer | 🔴 CRITICAL |
| Business logic in Repository | 🔴 CRITICAL |
| Manual service instantiation | 🟡 WARNING |
| Missing type hints | 🟡 WARNING |
| Circular imports | 🔴 CRITICAL |
