What is the difference between commands and events in architectures that emphasize events? The only distinction I can see is that commands are usually sourced/invoked by actors outside the system, whereas events seem to be sourced by handlers and other code in a system. However, in many example applications I have seen, they have different (but functionally similar) interfaces.
Commands can be rejected.
Events have happened.
This is probably the most important reason. In an event-driven architecture, there can be no question that an event raised represents something that has happened.
Now, because Commands are something we want to happen, and Events are something that has happened, we should be using different verbs when we name these things. This drives separate representations.
I can see is that commands are usually sourced/invoked by actors outside the system, whereas events seem to be sourced by handlers and other code in a system
This is another reason they are represented separately. Conceptual clarity.
Commands and Events are both Messages. But they are in fact separate concepts, and concepts should be modeled explicitly.
The event is a fact from the past.
The command is only a request, and thus may be refused.
Commands | Events | |
---|---|---|
Purpose | Invoke Behavior | Something Happened |
Ownership | Command Owned by Consumer | Event Owned by Publisher |
Consumers | One Consumer | Zero or Many Consumers |
Senders | Many Senders | Single Publisher |
Naming | Verb | Past Tense |
An important characteristic of a command is that it should be processed just once by a single receiver. This is because a command is a single action or transaction you want to perform in the application. For example, the same order creation command should not be processed more than once. This is an important difference between commands and events. Events may be processed multiple times because many systems or microservices might be interested in the event. 'msdn'
Also, in addition to all the answers here exposed, an event handler may be able to trigger a command as well after receiving notification that an event occurred.
Say for example that after you create a Customer, you also want to initialize some accounts values, etc. After your Customer AR add the event to the EventDispatcher and this is received by a CustomerCreatedEventHandler object, this handler can trigger a dispatch of a command which will execute whatever you need, etc.
Also, there are DomainEvents and ApplicationEvents. The difference is simply conceptual. You want to dispatch all your domain events first (some of them may produce Application Events). What do I mean by this?
Initializing an account after a CustomerCreatedEvent has occurred is a DOMAIN event. Sending an email notification to the Customer is an Application Event.
The reason you shouldn't mix them is clear. If your SMTP server is temporarily down, that doesn't mean that your DOMAIN OPERATION should be affected by that. You still want to keep a non-corrupted state of your aggregates.
I usually add events to my Dispatcher at the Aggregate Root level. This events are either DomainEvents or ApplicationEvents. Can be both and can be many of them. Once my command handler is done and I am back in the stack to the code that execute the Command handler, then I check my Dispatcher and dispatch any other DomainEvent. If all of this is successful, then I close the transaction.
If I have any Application Events, this is the time to dispatch them. Sending an email doesn't necessarily need an open connection to a database nor a transaction scope open.
I strayed away a little bit from the original question but it is also important for you to understand how events may also be conceptually treated differently.
Then you have Sagas.... but that's WAYYYY OFF of the scope of this question :)
Does it make sense?
After working through some examples and especially the Greg Young presentation (http://www.youtube.com/watch?v=JHGkaShoyNs) I've come to the conclusion that commands are redundant. They are simply events from your user, they did press that button. You should store these in exactly the same way as other events because it is data and you don't know if you will want to use it in a future view. Your user did add and then later remove that item from the basket or at least attempt to. You may later want to use this information to remind the user of this at later date.
Just to add to these great answers. I'd like to point out differences with regards to coupling.
Commands are directed towards a specific processor. Thus there is some level of dependence/coupling with the Command initiator and the processor.
For example, a UserService
upon creating a new user sends a "Send Email" Command to the EmailService
.
The fact that the UserService
knows that it needs the EmailService
, that is already coupling. If EmailService
changes its API schema or goes down, it directly affects the UserService
function.
Events are not directed towards any specific event handler. Thus the event publisher becomes loosely coupled. It does not care what service consumes its event. It's even valid to have 0 consumer of an Event.
For example, a UserService
upon creating a new user publishes a "User Created Event". Potentially an EmailService
can consume that event and sends an email to the user.
Here the UserService
is not aware of the EmailService
. They are totally decoupled. If the EmailService
goes down, or changes business rules, we only need to edit the EmailService
Both approaches have merits. A purely Event Driven Architectural design is harder to track since it is too loosely coupled, especially on large systems. And a Command heavy Architecture have high level of coupling. So a good balance is ideal.
Hope that makes sense.
They are represented separetly because they represent very different things. As @qstarin said commands are messages that can be rejected, and that on success will produce an event. Commands and events are Dtos, they are messages, and they tend to look very similar when creating and entity, however from then on, not necessarily.
If you are worried about reuse, then you could use commands and events as envelopes to your (messge) payload
class CreateSomethingCommand
{
public int CommandId {get; set;}
public SomethingEnvelope {get; set;}
}
however, what I d like to know is why are you asking :D ie do you have too many commands/events?
In addition to the conceptual differences mentioned above, I think there is another difference related to common implementations:
Events are typically processed in a background loop that needs to poll the event queues. Any party interested in acting on the event may, usually, register a callback that is called as a result of the event queue processing. So an event may be one to many.
Commands may not need to be processed in such a manner. The originator of the command will typically have access to the intended executor of the command. This could be, for example, in the form of a message queue to the executor. Thus a command is intended for a single entity.
You cannot recompute a state based on commands, because in general they can produce different outcomes each time they are processed.
For example, imagine a GenerateRandomNumber
command. Each time it's invoked it will produce a different random number X. Thus, if your state depends on this number, each time you recompute your state from the commands history, you'll get a different state.
Events solve this problem. When you execute a command, it produces a sequence of events that represent the outcome of the command execution. For example, the GenerateRandomNumber
command could produce a GeneratedNumber(X)
event that logs the generated random number. Now, if you recompute your state from the events log, you'll always get the same state, because you'll always use the same number that was generated by a particular execution of the command.
In other words, commands are functions with side-effects, events record the outcome of a particular execution of a command.
Note: You can still record a history of commands for audit or debugging purposes. The point is that to recompute the state, you use the history of events, not the history of commands.
I think something to add to quentin-santin's answer is that they:
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
Let's throw the functional core, imperative shell pattern into the mix. Events correlate to the functional core and commands to the imperative shell. That is, events reside in the pure and commands in the impure. The shell, like any CLI, handles the impurities.
This can be seen in that commands are validated and sometimes rejected. It can also be seen in that commands can rely on operations not permitted in the world of events. For example, rolling dice in a game of Backgammon involves issuing a roll
command and its result when generated would be memorialized in a rolled
event. The value of this separation can be further seen in the ability to source or replay events.
While live events may bubble up to the shell and cause new commands to be issued, replayed events cannot. They must cause no further side effects.
Expanding on the concepts from @LeviFuller comment, I also used command and event patterns to represent "exactly one" and "0 or more" listeners respectively, but in a different way from @froi answer, where both are still decoupled.
In this architecture, a command publisher still does not know who is going to process the command (much like with events). The difference is that the publisher is assured that someone will process the command. So while you could model any command as an event, it would be undesirable to do so, because the publisher doesn't have that assurance.
This becomes especially important when considering the transition between command processors. A transition may occur in many scenarios - for example, if the command processor is a network service, it may need to go down for maintenance; alternatively, in a UI based application, the command processor could represent the screen the user is on, and the user is moving to a different screen.
During a transition, you typically want to ensure that commands coming in are only processed by the new processor. The old processor would deregister itself as soon as possible, meaning that there may be a gap while no processor is registered for the command type - and the command hub (analogous to an event hub) should maintain a queue of commands received during this time. This may also include partially processed commands from that time, as the old processor should resubmit the "work still to do" after deregistering itself.
None of this would be possible using events. If nobody is listening to an event, it means nobody cares about it, and that's okay. Events would not need to be queued in that case. If an event subscriber had not finished processing an event at the time it begins to shut down, there's typically no need to resubmit the "work still to do" as it's no longer relevant. Events are "fire and forget" in that there is no expectation of what happens after they are submitted, but with a (valid) command there is an expectation that something will be done, even during a transition.
© 2022 - 2024 — McMap. All rights reserved.