From python-clean-architecture
This skill should be used when the user asks to "scaffold a FastAPI project", "set up clean architecture", "refactor to clean architecture", "add a new endpoint", "add a router", "add an operation", "add a repository", "review my code structure", "apply design patterns in Python", "decouple my code", "improve code quality", "make my code testable", or mentions layered architecture, dependency injection, Protocol-based design, or Pythonic design patterns for Python/FastAPI projects.
npx claudepluginhub mktoronto/python-clean-architecture --plugin python-clean-architectureThis skill uses the workspace's default tool permissions.
Provide Clean Architecture guidance for Python projects, specifically FastAPI APIs. Based on seven core design principles, Pythonic implementations of classic patterns, and a three-layer architecture (Routers → Operations → Database).
examples/fastapi-hotel-api/db/__init__.pyexamples/fastapi-hotel-api/db/database.pyexamples/fastapi-hotel-api/db/db_interface.pyexamples/fastapi-hotel-api/db/models.pyexamples/fastapi-hotel-api/main.pyexamples/fastapi-hotel-api/models/__init__.pyexamples/fastapi-hotel-api/models/booking.pyexamples/fastapi-hotel-api/models/customer.pyexamples/fastapi-hotel-api/models/room.pyexamples/fastapi-hotel-api/operations/__init__.pyexamples/fastapi-hotel-api/operations/booking.pyexamples/fastapi-hotel-api/operations/customer.pyexamples/fastapi-hotel-api/operations/interface.pyexamples/fastapi-hotel-api/operations/room.pyexamples/fastapi-hotel-api/routers/__init__.pyexamples/fastapi-hotel-api/routers/bookings.pyexamples/fastapi-hotel-api/routers/customers.pyexamples/fastapi-hotel-api/routers/rooms.pyexamples/fastapi-hotel-api/tests/__init__.pyexamples/fastapi-hotel-api/tests/test_bookings.pyProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Analyzes competition with Porter's Five Forces, Blue Ocean Strategy, and positioning maps to identify differentiation opportunities and market positioning for startups and pitches.
Provide Clean Architecture guidance for Python projects, specifically FastAPI APIs. Based on seven core design principles, Pythonic implementations of classic patterns, and a three-layer architecture (Routers → Operations → Database).
Attribution: The principles, patterns, and architectural approach in this skill are inspired by and synthesized from Arjan Codes' courses: The Software Designer Mindset, Pythonic Patterns, and Complete Extension. The specific Pythonic framing (Protocol-based DI, functional pattern progression, three-layer FastAPI architecture) originates from his teaching. This plugin distills those principles into actionable guidance for Claude Code — it is not a reproduction of course content. See also: github.com/arjancodes | youtube.com/arjancodes.
Every FastAPI project follows a strict three-layer dependency flow:
Routers (API layer) → Operations (business logic) → Database (persistence)
Each layer depends ONLY on the layer below it. Never skip layers.
Routers — HTTP interface. Accept requests, call operations, return responses. No business logic. Act as the composition root where concrete implementations are injected.
Operations — Business logic. Accept a DataInterface (Protocol) parameter for data access. Compute derived values, enforce rules, orchestrate workflows. Never import database modules directly.
Database — Persistence. Implement the DataInterface Protocol using SQLAlchemy, file storage, or any backend. Expose read_by_id, read_all, create, update, delete methods. Return DataObject = dict[str, Any] to decouple from ORM models.
from typing import Any, Protocol
DataObject = dict[str, Any]
class DataInterface(Protocol):
def read_by_id(self, id: str) -> DataObject: ...
def read_all(self) -> list[DataObject]: ...
def create(self, data: DataObject) -> DataObject: ...
def update(self, id: str, data: DataObject) -> DataObject: ...
def delete(self, id: str) -> None: ...
Operations accept data_interface: DataInterface as a parameter. The router passes the concrete implementation. This is dependency injection — no ABC inheritance needed.
Define separate Create and Read models per entity:
class CustomerCreate(BaseModel):
name: str
email: str
class Customer(BaseModel):
id: str
name: str
email: str
Use **data.model_dump() to unpack Pydantic models into DataObject dicts. Use exclude_none=True on update models for partial updates.
When writing or reviewing code, apply these principles in order of priority:
Protocol for structural typing and Callable type aliases for function injection. Patterns emerge from good abstraction.list[PaymentSource] patterns. Never use mixins.When encountering these code smells, apply the corresponding pattern:
| Code Smell | Pattern | Pythonic Implementation |
|---|---|---|
| Long if/elif switching behavior | Strategy | Callable type alias, pass functions as args |
| Need to create objects from config/JSON | Registry | dict[str, Callable] mapping + **kwargs unpacking |
| Event/notification side effects mixed with core logic | Notification (Pub/Sub) | subscribe(event, handler) / post_event(event, data) dict-based |
| Duplicated algorithm across classes | Template Method | Extract to free function + Protocol parameter |
| Two independent hierarchies that vary | Bridge | Callable type alias replaces abstract reference |
| Need undo/batch/queue operations | Command | Functions returning undo closures |
| Object creation coupled to usage | Abstract Factory | Tuples of functions + functools.partial |
| Sequential data transformations | Pipeline | functools.reduce for composition, or framework .pipe() |
| Need to react to events without coupling | Callback | Function passed as argument, called when event occurs |
| Reusing a function with a different interface | Function Wrapper | Calls another function, translates its arguments |
| Separating configuration from usage | Function Builder | Higher-order function returns a configured function |
| Bare primitives for domain concepts (prices, emails) | Value Objects | Subclass built-in types with __new__ validation, or frozen dataclass |
| Need audit trail, temporal queries, or event replay | Event Sourcing | Immutable Event[T], append-only EventStore[T], projection functions |
| Read/write patterns diverge; list views compute derived fields | CQRS | Separate write model + read projection, projector function after writes |
| Complex object with many optional parts | Builder | Fluent API with Self return type, .build() returns frozen product |
| Multiple DB writes that must succeed or fail together | Unit of Work | Context manager wrapping transaction: commit on success, rollback on error |
| Need exactly one instance of a shared resource | Singleton | Module-level instance (preferred), or metaclass with _instances dict |
| Object behaves differently depending on internal state | State | Protocol-based state objects, context delegates to current state |
| Incompatible interface from external library | Adapter | Protocol interface + functools.partial for single-method adaptation |
| Client coupled to complex subsystem details | Facade | Simplified interface class, functools.partial to bind dependencies |
| Transient failures in external API/DB calls | Retry | @retry decorator with exponential backoff, fallback strategies |
| Slow startup loading unused resources | Lazy Loading | functools.cache, TTL cache, generators, background preloading |
| Data access logic mixed into domain classes | Repository | Protocol interface for CRUD, concrete implementations per backend |
| Sequential operations on an object are verbose and error-prone | Fluent Interface | Methods return self for chaining, domain-specific verbs |
| Need extensibility without modifying core code | Plugin Architecture | Config-driven creation, importlib auto-discovery, self-registering modules |
functools.partial — to configure generic functions rather than creating wrapper classesWhen asked to create a new FastAPI project, generate this structure:
project_name/
├── main.py # FastAPI app, include_router, lifespan handler
├── routers/
│ ├── __init__.py
│ └── {entity}.py # APIRouter, endpoint functions
├── operations/
│ ├── __init__.py
│ ├── interface.py # DataInterface Protocol + DataInterfaceStub
│ └── {entity}.py # Business logic functions
├── db/
│ ├── __init__.py
│ ├── database.py # Engine, SessionLocal, Base
│ ├── db_interface.py # Generic DBInterface class
│ └── models.py # SQLAlchemy models
├── models/
│ ├── __init__.py
│ └── {entity}.py # Pydantic Create/Read models
├── tests/
│ └── test_{entity}.py # Tests using DataInterfaceStub
├── requirements.txt
└── .gitignore
Create a DataInterfaceStub base class that stores data in a plain dict. Override specific methods in test-specific subclasses. Pass the stub to operations functions — no database needed.
from typing import Any
DataObject = dict[str, Any]
class DataInterfaceStub:
def __init__(self):
self.data: dict[str, DataObject] = {}
def read_by_id(self, id: str) -> DataObject:
if id not in self.data:
raise KeyError(f"Not found: {id}")
return self.data[id]
def read_all(self) -> list[DataObject]:
return list(self.data.values())
def create(self, data: DataObject) -> DataObject:
self.data[data["id"]] = data
return data
def update(self, id: str, data: DataObject) -> DataObject:
if id not in self.data:
raise KeyError(f"Not found: {id}")
self.data[id].update(data)
return self.data[id]
def delete(self, id: str) -> None:
if id not in self.data:
raise KeyError(f"Not found: {id}")
del self.data[id]
For detailed guidance beyond this overview, consult:
Architecture & Design:
references/design-principles.md — Full treatment of the seven design principles with refactoring recipes and code examplesreferences/grasp-principles.md — GRASP principles: Creator, Information Expert, Controller, Low Coupling, High Cohesion, Polymorphism, Indirection, Protected Variations, Pure Fabricationreferences/domain-driven-design.md — Domain-Driven Design: domain models, ubiquitous language, model distillation, code as temporary expressionreferences/layered-architecture.md — Detailed three-layer architecture guide: DataInterface (Repository pattern), DBInterface, router composition, Pydantic models, to_dict utilityreferences/testable-api.md — Testing strategy: stub-based testing, DataInterfaceStub, test isolation, no-database testingreferences/testing-advanced.md — Pytest organization, property-based testing (Hypothesis), model-based stateful testing, code coverage philosophyreferences/rest-api-design.md — HTTP method semantics, status codes, resource naming, pagination, error response format, OpenAPI, versioningPython Fundamentals:
references/classes-and-dataclasses.md — When to use classes vs dataclasses, @dataclass, field(), frozen, encapsulationreferences/function-design.md — Pure functions, higher-order functions, closures, functools.partial, classes vs functions vs modulesreferences/data-structures.md — Choosing list vs dict vs tuple vs set, enums, performance trade-offsreferences/types-and-type-hints.md — Python's type system, Callable types, nominal vs structural typing, best practicesreferences/error-handling.md — Custom exceptions, context managers, error handling layers, anti-patternsreferences/code-quality.md — 22 code quality rules: naming, nesting, flags, type abuse, isinstance dispatch, overloaded classes, and code review checklistreferences/project-organization.md — Modules, packages, imports, folder structure, avoid "utils" anti-patternreferences/context-managers.md — Context manager protocol, __enter__/__exit__, @contextmanager, ExitStack, async context managersreferences/decorators.md — Decorator patterns: retry with backoff, logging, timing, functools.wraps, parameterized decoratorsreferences/async-patterns.md — Async/await for FastAPI: coroutines, asyncio.gather, TaskGroup, async context managers, async generators, async DataInterfacereferences/pydantic-validation.md — Pydantic v2 validators: @field_validator, @model_validator, Field() constraints, ConfigDict, serializers, special typesreferences/pattern-matching.md — Structural pattern matching (match/case): literal, capture, OR, sequence, class, mapping patterns, guard clausesPythonic Patterns:
references/pythonic-patterns.md — Quick reference lookup table for all 25 patterns (use for reviews and pattern selection)Pythonic Patterns (full progressions from OOP → functional):
references/patterns/strategy.md — Callable type alias, closures, functools.partialreferences/patterns/abstract-factory.md — Tuples of functions, partial, builder functionsreferences/patterns/bridge.md — Bound methods as callables, when to stop going functionalreferences/patterns/command.md — Functions returning undo closures, batch via list comprehensionreferences/patterns/notification.md — Observer, Mediator, and Pub/Sub with dict-based subscribe/post_eventreferences/patterns/registry.md — Dict mapping + **kwargs unpacking, self-registering plugins via importlibreferences/patterns/template-method.md — Free function + Protocol parameters, protocol segregationreferences/patterns/pipeline.md — Chain of Responsibility, functools.reduce composition, pandas .pipe()references/patterns/functional.md — Callback, Function Wrapper, Function Builder patternsreferences/patterns/retry.md — Exponential backoff, @retry decorator, fallback strategies, tenacity libraryreferences/patterns/lazy-loading.md — functools.cache, TTL cache, generators, background preloadingreferences/patterns/plugin-architecture.md — Config-driven plugins, importlib auto-discovery, self-registering modules, Protocol conformanceArchitectural & Domain Patterns:
references/patterns/repository.md — Repository pattern: separating data storage from data access, relationship to DataInterfacereferences/patterns/fluent-interface.md — Method chaining with return self, domain-specific verbs, when to use vs Builderreferences/patterns/value-objects.md — Wrapping primitives in validated domain types: Price, Percentage, EmailAddressreferences/patterns/event-sourcing.md — Immutable events, EventStore[T], projections, cache invalidationreferences/patterns/cqrs.md — Separate read/write models, command handlers, projector functionsreferences/patterns/builder.md — Fluent API with Self return type, mutable builder to immutable productreferences/patterns/unit-of-work.md — Transaction context managers, automatic rollback, repository compositionreferences/patterns/singleton.md — Module-level instance (preferred), metaclass approach, thread safetyreferences/patterns/state.md — Protocol-based state objects, context delegation, state transitionsreferences/patterns/adapter.md — Object adapter (composition), function adapter (partial), Protocol interfacereferences/patterns/facade.md — Simplified interface to complex subsystems, partial for controller bindingAdvanced Error Handling:
references/monadic-error-handling.md — Railway-oriented programming: Result types, @safe decorator, flow()/bind() composition (functional alternative to exceptions)examples/fastapi-hotel-api/ — Complete working FastAPI project demonstrating all patterns: rooms and bookings with computed pricesThe example code applies the same architectural principles taught in the Arjan Codes course but modernizes several implementation details. Each file documents its specific upgrades in its module docstring. Summary of deliberate changes:
| Category | Transcript (original) | Example (upgraded) | Rationale |
|---|---|---|---|
| IDs | Integer, autoincrement | String UUID (uuid.uuid4()) | App-generated, portable across databases |
| SQLAlchemy | Legacy 1.x (declarative_base(), session.query()) | 2.0 style (DeclarativeBase, session.get(), select()) | Current best practice, forward-compatible |
| Pydantic models | @dataclass + raw dicts | BaseModel with Create/Update/Read variants | FastAPI native, automatic validation & serialization |
| Startup | @app.on_event("startup") | lifespan context manager | on_event is deprecated in modern FastAPI |
| Test framework | unittest.TestCase | pytest functions | Simpler, more Pythonic, industry standard |
| Stub pattern | NotImplementedError base, subclass per test | Full in-memory dict implementation | Directly usable without boilerplate subclasses |
| Error handling | None (silent None returns) | KeyError + HTTPException(404) | Production-ready error responses |
| Router config | Bare APIRouter() | prefix=, tags=, response_model, status codes | OpenAPI docs, proper HTTP semantics |
| Customer model | first_name/last_name/email_address | name/email | Simplified for example clarity |
| Table names | Singular ("room") | Plural ("rooms") | SQL naming convention |
| Session mgmt | Global db_session in DBInterface | Injected via constructor parameter | Better dependency injection |
| Package | hotel.* top-level package | Flat imports | Standalone example clarity |
The core architecture (three-layer separation, DataInterface Protocol, dependency injection via router composition root, operations decoupled from database) is faithfully preserved.