Command-Query Separation for Business Logic Entry Points
Goal
Apply Command-Query Separation at entry points to business logic.
Treat each business-logic entry point as exactly one of these:
- a command, which changes state
- a query, which returns data without changing state
Do not mix the two in the same entry point.
This skill governs CQS, not CQRS. It is a design rule for the behavior and return shape of individual business-logic entry points. It does not require separate read and write models, separate stores, separate services, or distributed architecture changes.
The only allowed exception to the no-data-return rule for commands is a create operation. A create command may return the ID of the created domain entity, or the IDs if the create operation creates more than one domain entity, so the caller can continue acting on those newly created entities.
What Counts as In Scope
Apply this skill to code that does one or more of these things:
- defines a public function or method that starts a business workflow
- defines a use case, application service method, interactor, handler, facade, or similar business-logic entry point
- changes the return shape of a business operation
- combines reading and writing behavior in one business-facing entry point
- exposes command or query behavior to callers of business logic
CQS Rule
Classify every business-logic entry point as either a command or a query.
- A command changes business state.
- A query returns business data.
- Do not let one entry point do both.
Commands must not return business data.
- Use
void, unit, undefined, None, or the closest equivalent when the stack allows it.
- If the project uses result wrappers or error envelopes, keep those patterns without adding read-model payloads to commands.
Create commands may return created entity IDs only.
- A create command may return the ID of the created domain entity.
- If one create operation creates multiple domain entities, it may return the set or collection of created IDs.
- Do not return the full created entity, a read model, derived business data, or other additional payload from a create command.
Queries must not change business state.
- Do not persist writes, emit business-changing side effects, transition entity state, or trigger state-changing workflows inside a query.
- Keep queries observational.
Split mixed entry points instead of compromising the rule.
- If one entry point both changes state and returns data, separate it into a command and a query, or move the extra read to a follow-up query by the caller.
Detection Workflow
Find the business-logic entry points first.
- Identify public functions, methods, handlers, or use cases that callers invoke to trigger business behavior.
- Focus on the boundary where a caller asks the business logic to do something or to answer something.
Determine whether the entry point changes state.
- Check whether it creates, updates, deletes, approves, assigns, schedules, cancels, or otherwise changes domain or persisted state.
- Check whether it triggers business side effects that are part of changing state.
Determine whether the entry point returns data.
- Identify whether it returns domain entities, read models, DTOs, counts, lists, booleans, summaries, or other business-facing results.
- If it both changes state and returns data, treat that as a CQS violation unless it is returning only created IDs from a create operation.
Prefer semantic classification to naming.
- Do not trust names like
get, create, handle, or execute by themselves.
- Classify by actual behavior and return shape.
Writing or Changing Business-Logic Entry Points
Choose the role first.
- Decide whether the entry point is a command or a query before writing the code.
- Let that choice determine side effects and return type.
Write commands as state-changing operations with minimal return values.
- Return no business data from commands.
- For create commands, return only the created entity ID or IDs.
Write queries as read-only operations.
- Return the requested business data.
- Keep them free of business-state mutations.
Refactor mixed entry points by separation.
- Move write behavior into a command entry point.
- Move data retrieval into a query entry point.
- Let callers perform the query after the command when they need additional data beyond created IDs.
Keep the boundary explicit.
- Make the command or query role obvious in the method behavior, return type, and surrounding API.
- Do not hide writes inside a supposedly read-only entry point.
Review Questions
When reading or reviewing code, ask:
- Is this entry point a command or a query?
- Does it change business state?
- Does it return business data?
- If it is a command, is it returning anything other than created entity IDs for a create operation?
- If it is a query, is it truly free of business-state changes?
If the answer is yes, apply this skill.
Report the Outcome
When finishing the task:
- state which business-logic entry points were identified or changed
- state which ones are commands and which ones are queries
- state where mixed command-query behavior was removed or prevented
- state whether any create commands return created entity IDs, and only those IDs
1---2name: business-logic-entry-point-command-query-separation3description: Enforce Command-Query Separation specifically at business-logic entry points. Use when an agent needs to create, modify, review, or interpret public functions, methods, handlers, use cases, application services, facades, or similar entry points that trigger business logic. Classify each entry point as either a command that changes state or a query that returns data, never both. The only allowed data return from a command is the ID or IDs of domain entities created by a create operation.4---56# Command-Query Separation for Business Logic Entry Points78## Goal910Apply Command-Query Separation at entry points to business logic.1112Treat each business-logic entry point as exactly one of these:1314- a command, which changes state15- a query, which returns data without changing state1617Do not mix the two in the same entry point.1819This skill governs CQS, not CQRS. It is a design rule for the behavior and return shape of individual business-logic entry points. It does not require separate read and write models, separate stores, separate services, or distributed architecture changes.2021The only allowed exception to the no-data-return rule for commands is a create operation. A create command may return the ID of the created domain entity, or the IDs if the create operation creates more than one domain entity, so the caller can continue acting on those newly created entities.2223## What Counts as In Scope2425Apply this skill to code that does one or more of these things:2627- defines a public function or method that starts a business workflow28- defines a use case, application service method, interactor, handler, facade, or similar business-logic entry point29- changes the return shape of a business operation30- combines reading and writing behavior in one business-facing entry point31- exposes command or query behavior to callers of business logic3233## CQS Rule34351. Classify every business-logic entry point as either a command or a query.36 - A command changes business state.37 - A query returns business data.38 - Do not let one entry point do both.39402. Commands must not return business data.41 - Use `void`, `unit`, `undefined`, `None`, or the closest equivalent when the stack allows it.42 - If the project uses result wrappers or error envelopes, keep those patterns without adding read-model payloads to commands.43443. Create commands may return created entity IDs only.45 - A create command may return the ID of the created domain entity.46 - If one create operation creates multiple domain entities, it may return the set or collection of created IDs.47 - Do not return the full created entity, a read model, derived business data, or other additional payload from a create command.48494. Queries must not change business state.50 - Do not persist writes, emit business-changing side effects, transition entity state, or trigger state-changing workflows inside a query.51 - Keep queries observational.52535. Split mixed entry points instead of compromising the rule.54 - If one entry point both changes state and returns data, separate it into a command and a query, or move the extra read to a follow-up query by the caller.5556## Detection Workflow57581. Find the business-logic entry points first.59 - Identify public functions, methods, handlers, or use cases that callers invoke to trigger business behavior.60 - Focus on the boundary where a caller asks the business logic to do something or to answer something.61622. Determine whether the entry point changes state.63 - Check whether it creates, updates, deletes, approves, assigns, schedules, cancels, or otherwise changes domain or persisted state.64 - Check whether it triggers business side effects that are part of changing state.65663. Determine whether the entry point returns data.67 - Identify whether it returns domain entities, read models, DTOs, counts, lists, booleans, summaries, or other business-facing results.68 - If it both changes state and returns data, treat that as a CQS violation unless it is returning only created IDs from a create operation.69704. Prefer semantic classification to naming.71 - Do not trust names like `get`, `create`, `handle`, or `execute` by themselves.72 - Classify by actual behavior and return shape.7374## Writing or Changing Business-Logic Entry Points75761. Choose the role first.77 - Decide whether the entry point is a command or a query before writing the code.78 - Let that choice determine side effects and return type.79802. Write commands as state-changing operations with minimal return values.81 - Return no business data from commands.82 - For create commands, return only the created entity ID or IDs.83843. Write queries as read-only operations.85 - Return the requested business data.86 - Keep them free of business-state mutations.87884. Refactor mixed entry points by separation.89 - Move write behavior into a command entry point.90 - Move data retrieval into a query entry point.91 - Let callers perform the query after the command when they need additional data beyond created IDs.92935. Keep the boundary explicit.94 - Make the command or query role obvious in the method behavior, return type, and surrounding API.95 - Do not hide writes inside a supposedly read-only entry point.9697## Review Questions9899When reading or reviewing code, ask:100101- Is this entry point a command or a query?102- Does it change business state?103- Does it return business data?104- If it is a command, is it returning anything other than created entity IDs for a create operation?105- If it is a query, is it truly free of business-state changes?106107If the answer is yes, apply this skill.108109## Report the Outcome110111When finishing the task:112113- state which business-logic entry points were identified or changed114- state which ones are commands and which ones are queries115- state where mixed command-query behavior was removed or prevented116- state whether any create commands return created entity IDs, and only those IDs