Apply functional core/imperative shell architecture and Pydantic patterns to Python code. Use when designing new code, discussing architecture, structuring modules, or when the user asks about code organization. Activates for phrases like "how should I structure", "design", "architect", or when writing significant new Python code.
Separate code into two distinct layers:
project/
├── handlers/ # Ports (HTTP, CLI, GraphQL)
├── infrastructure/ # Adapters (DB, email, external APIs)
└── domain/ # Functional core (business logic)
from pydantic import BaseModel, Field
class Event(BaseModel, frozen=True):
"""Immutable value object with validation."""
id: str | None = None
name: str = Field(min_length=1, max_length=100)
start_time: datetime
max_attendees: int | None = Field(default=None, ge=1)
class CreateEventRequest(BaseModel):
"""HTTP/CLI input validation."""
name: str = Field(min_length=1, max_length=100)
start_time: datetime
def to_domain(self) -> Event:
return Event(name=self.name, start_time=self.start_time)
from collections.abc import MutableMapping
class InMemoryEvents(MutableMapping[str, Event]):
def __init__(self) -> None:
self._data: dict[str, Event] = {}
def __getitem__(self, key: str) -> Event:
return self._data[key]
def __setitem__(self, key: str, value: Event) -> None:
self._data[key] = value
# ... implement other MutableMapping methods
from typing import Protocol
class EmailService(Protocol):
def send(self, to: str, subject: str, body: str) -> None: ...
Handlers only:
No business logic in handlers.
@app.post("/events")
async def create_event(
request: CreateEventRequest,
app: EventApplication = Depends(get_app)
) -> dict[str, str]:
event_id = app.create_event(
name=request.name,
start_time=request.start_time
)
return {"event_id": event_id}
Handlers (Ports) → Domain (Core) ← Infrastructure (Adapters)
↓ ↓ ↓
HTTP/CLI Pure functions Database/APIs
Transform I/O Business logic External systems
list[str] not List[str], str | None not Optional[str]-> None)pyproject.toml