Enforce FastAPI Clean Architecture with BLOCKING validation.
Architecture Overview
+-------------------------------------------------------------------+
| ROUTERS LAYER |
| HTTP concerns only: request parsing, response formatting |
| Files: router_*.py, routes_*.py, api_*.py |
+-------------------------------------------------------------------+
| SERVICES LAYER |
| Business logic: orchestration, validation, transformations |
| Files: *_service.py |
+-------------------------------------------------------------------+
| REPOSITORIES LAYER |
| Data access: database queries, external API calls |
| Files: *_repository.py, *_repo.py |
+-------------------------------------------------------------------+
| MODELS LAYER |
| Data structures: SQLAlchemy models, Pydantic schemas |
| Files: *_model.py (ORM), *_schema.py (Pydantic) |
+-------------------------------------------------------------------+
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 |
| File Naming | Must follow naming convention | all |
File Naming Conventions
Quick Reference
| Layer | Allowed Patterns | Blocked Patterns |
|---|
| Routers | , , , | , |
| Services | | , , |
| Repositories | , | , |
| Schemas | , , , | , |
| Models | , , , | , |
Layer Separation Summary
Routers (HTTP Only)
- Request parsing and response formatting
- HTTP status codes and auth checks
- Delegate to services via
Services (Business Logic)
- Validation and orchestration
- Data transformations
- Raise domain exceptions (NOT HTTPException)
Repositories (Data Access)
- Database queries and persistence
- External API calls
- Return domain objects or None
Dependency Injection Quick Reference
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 | , in routers/ | Move to repository |
| HTTPException in service | in services/ | Use domain exceptions |
| Direct instantiation | without Depends | Use |
| Wrong naming | Missing suffix/prefix | Rename per convention |
| Sync in async | Missing | Add or use executor |
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")
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)
References
For detailed patterns and examples, see:
| Reference | Content |
|---|
| layer-rules.md | Detailed layer separation rules with code examples |
| dependency-injection.md | DI patterns, authentication, testing with overrides |
| violation-examples.md | Common violations with proper patterns and auto-fix suggestions |
Related Skills
- - DDD patterns
- - Advanced FastAPI patterns
- - DI patterns
project-structure-enforcer
- Folder structure
Capability Details
layer-separation
Keywords: router, service, repository, layer, clean architecture, separation
Solves:
- Prevent database operations in routers
- Block business logic in route handlers
- Ensure proper layer boundaries
dependency-injection
Keywords: depends, dependency injection, DI, fastapi depends, inject
Solves:
- Enforce use of FastAPI Depends() pattern
- Block direct instantiation in routers
- Ensure testable code structure
file-naming
Keywords: naming convention, file name, router_, _service, _repository
Solves:
- Enforce consistent file naming patterns
- Validate router/service/repository naming
- Maintain codebase consistency
async-patterns
Keywords: async, await, sync, blocking call, asyncio
Solves:
- Detect sync calls in async functions
- Prevent blocking operations in async code
- Ensure async consistency