CQS (Command Query Separation) is a well known principle for disentangling method responsibilities in OO software.
I had known it for quite some time, but only recently actually have taken it to heart. And now I’m really loving it. I cannot imagine doing without it. It’s a great guidance when designing single classes or whole service processes.
And then there is CQRS which builds on CQS. I like that, too. It’s been a great inspiration.
But then I felt the need to move beyond it: CQS does not cover the „message reality“ I’m encountering. And CQRS does deliver the flexibility I expected from giving up „the single model“.
Code structure guided by CQS
To distinguish between Commands and Queries is a great guidance for structuring code. It’s a form of outside-in design, I’d say: You’re looking at the interactions of users (or more generally: the environment) with a system by sending/receiving messages and from that derive a certain structure.
Example: A player makes a move in a Tic Tac Toe game application.
Technically that means a user sends a message to the application with data about the move and receives back a message telling her the new game state.
Or more specifically this is a request for a certain behavior expressed in a response plus a change to some internal state.
The frontend first is used as an editor for the request message. A player certainly does not want to type in a JSON document but rather would like to just click on a cell on some visual game board.
And then the frontend is used as a projector for the response message. A player certainly does not want to interpret a JSON document but rather would like to see the game state rendered as a visual game board.
The domain or game logic itself is encapsulated in the backend with which the frontend is conversing only through messages. Frontend and backend don’t should share state, in my view.
So far so normal. A request is transformed into a state change plus a response. All the necessary logic is a big lump.
However, when applying the CQS to this, the request-response message flow can be dissected into a command followed by a query:
- First the command orders the backend to execute the move; the internal state of the backend is changed. This order of course is only carried out if the move is valid and the state’s consistency is not compromised.
- Then the query asks the backend for a description of its internal state.
What has been a single lump of logic now is two smaller lumps of logic. How cool is that? And all this without much creativity. The user’s requirement guided the design. Software development just needs to listen.
The tangible outcomes of a CQS-guided analysis are:
- A command message (data structure)
- A query result message (data structure)
- A query message (data structure; maybe only for internal use)
- A command status message (data structure; maybe only for internal use)
- A command handler (function)
- A query handler (function)
- A request handler (function; optional for hiding the command and query handler from the frontend for less traffic between frontend and backend)
I like this almost mechanically generated basic structure for my source code. It makes me focus more on the conversation with the user/customer because it’s from there I gain valuable first insights about the software design.
Message structure freed up by Event-Orientation
This is, what CQS does already. It’s a great help, but still leaves something to be desired.
What I found quite difficult for a long time was „message design“. How should messages look like, what structure should they have? How should this structure look like compared to the internal domain model?
CQS does not have an answer to that. CQRS on the other hand suggested to take it easier: there could be two models, one for commands and another one for queries.
But still I felt compelled to find the one model for handling commands, and the other one model for satisfying queries.
It took me a while to let go of that restriction.
Today, with Event-Orientation (EO) I don’t feel any restriction anymore. And I don’t feel I need to get „it“ right anymore „once and for all“.
The internal state of an application is not kept in a single (or two or three) data models anymore. I allow myself to program „model free“ or „schemaless“.
The model is dead! Long live the model multitude!
Each and every message is handled individually and is supported by the best model I can think of for that single purpose. With EO I feel free to let message structures be preliminary. They can be honed for consumption by the frontend; the backend then is a true service by catering to the needs of its client. Or message structures can be more palatable for the backend; the frontend then is more of a „slave“ to the provider of the core application behavior, the backend.
I remember how discussions about message structure took up quite some time during modelling sessions. Not anymore. Because there is no question anymore about how much a message model might couple frontend and backend.
There simply is no domain model anymore in the backend. There cannot be any coupling. Whatever a message carries is a projection or something that needs projection. It’s not some form of copy or part of a domain model.
Message structures are freed up by EO to look how they want. The internal state of an EO-based backend is something completely different: a stream of events.
To change the state through a command it has to be translated into a collection of events.
And to query the state the events in the stream have to be aggregated and projected into a result.
The proposition of EO is that such translations and aggregations/projections are all separate. By default they don’t share anything. No coupling, no dependency between them - except for the one event stream.
That makes message processing in both directions cheap. And it allows for independent development. EO slices an application not only into services but into message processing pipelines each catering to just a single message.
Deciding today that the TicTacToe game state query result should look like this, and tomorrow changing the decision to let it look like that is comparatively easy. It has no impact on some comprehensive domain model relevant to many messages.
The contract between message pipelines are just event data types. They are the core representation of the domain. Look at the events and you know what the domain is about. Events are telling a story about what is happening, what is deemed important, what the causal sequences are.
And this can be interpreted in a million ways, or at least as many ways as there are incoming message types to handle.
Notifications as ambient messages
CQS primarily distinguishes two kinds of messages: commands and queries.
That’s incoming messages. How about outgoing ones? I’d like to make explicit also two respective outgoing messages: command status and query result. Command status messages might be quite simple and generic (success vs failure), but query results surely are of very different form and need careful design.
However, in my experience there is a fifth message type. It’s particular because it’s not a request. Commands and queries stand for bidirectional message flow: a client sends a request (command or query) and expects a response (command status or query result).
Sometimes, though, messages are not produced with a certain expectation for a behavior of another (part of) a system. Sometimes messages are just „utterances“ of a system broadcast „to whom it may concern“.
I call such messages notifications.
Usually the name for them is „event“ with a prefix or suffix (like „domain event“). But that clashes with the data stored in an event stream inside of a system. I don’t like such homonymes.
„Notification“ is a term everyone is familiar with: we are notified about emails arriving in our inboxes or new messages in a Slack channel or of an overdue backup etc.
A lot of applications are broadcasting notifications to inform us about something that has happened/changed. And we are free to subscribe to such notifications. Some we are interested in, some not.
If we receive a notification we might react, eg. open the mail client to check our inbox. But the broadcasting application did not request such a reaction.
Notifications are one way messages. Software broadcasts them through some channel/medium as outgoing notifications. And other software might subscribe to a channel/medium to receive notifications from other software as incoming messages.
I call notifications ambient messages because they „float around in the environment“ and are not directed. They have a source, but no destination.
From that follows there is no guarantee that a notification will be handled at all. Notifications thus cannot carry requests. Their purpose solely is to inform about something that happened. They make a statement about the originating system - for others to possibly care for.
Notifications might be more relevant in distributed systems. Nevertheless I think they are a very general concept and thus should be included in the CQS principle. Hence I propose to expand it to:
CQNS: Command Query Notification Separation principle
The form of notifications is considerably different (being one-way messages). And their processing is different, too, I think.
- Notifications do not query state, because there would be no receiver for a query result.
- Notifications do not change state; that’s what commands are for.
- The single responsibility of incoming notifications is to cause commands to be executed inside a receiver. However the command status messages generated by the triggered commands are not delivered to the originator of the notification.
So much for incoming notifications.
But where do outgoing notifications come from?
- Outgoing notifications are generated during command processing. Only commands change state, and notifications are signalling state change.
Notifications help to further structure code from the outside-in:
- Handling incoming notifications requires logic to translate them into commands.
- Command processing consists not only of
- model generation,
- command validation,
- event generation,
- but also of generating outgoing notifications.
That, to me, makes requirements analysis yet easier and delivers more input for further software design.
In a gaming scenario there could be two services: one for playing a game like TicTacToe and one for keeping a score board.
Both services could be connected in a request/response manner, for example, for registering the score of a finished game: the TicTacToe service could send a command to the score board service to inform it of another finished game and who has won. But why couple the services so tightly? The TicTacToe service would now depend on another service, it could not do its job without the other.
Instead a notification can be used to more loosely couple both service. With a notification they would not even know of each other.
- The TicTacToe service would broadcast a notification when a game finishes, e.g. by writing it to a queue hosted by some infrastructure.
- The score board service would subscribe to „game over“ notifications arriving in a queue hosted by some infrastructure.
One service’s outgoing notification is another service’s incoming notification.
Anatomy of a backend
To me the beauty of CQNS + EO is a very straightforward and generic basic software structure. And in that it’s easy to independently focus on (comparatively) small functional units.
Message-oriented requirements analysis
Designing software is helped by applying this pattern. But also analysis is helped because it’s guided by the message hierarchy:
- Request (outgoing) -> Response (incoming)
- Command -> Command Status
- Query -> Query Result
- Incoming: Notification -> Commands
- Request (outgoing) -> Response (incoming)
- Event (internal)
My approach to software development is outside-in: I start at the surface of a software system and ask „How do users want to interact with the system?“ Requests with their responses are the primary answer to that.
Next I ask to what the software system should react in addition. Incoming notifications are the answer to that.
Then from command requests I derive the internal events to be stored in the event stream.
Next I try to balance the events found with what query requests might deem helpful to get from the event stream.
(If you’re a fan of Event Storming you might prefer to start with events and work your way „up“ to incoming/outgoing messages. That’s fine for me, too. That’s serving the same goal. If you find it easier that way around, why not?)
Finally I’m concerned with outgoing notifications generated by command request processing.
What I’m not really interested in anymore, though, is „the one“ internal domain data structure. I’m not trying to figure out how the domain can be represented in a comprehensive data model.
For a long, long that had been my primary concern. I was fixated on „the one“ domain data model. I wanted to represented „the world“ in software.
But those days are gone now!
Coloring the design blueprint mandala
Once I’ve gotten the messages down I can work on them one by one. It’s like eating a pizza slice by slice.
Each slice being a message handling pipeline. And the pizza crust being a tranceiver which receives incoming messages from the environment and sends/broadcasts outgoing messages to the environment.
The structure of all the pipeline slices is the same; each consists of three segments (and is attached to the event store at the center):
So for each message I can design the following step by step:
- Define the message context, i.e. all the relevant events
- Design the message context model
- Design how the message context model should be loaded and queried before message processing
- Design how the message should be processed in light of the message context model
- Design how to generate events from the message in light of the message context model
- Design how to generate outgoing notifications from the message
- Design how to generate the query result from the message context model in light of the message
- Design how to generate commands from the message in light of the message context model
- Design how the message context model should be updated
To me that feels very guided, almost like a meditative activity (if not a boring one). It’s like coloring a mandala area by area…
Refining the CQS to CQNS and being grounded in Event-Orientation really helps me progressing through software development in a very straightforward manner.
Sure, there are still challenges to master. Sure, especially processing messages still can be tough.
With CQNS + EO, however, I’m relieved of a lot of confusing stuff around that. I find it much, much easier to focus on the important things.
- I’m not wasting energy on asking myself „How to start development?“
- I’m not wasting energy on „getting the one model right“.
- I’m not wasting energy on designing structures which should not differ from software to software anyway.
Yes, I believe there is a basic anatomy to all software. Like there is a basic anatomy to all animals. And that basic anatomy is more fine grained than the usual architectural pattern suggest. And it still leaves room and freedom for all the incredibly complicated stuff you have to implement, don’t worry.
I’d even say: By giving software a basic anatomy we’re set free to focus on the really important things much more. CQNS and EO, to me, are part of that anatomical blueprint.