Defines coding conventions, error handling patterns, async practices, logging standards, API response formats, and testing workflows for the Order Service — a FastAPI/Python microservice. Covers typed exception classes with FastAPI exception handler registration, structlog structured logging, Pydantic request/response models, and pytest-asyncio endpoint tests. Use when writing or modifying order endpoints, implementing order processing logic, adding error handling, or working with any file matching app/**/*.py or tests/**/*.py in the Order Service.
app/
main.py - FastAPI app, exception handlers
orders.py - Order endpoints
models.py - Pydantic request/response models
tests/
conftest.py - Shared fixtures (client, sample_order)
test_*.py - Async endpoint tests
Use typed exception classes. Never use bare try/except at the endpoint level.
Define exceptions per domain:
class OrderNotFoundError(Exception):
def __init__(self, order_id: str):
self.order_id = order_id
super().__init__(f"Order {order_id} not found")
Register exception handlers on the FastAPI app in main.py:
from fastapi.responses import JSONResponse
@app.exception_handler(OrderNotFoundError)
async def handle_order_not_found(request, exc):
return JSONResponse(
status_code=404,
content={
"error": "OrderNotFound",
"order_id": exc.order_id,
"message": str(exc),
},
)
In endpoints, raise the exception and let the handler deal with it:
if order_id not in _orders:
raise OrderNotFoundError(order_id)
{
"error": "ErrorTypeName",
"order_id": "the-resource-id",
"message": "Human-readable description"
}
All endpoint handlers are async. All I/O operations use async/await. Never use synchronous blocking calls inside an endpoint.
External service calls (database, third-party APIs) must be awaited.
Use structlog for all logging. Never use print().
import structlog
logger = structlog.get_logger()
logger.info("order_confirmed", order_id=order_id)
logger.error("order_not_found", order_id=order_id)
All successful mutation endpoints return the resource ID and new status:
return {"order_id": order_id, "status": "confirmed"}
Include additional fields when the endpoint triggers side effects.
pytest with pytest-asyncio (asyncio_mode = auto)httpx.AsyncClient with ASGITransport for endpoint testsclient and sample_order fixtures from conftest.pyunittest.mock.AsyncMockclient and sample_order fixtures from conftest.py.AsyncMock before invoking the endpoint.AsyncClient.AsyncMock vs MagicMock), then re-run until all assertions pass.Run tests:
python -m pytest tests/ -v
Architectural decisions are stored in .memory/decisions/:
.memory/decisions/
YYYY-MM-DD-short-description.md
Each file:
# Decision: [short title]
Date: YYYY-MM-DD
Status: accepted
## Context
[Why this decision was needed]
## Decision
[What was decided]
## Consequences
[What this means for the codebase]