This articleneeds additional citations forverification.(February 2008) |
Command-query separation(CQS) is a principle ofimperativecomputer programming.It was devised byBertrand Meyeras part of his pioneering work on theEiffel programming language.
It states that everymethodshould either be acommandthat performs an action, or aquerythat returns data to the caller, but not both. In other words,asking a question should not change the answer.[1]More formally, methods should return a value only if they arereferentially transparentand hence possess noside effects.
Connection with design by contract
editCommand-query separation is particularly well suited to adesign by contract(DbC) methodology, in which the design of aprogramis expressed asassertionsembedded in thesource code,describing thestateof the program at certain critical times. In DbC, assertions are considered design annotations—not program logic—and as such, their execution should not affect the program state. CQS is beneficial to DbC because any value-returning method (any query) can be called by any assertion without fear of modifying program state.
In theoretical terms, this establishes a measure of sanity, whereby one can reason about a program's state without simultaneously modifying that state. In practical terms, CQS allows all assertion checks to be bypassed in a working system to improve its performance without inadvertently modifying its behaviour. CQS may also prevent the occurrence of certain kinds ofheisenbugs.
Broader impact on software engineering
editEven beyond the connection with design by contract, CQS is considered by its adherents to have a simplifying effect on a program, making its states (via queries) and state changes (via commands) more comprehensible.[citation needed]
CQS is well-suited to theobject-orientedmethodology, but can also be applied outside of object-oriented programming. Since the separation of side effects and return values is not inherently object-oriented, CQS can be profitably applied to any programming paradigm that requires reasoning about side effects.[citation needed]
Command Query Responsibility Segregation
editCommand query responsibility segregation(CQRS)generalises CQS to services, at the architectures level: it applies the CQS principle by using separateQueryandCommandinterfaces and usually data models toretrieveandmodifydata, respectively.[2][3]
Other architectural patterns
edit- As we move away from a single representation that we interact with viaCRUD,we can easily move to a task-based UI.
- CQRS fits well with event-based programming models. It's common to see a CQRS system split into separate services communicating with Event Collaboration. This allows these services to easily take advantage ofEvent Driven Architecture.
- Having separate models raises questions about how hard it is to keep those models consistent, which raises the likelihood of using eventual consistency.
- For many domains, much of the logic required is needed when you're updating, so it may make sense to use Eager Read Derivation to simplify your query-side models.
- If the write model generates events for all updates, you can structure read models as Event Posters, allowing them to be Memory Images and thus avoiding a lot of database interactions.
- CQRS is suited to complex domains, the kind that also benefits fromDomain-Driven Design.[3]
Limitations
editCQS can introduce complexities for implementingreentrantandmultithreadedsoftware correctly. This usually occurs when a non-thread-safe pattern is used to implement the command-query separation.
Here is a simple example that does not follow CQS, but is useful for multi-threaded software because it solves the complexity of locking for all other parts of the program, but by doing so it doesn't follow CQS because the function both mutates state and returns it:
privateintx;
publicintincrementAndReturnX(){
lockx;// by some mechanism
x=x+1;
intx_copy=x;
unlockx;// by some mechanism
returnx_copy;
}
Here is a CQS-compliant version. Note that it is safely usable only in single-threaded applications. In a multithreaded program, there is a race condition in the caller, between whereincrement()
andvalue()
would be called:
privateintx;
publicintvalue(){
returnx;
}
voidincrement(){
x=x+1;
}
Even in single-threaded programs, it is sometimes arguably significantly more convenient to have a method that is a combined query and command.Martin Fowlercites thepop()
method of astackas an example.[4]
See also
editReferences
edit- ^Meyer, Bertrand."Eiffel: a language for software engineering"(PDF).p. 22.Retrieved16 December2014.
- ^Young, Greg."CQRS Documents"(PDF).Retrieved2012-12-28.
- ^abFowler, Martin."CQRS".Retrieved2011-07-14.
- ^Fowler, Martin."CommandQuerySeparation".Retrieved5 December2005.
Further reading
edit- Meyer, Bertrand(September 1994) [1988].Object-oriented Software Construction.Prentice Hall.ISBN0-13-629049-3.