home / skills / yonatangross / orchestkit / backend-architecture-enforcer
/plugins/ork/skills/backend-architecture-enforcer
This skill enforces FastAPI Clean Architecture with blocking validation across routers, services, and repositories to ensure proper layer separation.
npx playbooks add skill yonatangross/orchestkit --skill backend-architecture-enforcerReview the files below or copy the command above to add this skill to your agents.
---
name: backend-architecture-enforcer
description: Enforces FastAPI Clean Architecture with blocking validation. Use when implementing router-service-repository patterns, enforcing layer separation, or validating dependency injection in backend code.
context: fork
agent: backend-system-architect
version: 1.0.0
author: OrchestKit AI Agent Hub
tags: [backend, fastapi, architecture, enforcement, blocking, clean-architecture, di]
user-invocable: false
---
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** | `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` |
## Layer Separation Summary
### Routers (HTTP Only)
- Request parsing and response formatting
- HTTP status codes and auth checks
- Delegate to services via `Depends()`
### 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 | `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)` |
| Wrong naming | Missing suffix/prefix | Rename per convention |
| Sync in async | Missing `await` | Add `await` 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](references/layer-rules.md) | Detailed layer separation rules with code examples |
| [dependency-injection.md](references/dependency-injection.md) | DI patterns, authentication, testing with overrides |
| [violation-examples.md](references/violation-examples.md) | Common violations with proper patterns and auto-fix suggestions |
## Related Skills
- `clean-architecture` - DDD patterns
- `fastapi-advanced` - Advanced FastAPI patterns
- `dependency-injection` - 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 consistencyThis skill enforces a FastAPI Clean Architecture with blocking validation to keep routers, services, repositories, and models strictly separated. It detects and blocks violations like DB usage in routers, HTTPExceptions in services, direct instantiation instead of Depends(), incorrect file naming, and sync calls inside async code. Use it to ensure maintainable, testable, and consistent backend codebases.
The enforcer scans project files and applies layer-specific rules and file-naming patterns. It flags blocking violations (preventing merges or CI passes) such as database operations in router files, raising HTTPException in service code, direct construction of services or repositories, and missing await in async functions. It also validates DI usage by checking for Depends() providers and verifies naming conventions for each layer.
What happens if a service needs to signal an HTTP error?
Raise a domain exception in the service and convert it to HTTPException in the router layer.
How do I inject a repository or service into a router?
Expose provider functions (get_* returning the instance) and use FastAPI Depends() in the router signature.