Filling the gaps in CQRS and Event Sourced systems: taxonomy of system components

In this post I look at different types of components one encounters in systems that make use of CQRS and Event Sourcing.

Image by cdd20.

Introduction

Just as it takes time for a speck of fish spawn to mature into a fully-grown fish, so we need time for everything that develops and crystallizes in our world of ideas.
Alvar Aalto, Finnish architect and designer.

In this post I would like to propose a taxonomy for the CQRS and Event Sourced system components. We will do so by enumerating different types of components present in complex, distributed systems and providing definitions and examples for each. We will also examine the applicability of certain more esoteric components, not often encountered in regular projects.

Here I attempted, as much as possible, to keep the post restricted to definition of the components. This should serve as a foundation for further study of the interaction between the software system and the environment. After we cover the fundamental components it will be easier to think about and design large systems with heterogeneous components, all within the framework of Event Sourcing and CQRS.

Here we assume the reader is familiar with the notions of Command Query Responsibility Segregation (CQRS), Event Sourcing and Domain Driven Design (DDD). Please see resources such as cqrs.nu for a detailed explanation.

Most people only work with a few types of components, like the process manager, aggregate root and a projection. Are these the only component types that exist? If not, what other types are out there, and is there an exhaustive list?

The three common components

How many Prolog programmers does it take to change a lightbulb? Yes.

Let us start with a typical collection of components that we encounter when we build a CQRS and Event Sourced systems. First we have the Aggregate Root.

Aggregate Root (AR)

This system component is responsible for ensuring a consistency boundary around some group of objects, or a sub-state of the system. The reason we worry about aggregate roots but not any old aggregates, is because aggregate roots are the only points of contact with the outside world.

From a perspective of a CQRS system, the aggregate root should accept or reject commands. Regardless of the result, the state protected by aggregate root should remain consistent.

Since the system developed with CQRS is of eventually consistent nature, we have as output a, possibly empty, set of events. These events, in turn, are stored in event store.

Let us now examine the aggregate root from the level of the whole system in which it participates: it receives as input a command and produces as output a set of events. We might depict this relation between inputs and outputs as follows:

Table 1: Aggregate Root (AR) acts as a relation between commands and events.

Process Manager (PM)

The next component we will look at is, surprisingly, not a Projection, but a Process Manager. This is because the Process Manager is, conceptually, easier to understand.

We all understand the notion of a “process”, and if we don’t the dictionary does a really good job of explaining the meaning of words:

proc·ess
a series of actions or steps taken in order to achieve a particular end.

So it must be that a process manager must cause a series of actions to happen. How does it do it? Well, by issuing commands of course! But what causes it to issue said commands? Some sort of an occurrence, or an event. So we must have that a process manager responds to some sort of an event and possibly issues a command. We record this relation in our table as follows:

Table 2: Process Manager (PM) is a relation between events and commands.

Projection

Now let’s turn to a projection. Really simple to implement, but not so simple to understand. What is it exactly? Let us start with the reason for its existence and then a definition.

Any system that interacts with humans, for example, must at some point display some information to them. Since most humans are not able to plug into a stream of events and make sense of it, they must be shown some sort of a summary. This is the job of a projection.

Projection is responsible for taking a series of events and producing from it a summary. This summary might be a table in the database but could equivalently be a set of pixels on a monitor. Confusingly, the result of a projection (this “summary”) is also sometimes called a projection.

Now, notice that this summary is really no different to the reading of temperature on the thermometer, which reduces a set of events, temperature increases and decreases, to one value — some number between -100 and +100. Also notice, that just like a thermometer, any summary necessarily looses some information — for where there were many events of type “temperature went up one degree” and “temperature dropped ten degrees”, there is now just one value: the current temperature.

What, then, is a projection? I put it to you that it is nothing more than a system component that has as its input a set of events and as its output it produces some sort of an effect on the environment. Thus, we may represent it as follows in our (now expanded) table:

Table 3: Projection takes events from our system and stores them in the environment.
Note: We use “world” interchangeably with “environment”. In the table we use it to make it distinct from the “events”. This way, if we are to shorten the words to one letter we may write “C”, “E”, and “W” for Commands, Events and the World respectively.

The missing pieces

To the optimist, the glass is half-full. To the pessimist, the glass is half-empty. To the IT professional, the glass is twice as big as it needs to be.

So far we have identified three system components that participate in most systems created with CQRS and Event Sourcing. The aggregate root responds to commands and produces events. The process manager reacts to events and issues commands, if necessary. The projection converts a series of events into some summary, which then becomes part of the environment in which the system is operating.

However, did you notice that, since we have expanded our table to a grid of three rows and three columns, we now have a bunch of holes?

Table 4: The missing components.

What goes in those holes? Are there components that satisfy the missing relations? Well, I think the answer is yes. Next, we will define the missing components and their jobs.

The Actor

Let us define the actor to be a component takes its input from the environment and issues commands to our system. For example, a customer at an order kiosk. Where does it fit in our table?

Table 5: Actor exists in the environment and issues commands to our system.

Actor expects the system to reply with either ok or {error, reason}. So our system either accepts the command and the actor is happy, or rejects the command, possibly with a reason. In the latter case it is up to the actor to take further action by, for example, trying to issue the command again. Imagine that you try to book a table at a restaurant. You may either be admitted (ok) or refused because there are no empty tables available ({error, no_tables_available}). It is not unreasonable that you will decide to re-try your booking at a later time, since a table might open up.

Now that the environment is aware of our system — via a projection supplied by us, and can tell us what to do — via an actor, let us introduce a component which makes our system aware of the environment.

The Sensor

The sensor is a system component that produces events for our system. These events are in the domain of our system, and we can readily make use of them. For example, if there is an earthquake, the real world event might be called an “earthquake”, but our sensor might produce 1,000 events of type “Measurement(time, date, magnitude, latitude, longitude)”, which are then stored in our event store.

Which cell does the sensor fit into?

Table 6: Sensor bridges the gap between the environment and the system.

The Plant

Often, our system must cooperate with external systems which do not speak our ubiquitous language. That is, they don’t understand our events or commands. We call such a system the plant.

Here, the term I chose comes from a combination of definitions from control theory, where the plant means “combination of process and actuator” and the simple dictionary definition of the word:

plant
a place where an industrial or manufacturing process takes place

We mean by it something that is completely separate from our system and performs some function that we would like to make the use of.

How do we interact with a plant component, if it does not understand neither commands or events? Well, let use see how it fits into our table first:

Table 7: The plant component exists solely in the environment.

Looking at the output of the plant, we see that it goes to the environment. So we need something that is able to convert the effect on the world to either commands or events. Fortunately, we have already seen the two components that can do that, the actor and the sensor. The actor, takes its input from the environment, or the plant, and affects our system via a command. The sensor, also draws its input from the plant, but instead produces an event.

For example, the actor might be a worker at the factory (the plant) who presses the buttons (issues commands). The sensor might be a water level sensor that issues an alert (an event) if the water level rises too high.

How do we tell the plant what to do? One way is make use of a projection. Projection can let the plant know about the state of our system, and leave the decision of doing something up to the plant itself.

Thus, a projection can summarize the state of our system and expose it to the environment. In turn, the plant reads the summary and decides to take some action. For example, a security guard at a factory (plant) might read the reactor display screen (projection), see the “Danger critical!” message on it and then decide to go home before the whole thing blows up.

The second way to interface with the plant is by telling it to execute some specific action directly. However, for this we need another component: the actuator.

The Actuator

As alluded to in the previous section, the actuator is something that allows us to interact with the environment. What is an actuator? It is a component that takes some command from our system as input and causes something to happen in the environment. Actuator might be able to accept or reject our command, based on the environment’s reaction.

For example, we might issue a command such as “Raise Water Pressure to 1000 psi” to an actuator. It, in turn, will send a pulse to the motor. If the motor is malfunctioning, the actuator component may reply with {error, malfunction}. Our system can then make a decision on what to do next.

Aside: notice that whether the actuator or the projection is used is completely determined by the design of the plant. This is because a projection can be deemed a command to some sub-system in the plant. For example, if we project the message “Current Temperature 100” to a screen in a factory, some worker may be required to turn some nob to reduce said temperature. In other words, we should not prefer to use projections or actuators but rather find out how the plant operates and supply input that way.

Let’s see where the actuator fits in our table of components:

Table 8: An actuator component translates commands into actions in the environment.

The Controller

The controller is a component that responds to a command by issuing commands to other system components. Take, for example, a thermostat. I issue a command “Keep temperature at 24°C” and then the thermostat proceeds to issue a series of commands to the cooling or heating subsystem, all the while sensing the temperature of the room. Now, if the cooling and heating subsystems are part of our system, we here have a component that takes as input a command and then, in turn, issues commands back us.

Just like the case for other components that react to commands, the controller may accept or reject commands. Such may be the case, for example, when in a Banking system we issue a command “Pay $100 to John” and a controller, in turn, issues a command to the Checking subsystem “Transfer $100 from Checking to John”. Say that this command fails with {error, insufficient_funds}. This will cause the controller to reject our initial command, with a possible error message like {error, transfer_denied}.

In the table we have, the controller fits in the following slot:

Table 9: Controller component accepts or rejects a command by issuing commands to other components.

The Event System

Here we come to the last cell in our table. As we can see it must be occupied by a system component that takes as input events and outputs events. Such is the case for most stream processing systems. We will refer to such a component as the event system.

The event system takes events as input and produces events as output. The readily available example of such a system is the Greg Young’s Event Store offering which provides the means to produce new streams of events from existing streams of events. We will not go more into such types of systems here as they are already covered quite extensively on the internet.

Finally, our table is complete! We caught them all!

Table 10: Event System processes events and produces more events.

Conclusion

I love pressing F5. It’s so refreshing.

In the large heterogeneous systems it is hard to keep the components clearly defined. Hopefully this overview will help with the design of the software systems that have to function in such an environment.

In this overview I tried to give each component a name and describe its function with the use of examples. Some components are used regularly, e.g. Aggregate Roots and Projections, while others, like the Plant or the Controller are only encountered when the system must interact with the external entities or the environment.

Here, again, is the complete table of components present in CQRS and Event Sourced systems:

Table 11: Components participating in CQRS and Event Sourced system.

Happy system building!