- Separate commands (write operations) and queries (read operations) into distinct modules and handlers.
- Commands mutate state. Queries return data. Never mix side effects with data retrieval.
- Use separate read and write functions — query functions return lightweight dicts/dataclasses, command functions modify domain aggregates.
- Structure as `commands/create_user.py`, `queries/get_user.py` within each domain module.
- Use a simple mediator or direct handler calls — avoid over-engineering with heavy CQRS frameworks.
- Optimize queries independently: use raw SQL, denormalized views, or caching for read-heavy paths.
- Validate command input with Pydantic before processing. Commands carry validated, typed data.