Oskar Dudycz

Pragmatic about programming

What's the difference between a command and an event?

2021-02-17 oskar dudyczEvent Sourcing

cover

What’s the difference between a command and an event?

The answer seems apparent, but let’s see if it’s straightforward.

The command represents the intention. It targets a specific audience. It can be your friend when you’re asking “pass me the salt”. It can be an application service and request with intention to “add user” or “change the order status to confirmed”. So the sender of the command must know the recipient and expects the request to be executed. Of course, the recipient may refuse to do it by not passing us the salt or throwing an exception during the request handling.

The event, in turn, represents a fact in the past. It carries information about something accomplished. What has been seen, cannot be unseen. Following our examples “user added”, “order status changed to confirmed” are facts in the past. We do not direct events to a specific recipient, we broadcast the information. It’s like telling a story at a party. We hope that someone listens to us, but we may quickly realise that no one is paying attention.

What the event and the command have in common?

Both are messages. They convey specific information; a command about the intent to do something, or an event about the fact that happened. From the computer’s point of view, they are no different. Only the business logic and the interpretation of the message can find the difference between event and command.

Commands are usually assumed to be synchronous and events to be asynchronous. We typically send commands via, e.g. Rest API, events via queues (In-memory, RabbitMQ, Kafka etc.). This distinction comes from custom. When we’re sending a command, we’d like to know immediately whether it has been done or not. Usually, we want to do an alternative scenario or error handling when the operation failed. Likewise, we typically assume that it is better to immediately stop the process, e.g. buying a cinema ticket than wait and refresh and see if it has worked.

It makes sense, but it’s not always so obvious. For example, a bank transfer: when we make it, it won’t happen right away. We have to wait for a while. It’s the same when making a purchase on the Internet. Placing an order and making a payment doesn’t immediately finish the whole process. It still has to be shipped, the invoice issued, etc. This is an asynchronous process, so the results of our commands may also be asynchronous.

Microservices and distributed systems add additional complexity.

Traditionally, the sender needed to know who to tell to take over the rest of the work. Using queuing/streaming systems reverse the services’ dependency. The sender publishes the message to the unified channel, the recipient subscribes and waits for it. When we buy a cinema ticket, a receipt must be generated, the seat must be reserved, the user should be notified by e-mail and displayed on the website. All that can be split into separate workflow steps. Which one should be triggered by a command and which one by the event?

We could use heuristics: we send the command when the recipient has the right to refuse the request and event when the recipient just accepts it. If we want to add a user, the system may refuse when we used the existing username. Can a financial service refuse the reservation service to generate a receipt? If the reservation is confirmed, transfer was made, then the financial module should just accept it. So, is it an event?

In theory, yes, if there are no validation rules and if the event happened then, the other system should accept it and perform the logic. Kafka-like streaming systems should guarantee delivery even if the financial service is temporarily unavailable. We have bugs in the code? Then we have to fix them.

Here, the theory ends, and practice begins. Our customer doesn’t care if the error is a bug that we know and fix it in the near future? The customer wants to do a business and be operational.

Of course, in the message queuing tutorials, you can find suggestions like: “There is a dead-letter queue/poison-message queue where not-processed messages will be put. You can check there and react”. That’s cool, but how many people are monitoring it? And even if they do, how quickly will they be able to react? Let’s assume that we collect metrics, send alerts, support responses quickly, and report a ‘ticket’ to programmers. How quickly will they fix it? We can ease that with the design upfront and adding the compensating operation (e.g. button in the UI to generate the invoice if the reservation confirmed event was lost).

Sometimes it turns out that a given event will always have only one recipient. What’s more, we expect it to be always handled. Is it still an event? It is worth considering whether we’re not sending the command “issue a receipt” under the event with the “reservation was confirmed” name. When the business process is crucial, we may prefer to fail fast and have an immediate result. For such cases, it’s worth considering whether it would be better to do an asynchronous command instead of an asynchronous event.

Of course, we’re touching here on the separate topic of the distributed processes. By itself it’s non-trivial. You can read more on that in my other posts:

The distinction between commands and events may not be so simple as it looks. We need to take many factors into consideration:

  • Is it an intention to do something or the recorded fact?
  • Is it asynchronous or synchronous operation?
  • Can it have multiple recipients?
  • Is it business-critical?
  • And many more

If someone asks you “is it a command or an event”, don’t be afraid to say “it depends” and start to work on the business logic understanding.

Oskar

👋 If you found this article helpful and want to get notification about the next one, subscribe to Architecture Weekly.

✉️ Join over 11500 subscribers, get the best resources to boost your skills, and stay updated with Software Architecture trends!

Loading...
Event-Driven by Oskar Dudycz

cover

Through my window, I see the result of good plans but poor execution. Opposite my flat, there is a partially completed construction place. Buildings were supposed to be eye-catching Mediterranean style apartments. Delivery date? Two years ago. Actual? More and more unknown.

Some time ago, I heard that using Event Sourcing makes creating Event-Driven Architecture easier. The arguments were correct, that if we’re already publishing events to trigger business workflows, then at some point, we may want to also store events to not lose information. Agreed. However, I also heard that keeping the state as events will simplify things. We’ll have a source of truth with a record of the system behaviour. This will allow, e.g. to confront the results of the operations with the recorded state. I’d agree with that, with one distinction. It’s easier as long as you already know Event Sourcing.

Many people in the DDD community claim that the essential is to properly break down the system into autonomous parts called bounded contexts. Once we have it, the rest is secondary and will sort itself out. For sure.

Many seasoned programmers speak similarly about new technologies. They claim that they can translate past experience into new technologies. That’s true that by analogy, they can catch the big picture quicker. But isn’t it a bold assumption to say that Win.Forms specialist will learn Angular quickly?

The end result may differ a lot from the initial ideas. I saw the plan of those buildings next to me. Now I can see the effects of the execution. Or actually, the lack.

I believe that we should carefully acknowledge not only the point of view of our authorities but also their seating point. If we want to find out how to form a wall, do we ask an architect or a foreman? An architect may know the theory, but the practice is what we’re looking for. On the other hand, if you want to know where to put the wall, you prefer the architect to do measurements. At least if you don’t want to have the roof falling to your head.

After I had torn a ligament in my knee, I went to two qualified orthopedists. One said I should have surgery and do a reconstruction. The second stated that there is no need for that; rehabilitation should be enough. Guess which one had a specialization in surgery and which in rehabilitation?

People usually give us advice from the point where they’re currently standing. They are entitled to a biased view. An architect who rarely does programming will tend to downplay the value of implementation and tactical patterns. Midlevel developers will focus on technicalities instead of the global system impact. The team manager or consultant will emphasize the importance of soft skills (or esoteric techniques known only to them).

The truth is that we need all of them. The excellent plan will fall on the bad execution. The best execution for the wrong case will be just a waste of time. We should carefully evaluate the advice considering what we need and what an expert can give us.

Therefore, when we’re reading an article, watching a talk, let’s also pay attention to the place where the person is standing. The perspective from there may be much different from where we are right now. That can be good, as it may push us in the right direction. But it may also be misleading, as we accidentally take biases of this person without understanding the tradeoffs. Personally, I prefer to follow not only people from pedestal but also those that are closer to my position. A bit further in the journey, but not too far. That helps me to calibrate my view as those people are more relative to my daily struggles.

Polish historical leader Józef Piłsudzki reportedly used to say: “Right is like an ass, everyone has its own”.

Cheers!

Oskar