Design by
contract and CQS
This paper is published under the terms and conditions in the
footnote.
In the OOP world, Bertrand Meyer proposed two patterns for object-oriented program
design.
Neither pattern applies well to a
world of coarse-grained loosely-coupled components in client-server and
distributed systems.
Contents
Design by contract v. Defensive design
Command
Query Separation v. Transactions that read and reply
Command
Query Separation (CQS)
Transactions
that read and reply
Meyer
proposed software should be written using this principle, meaning that:
·
Every operation that can be invoked is defined by a contract, including the
preconditions for its success.
·
Every component that invokes an operation will guarantee the preconditions
of that operation.
This is OK where components are tightly-coupled, as is
normal within a single program, running on one machine.
But the trend for decades has been towards increasingly
loosely-coupled (client-server and distributed) computing.
Design by contract is unrealistic in this world, since client components cannot guarantee all preconditions required for
operations on remote components.
And server-side
components, designed for reuse, cannot trust all clients (sending inputs
through different channels) will check the same preconditions.
So, the opposite "defensive design" approach is
taken.
This means that a server component tests that the
preconditions for an operation hold true.
It can
·
test some conditions
by inspecting the values of fields in the input Command message, then
·
test other
conditions by accessing the system state
Typically, the former conditions are tested on
the client side before invocation, and the latter on the server side.
In most business applications, code on the client device checks data entry fields for
·
missing values,
·
out of range checks
(age < 0)
·
mismatches with pre-defined enumerated values (Mr, Mrs, Dr…)
·
mismatches with pre-defined regular expressions.
The UI
does not send a Command message to the server-side unless and until the message
passes basic checks.
The
top-most tier on the server side usually repeats some or all of the tests made
by clients, including security checks.
Then,
server-side components perform the required business logic, accessing stored
data as need be.
During
this process, further preconditions may be found not to hold, meaning the
server-side processing cannot proceed to a successful conclusion.
If a
server-side component cannot succeed, it usually replies (directly or indirectly) to the client
with a suitable error message.
Sometimes,
invalid Commands get past a defensive design, and stored data is left in a
state that is inconsistent with business rules.
Consider
a requirement for usernames in new registrations on a website to be unique.
The application that processes the Commands cannot (without impractical
database locking) prevent duplicate usernames being registered.
In such a
case, validation can be done later, and a compensating
transaction can fix up that rare situation where duplicate usernames are
registered at the same time.
Meyer proposed one operation (on an object) should either change/write some data or get/read some data or but not do both at the same time.
The idea was that this should make systems easier to program, to test and understand.
Using the Design by Contract pattern, a server component has no reason to reject a Command, and no need to reply with a failure message.
Structured design methods (c1980) distinguished database update transactions from database queries.
· The queries contain nothing but read operations.
· The updates are transactions that apply business rules and maintain the integrity/consistency of stored data.
That separation appears similar to the Command/Query Separation pattern, but is different.
An update transaction is a coarse-grained operation; it may invoke read-only operations to access data it does not update.
E.g. The Place Order transaction may invoke operations that read the customer’s credit limit and the product stock balance.
And it may return the retrieved values to the Command sender, along with a success or failure error message.
A Command-triggered transaction usually reports its success or failure, with appropriate error messages, whether to its client or another party.
There is a relationship between Meyer’s Design by Contract and Command/Query Separation patterns.
Design by Contract means a server component has no reason to reject a Command or reply with a failure message.
So, if a client wants to know what effect a Command has produced, then it must follow up by sending a Query.
But neither of Meyer’s two patterns applies well to a world of loosely-coupled client-server and distributed computing.
Server-side components must be designed to defend themselves against being sent Command messages that are incorrectly formatted.
And update transactions triggered by Command messages sent to the server-side will often not only change data, but also read data and return data.
Footnote: Creative Commons Attribution-No Derivative Works Licence
2.0 20/04/2015 16:27
Attribution: You may copy, distribute and display this copyrighted work
only if you clearly credit “Avancier Limited: http://avancier.website” before
the start and include this footnote at the end.
No Derivative Works: You may copy, distribute, display only complete and verbatim
copies of this page, not derivative works based upon it.
For more information about the
licence, see http://creativecommons.org