Events are obviously the fundamental building block of event-sourced systems. Commands are equally a common concept in such systems although the distinction between events and commands, if any, is not always clear. There are certainly varying views on what role each one should play. Some views argue that the differences between these two concepts are mainly linguistic and therefore commands and events would represent fundamentally the same thing. Other points of view consider that commands and events are semantically different (command sourcing vs event sourcing).
This is a question we’ve considered, having been involved in designing and building a few event-driven systems. One helpful way of looking at it, is to consider the pragmatic nature of these systems i.e. how they behave end-to-end, rather than by only considering a purely local or programmatic view of events. Our own experiences of such systems is that they tend to be distributed, fronted by a public API and store system-wide state (as opposed to just application specific state).
Events and commands are subtly but significantly different. The two concepts might look similar on the surface but in an event-driven system, they serve different purposes, behave differently and should be treated differently. To clarify, let’s venture some simple definitions.
Definitions & Typical Scenarios
Commands, organised in a command log, are an account of all the things the system has been asked to do.
Events, organised in an event log, are an account of the all the facts and the changes the system knows about the entities that constitute its universe.
In short, commands are external actions while events are internal facts. But practically how do they relate to each other?
There is often a one-to-one correlation between commands and events but this is not always the case.
If we take a fictional Update User command as an example, in the most common case there would be a User Updated event being triggered when the command is accepted.
However, a mobile client might retry the same command multiple times. It is possible for the system to store all these commands sequentially to build a comprehensive audit trail of its interaction with the outside world (command log). In this case, we would expect some part of the system to take responsibility to deduplicate the redundant commands down the line, for example by ensuring that the accepted commands or the resulting events are effectively idempotent.
In some other cases, a command might be rejected for some valid business reason, and therefore we will have a one-to-zero correlation between commands and events.
Finally, one command might trigger multiple events. In the previous example, the Update User command could hypothetically result in two updates to two distinct entities, the user’s Profile and Address, assuming this is the way we modeled our entities. In this case we have a one-to-many correlation between commands and events.
Reproducible System State
If we consider replay, which is a defining feature of an event source system, replaying events will produce the most up-to-date state of each entity without necessarily knowing how this state was produced in the first place. Replaying commands will result in replaying the business logic that produced the associated states. You could picture the implications of that if the business logic changes between replays, intentionally or not. For example, if a command is supposed to trigger an email to be sent, then an email will be sent at each replay. Equally, if the code of the command changes in-between replays, then the resulting state and events could potentially be different.
Following these definitions, events are essential to building an event log for each entity and therefore to track the state of the system. Commands on the other hand serve a more operational purpose, such as auditing and troubleshooting. As such, they tend to be managed differently, despite the apparent similarity of form. For example you could choose different storage and retention policies for commands and events or decide to drop commands altogether.
In summary, we found it useful to draw a distinction between commands and events in an event-driven system where they actually play orthogonal and complementary roles. There is no right or wrong design choices; it is up the designer of the system to decide if it is valuable to introduce both concepts, based on the specific requirements of their systems.
However, given the broad and crosscutting role of events and commands in event-driven architectures, it is important to deliberately define clear and reliable semantics to cover both their functional and nonfunctional behaviours.