Commands can be found every where once one knows what to look for. Often commands are hidden from sight and we can produce better designs if we simply take a step back to formally define the commands in our system.
What is a command?
The original "command pattern" is from GoF design patterns and unfortunately is a very specific object-orientated pattern.
Now a days when people say "command", they tend to mean the C in CQRS.
Comparison of "GoF command pattern" vs CQRS command
We shall define a command as an immutable object with a name and arguments. Commands are sent over a command bus. Commands are routed to command handlers.
A concrete example is JSON object (command) sent over the network (command bus) and routed to express middleware (command handler).
Commands in products and services
An user performs a series of commands to achieve their objective.
create team (name)
Product management define epics and stories which can be broken down into commands user perform.
Commands in frontends
An user interface implemented by a framework such as React, performs a series of commands to update component state and DOM.
render react component to dom (component, domElement)
update team name in react component (name)
create team (name)
State management libraries might have a different name for commands. For example, Redux calls them Action.
Browser automation tools such as Puppeteer is a series of commands to control a Chrome instance.
Modern graphics APIs such as WebGPU allow developers to directly create a batch of commands and send them to the graphics hardware.
On the other hand, commands are annoying implicit in some event driven frameworks such as Cycle.
Commands in databases
A database such as MongoDB offers an API which is a series of commands to insert, find, update, and delete documents.
insert (collection, document)
find (collection, query)
The database itself is backed by a series of commands to the filesystem. The filesystem is persisted to disk which a series of NVMe commands to the physical drive. The solid state drive controller implements a series of flash chip commands.
Commands in testing
Testing patterns such as Arrange, Act and Assert are a series of commands.
- Arrange is a series of commands to put the system under test into a given state
- Act is the single command under test
- Assert is a series of commands to check the system is in the expected state
Ditto for Cucumber Given, When, Then. By formalizing a production/service into a series of commands, one can easily do Behaviour-driven development.
Essence of commands
Commands are every where, but what can we say about commands generally? Commands have many interesting properties.
- Commands are also called action, message, function call, request.
- Command buses are also called queue, message bus, message broker, network, point to point, event loop, scheduler, router.
- Command handlers are also called function, middleware, actor, listener, subscriber, watcher, reducer, service, lambda, business logic.
- Commands can be grouped together in batches (also called command buffers).
- Command handlers can create and submit more commands.
- Command handlers that don’t have side effects are equivalent to pure functions. This means the command handler is idempotent and the command is referentially transparent to command handler.
- Command and command handlers are space-time independent as the command bus acts an queue (and queues are space-time decouplers).
- This means execution of commands can be synchronous or asynchronous.
- Commands can fail. Since commands are often just plain objects, anything can construct any command. Thus a command handler needs to ensure a command is well formed and the caller is authorized.
- Command buses can be unreliable. For example if the command bus is a network. This means commands,
- …can be lost.
- …can be delayed.
- …can be order independent to other commands.
- …can be duplicated.
- …can be retried.
- …can be safely retried with a deduplication identifier.
- Commands are not events but are closely related. Commands are input/desire/intent/request. Events are output/what happen/facts/result/response.
- Commands are not the same as request/response. Commands can be one-way and the thing that submitted the command doesn’t expect any response. For example, logging (command) is a side-effect and often the caller is unable to handle a logger failures. Logging is often fire and forget.
Do you see everything in terms of commands? We like to hear from you! Battlefy is hiring.