plugins/tech-lead/skills/architecture-patterns/SKILL.md
# Architecture Patterns System-level architecture patterns for Python backend projects (FastAPI + SQLAlchemy 2.0). ## Triggers Use this skill when the user: - Designs a new system or major feature - Asks about project structure or module boundaries - Makes data modeling decisions - Designs API contracts - Evaluates architectural trade-offs - Creates or reviews Architecture Decision Records (ADR) - Needs to modernize legacy code ## Abstraction Levels | Plugin | Level | Focus | |--------|----
npx skillsauth add ruslan-korneev/claude-plugins plugins/tech-lead/skills/architecture-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
System-level architecture patterns for Python backend projects (FastAPI + SQLAlchemy 2.0).
Use this skill when the user:
| Plugin | Level | Focus | |--------|-------|-------| | software-architect | System | Architecture, modules, ADR | | fastapi-scaffold | Module | Boilerplate generation | | clean-code | Code | SOLID, code smells |
# Good — clear module boundaries
src/
├── core/ # Infrastructure (config, db, exceptions)
├── modules/
│ ├── users/ # User domain — fully autonomous
│ ├── orders/ # Order domain — depends on users via interfaces
│ └── payments/ # Payment domain — depends on orders via interfaces
└── main.py
# Bad — tangled dependencies
src/
├── models.py # All models in one file
├── services.py # All services mixed
├── routes.py # All routes together
└── utils.py # "Utils" — the graveyard of architecture
# Good — stateless service, state in external storage
class OrderService:
def __init__(self, cache: CacheProtocol, db: AsyncSession):
self._cache = cache # Redis
self._db = db # PostgreSQL
async def get_order(self, order_id: int) -> Order:
# Check cache first
cached = await self._cache.get(f"order:{order_id}")
if cached:
return Order.model_validate_json(cached)
# Fallback to DB
return await self._repository.get_by_id(order_id)
# Bad — state in memory
class OrderService:
_orders_cache: dict[int, Order] = {} # Lost on restart!
# Good — dependencies via constructor (easy to test)
class UserService:
def __init__(
self,
repository: UserRepositoryProtocol,
email_sender: EmailSenderProtocol,
):
self._repository = repository
self._email_sender = email_sender
# Test with mocks
async def test_create_user():
mock_repo = Mock(spec=UserRepositoryProtocol)
mock_email = Mock(spec=EmailSenderProtocol)
service = UserService(mock_repo, mock_email)
# ...
# Bad — hidden dependencies
class UserService:
def create_user(self, data: UserCreate) -> User:
from app.email import send_email # Hidden import!
from app.db import get_session # Hidden dependency!
# Good — validation at every layer
# Router layer
@router.post("/orders")
async def create_order(
data: OrderCreateDTO, # Pydantic validation
current_user: Annotated[User, Depends(get_current_user)], # Auth
) -> OrderReadDTO:
# Authorization check
if not current_user.can_create_orders:
raise ForbiddenError("User cannot create orders")
return await service.create(data, current_user.id)
# Service layer
class OrderService:
async def create(self, data: OrderCreateDTO, user_id: int) -> Order:
# Business rules validation
if data.total > MAX_ORDER_AMOUNT:
raise ValidationError("Order amount exceeds limit")
# ...
# Repository layer — parameterized queries only
async def get_by_id(self, order_id: int) -> Order | None:
stmt = select(Order).where(Order.id == order_id) # No f-strings!
result = await self._session.execute(stmt)
return result.scalar_one_or_none()
# Good — selective loading, caching
class OrderRepository:
async def get_with_items(self, order_id: int) -> Order:
stmt = (
select(Order)
.options(selectinload(Order.items)) # Eager load
.where(Order.id == order_id)
)
return (await self._session.execute(stmt)).scalar_one()
async def get_list(
self,
offset: int = 0,
limit: int = 20,
) -> Sequence[Order]:
stmt = select(Order).offset(offset).limit(limit) # Pagination
return (await self._session.execute(stmt)).scalars().all()
# Bad — N+1 queries, no pagination
async def get_all_with_items(self) -> list[Order]:
orders = await self._session.execute(select(Order))
for order in orders:
_ = order.items # N+1 query for each order!
return orders.scalars().all()
More details: ${CLAUDE_PLUGIN_ROOT}/skills/architecture-patterns/references/layered-architecture.md
┌─────────────────────────────────────────┐
│ Presentation Layer │
│ (Routers, DTOs, OpenAPI) │
├─────────────────────────────────────────┤
│ Application Layer │
│ (Services, Use Cases, Events) │
├─────────────────────────────────────────┤
│ Domain Layer │
│ (Entities, Value Objects, Rules) │
├─────────────────────────────────────────┤
│ Infrastructure Layer │
│ (Repositories, External Services) │
└─────────────────────────────────────────┘
Dependency Rule: Dependencies point inward. Inner layers don't know about outer layers.
from abc import abstractmethod
from typing import Protocol, Generic, TypeVar
T = TypeVar("T")
class RepositoryProtocol(Protocol[T]):
"""Abstract repository interface."""
@abstractmethod
async def get_by_id(self, id: int) -> T | None: ...
@abstractmethod
async def save(self, entity: T) -> T: ...
@abstractmethod
async def delete(self, id: int) -> None: ...
class SQLAlchemyRepository(RepositoryProtocol[T]):
"""Concrete implementation for SQLAlchemy."""
def __init__(self, session: AsyncSession, model: type[T]):
self._session = session
self._model = model
async def get_by_id(self, id: int) -> T | None:
return await self._session.get(self._model, id)
from dataclasses import dataclass
from typing import Callable, Awaitable
@dataclass
class DomainEvent:
"""Base domain event."""
pass
@dataclass
class UserCreated(DomainEvent):
user_id: int
email: str
class EventBus:
def __init__(self):
self._handlers: dict[type[DomainEvent], list[Callable]] = {}
def subscribe(
self,
event_type: type[DomainEvent],
handler: Callable[[DomainEvent], Awaitable[None]],
) -> None:
self._handlers.setdefault(event_type, []).append(handler)
async def publish(self, event: DomainEvent) -> None:
for handler in self._handlers.get(type(event), []):
await handler(event)
# Usage
event_bus = EventBus()
event_bus.subscribe(UserCreated, send_welcome_email)
event_bus.subscribe(UserCreated, create_default_settings)
# In service
await event_bus.publish(UserCreated(user_id=user.id, email=user.email))
# Commands — write operations
class CreateOrderCommand:
user_id: int
items: list[OrderItemDTO]
class CommandHandler:
async def handle(self, command: CreateOrderCommand) -> int:
# Complex write logic with validation
order = Order(user_id=command.user_id)
for item in command.items:
order.add_item(item)
await self._repository.save(order)
return order.id
# Queries — read operations (can use different DB, denormalized views)
class OrderQueryService:
async def get_order_summary(self, order_id: int) -> OrderSummaryDTO:
# Optimized read query, possibly from read replica
return await self._read_repository.get_summary(order_id)
More details: ${CLAUDE_PLUGIN_ROOT}/skills/architecture-patterns/references/anti-patterns.md
| Anti-Pattern | Description | Solution | |--------------|-------------|----------| | Big Ball of Mud | No structure, everything depends on everything | Modular architecture | | Golden Hammer | Using one technology for everything | Choose right tool for the job | | Tight Coupling | Direct dependencies between modules | Interfaces, DI | | God Object | One class knows and does everything | Single Responsibility | | Circular Dependencies | A → B → C → A | Dependency Inversion |
/architect:design <name> — interactive architecture design for new project/module/architect:modernize [path] — legacy analysis + phased modernization plan/architect:review [path] — architecture analysis (structure, dependencies, patterns)/architect:adr <title> — create Architecture Decision Record/architect:diagram <type> — generate Mermaid diagram (component, data-flow, sequence, er, deployment)/architect:deps [path] — dependency analysis, detect circular dependenciestools
# Code Review Skill This skill should be used when the user asks for "code review", "review my changes", "review this PR", "check my code", "pre-merge review", "review diff", or mentions reviewing code quality, implementation correctness, or preparing changes for merge. ## Overview Code review following the **Review Pyramid** methodology — a prioritized approach that focuses on what matters most. ## The Review Pyramid ``` ▲ /|\ 5. Code Style (Nit) ← Least important,
tools
# Python Typing Patterns Python type annotation patterns without `type: ignore`. Always the correct solution. ## Triggers Use this skill when the user: - Gets mypy/pyright errors - Asks about Python type annotations - Wants to add type hints - Works with generics, protocols, TypeVar ## Main Principle: NEVER type: ignore Every type error has a correct solution. `type: ignore` is: - Masking potential bugs - Disabling type checking - Technical debt More details: `${CLAUDE_PLUGIN_ROOT}/skills/
tools
# Ruff Patterns Knowledge about the ruff linter and patterns for solving errors. **ZERO noqa policy** — always look for the proper solution. ## Triggers Use this skill when the user: - Asks "how to configure ruff" - Gets a ruff error and wants to understand how to fix it - Wants to migrate from black/isort/flake8/pylint - Asks about a specific rule (E501, F401, etc.) ## Main Principle: NEVER USE NOQA **Every ruff error has a proper solution.** Using `# noqa` is: - Hiding the problem, not so
tools
# Pytest Patterns Quality testing patterns with pytest for FastAPI projects. **TDD-first approach**. ## Triggers Use this skill when the user: - Wants to write tests - Asks about pytest, fixtures, mocks - Wants to set up testing for FastAPI - Uses TDD approach ## Main Principle: TDD 1. **Red** — write a failing test 2. **Green** — write minimal code 3. **Refactor** — improve while keeping tests green ## What Makes a Good Test ### 1. Clear Name ```python # ✅ Good — describes what, when, a