Pattern-004: CQRS-lite Pattern

Status

Proven

Context

Mixing read and write operations in the same service classes creates performance bottlenecks and complexity. When query operations have different optimization requirements than command operations, a unified approach becomes inefficient. The CQRS-lite Pattern addresses:

Pattern Description

CQRS-lite Pattern separates read operations from write operations, providing optimized paths for simple data retrieval without the full complexity of CQRS event sourcing.

Core concept:

Implementation

Query Side Structure

class QueryRouter:
    """Routes query intents to appropriate services"""

    def __init__(self, project_repository: ProjectRepository):
        self.project_queries = ProjectQueryService(project_repository)

    async def route_query(self, intent: Intent) -> QueryResult:
        if intent.action == "list_projects":
            projects = await self.project_queries.list_active_projects()
            return QueryResult(
                success=True,
                data={"projects": [p.to_dict() for p in projects]}
            )
        elif intent.action == "get_project":
            project = await self.project_queries.get_project(intent.project_id)
            return QueryResult(
                success=True,
                data={"project": project.to_dict() if project else None}
            )
        # Route other queries...

class ProjectQueryService:
    """Handles project-related queries"""

    def __init__(self, repository: ProjectRepository):
        self.repo = repository

    async def list_active_projects(self) -> List[Project]:
        """Optimized read-only project listing"""
        return await self.repo.list_active_projects()

    async def get_project(self, project_id: str) -> Optional[Project]:
        """Simple project retrieval"""
        return await self.repo.get_by_id(project_id)

Command Side Structure

class WorkflowEngine:
    """Handles write operations (commands)"""

    def __init__(self, project_repo: ProjectRepository, workflow_factory: WorkflowFactory):
        self.project_repo = project_repo
        self.workflow_factory = workflow_factory

    async def execute_command(self, intent: Intent) -> WorkflowResult:
        """Process write operations through workflows"""
        workflow = await self.workflow_factory.create_from_intent(intent)
        if workflow:
            return await workflow.execute()
        else:
            raise UnsupportedCommandError("No workflow found for command")

Integration Pattern

class IntentProcessor:
    """Main processor that routes to query or command side"""

    def __init__(self, query_router: QueryRouter, workflow_engine: WorkflowEngine):
        self.query_router = query_router
        self.workflow_engine = workflow_engine

    async def process_intent(self, intent: Intent) -> Union[QueryResult, WorkflowResult]:
        """Route intent to appropriate side"""
        if self._is_query(intent):
            return await self.query_router.route_query(intent)
        else:
            return await self.workflow_engine.execute_command(intent)

    def _is_query(self, intent: Intent) -> bool:
        """Determine if intent is a query or command"""
        query_actions = ["list_projects", "get_project", "search", "status"]
        return intent.action in query_actions

Usage Guidelines

Benefits

Trade-offs

References

Migration Notes

Consolidated from: