Commands & Events: What’s the difference?

One of the building blocks of messaging is, you guessed it, messages! But there are different kinds of messages: Commands and Events. So what’s the difference? Well, they have very distinct purposes, usage, naming, ownership, and more!

YouTube

Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything in this post.

Commands

The purpose of commands is the intent to invoke behavior. When you want something to happen within your system, you send a command. There is some type of capability your service provides and you need a way to expose that. That’s done through a command.

I didn’t mention CRUD. While you can expose Create, Update, and Delete operations through commands, I’m more referring to specific behaviors you want to invoke within your service. Let CRUD just be CRUD.

Commands have two parts. The first is the actual message (the command), which is the request and intent to invoke the behavior. The second is consumer/handler for that command which is performing and executing the behavior requested.

Commands have only a single consumer/handler that resides in the same logical boundary that defines and owns the schema and definition command.

Commands can be sent from many different logical boundaries. There can be many different senders.

To illustrate this, the diagram below has many different senders, which can be different logical boundaries. The command (message) is being sent to a queue to decouple the sender and consumer.

Commands & Events: What's the difference?

A single consumer/handler, that owns the command, will receive/pull the message from the queue.

When processing the message, it may interact with its database, as an example.

Commands & Events: What's the difference?

As mentioned, there can be many senders, so we could have a completely different logical boundary also sending the same command to the queue, which will be processed the same way by the consumer/handler.

Lastly, naming is important. Since a command is the intent to invoke behavior, you want to represent it by a verb and often a noun. Examples are PlaceOrder, ReceiveShipment, AdjustInventory, and InvoiceCustomer. Again, notice I’m not calling these commands CreateOrder, UpdateProduct, etc. These are specific behaviors that are related to actual business concepts within a domain.

Events

Events are about telling other parts of your system about the fact that something occurred within a service boundary. Something happened. Generally, an event can be the result of the completion of a command.

Events have two parts. The first is the actual message (the event), which is the notification that something occurred. The second is the consumer/handler for that event which is going to react and execute something based on that event occurring.

There is only one logical boundary that owns the schema and publishes an event.

Event consumers can live within many different logical boundaries. There may not be a consumer for an event at all. Meaning there can be zero or many different consumers.

To illustrate, the single publisher that owns the event will create and publish it to a Topic on a Message Broker.

Commands & Events: What's the difference?

That event will then be received by both consumers. Each consumer will receive a copy of the event and be able to execute independently in isolation from each other. This means that if one consumer fails, it will not affect the other.

Commands & Events: What's the difference?

Naming is important. Events are facts that something happened. They should be named in the past tense which reflects what occurred. Examples are OrderPlaced, ShipmentReceived, InventoryAdjusted, and PaymentProcessed. These are the result of specific business concepts.

Full Cycle

So how do commands and events fit together? Since Commands are about invoking intent, and Events are about indicating that something occurred, you can see how the result of a command can be publishing an event.

First, we have a client/browser making a call to our HTTP API. Let’s say its to place an order.

In this specific case, we want to just accept the incoming HTTP request and capture the relevant data and send a PlaceOrder command to a queue on our message broker so we can process the message asynchronously. At which point we can then immediately return the client/browser that we accepted the request and are processing it.

Asynchronously the command handler can pull the message from the queue and execute whatever behavior is required to place the order.

The result of this is now we are going to publish an OrderPlaced event to a topic on the message broker.

We have 2 different consumers for the OrderPlaced event. One is to send an email to the customer saying thank you for your order. The other consumer might be to process their credit card and charge them for the order.

Commands & Events

To summarize commands and events we can compare and contrast their purpose, ownership, consumers, senders, and naming.

Commands & Events: What's the difference?

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design as well as access to source code for any working demo application that I post on my blog or YouTube. Check out the YouTube Membership or Patreon for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design

Microservices gets it WRONG defining Service Boundaries

Logical boundaries aren’t physical boundaries. A service boundary can be a one-to-one mapping of logical boundary and physical boundary but they don’t have to be. A logical boundary can have many physical boundaries and a physical boundary can be composed of many components from many logical boundaries. Do we have microservices to thank for the idea that the physical, development, and logical views are the same? This introduces a pile of unneeded complexity related to deployment, versioning, and data duplication.

YouTube

Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything in this post.

Physical & Logical Boundaries

When most people think of service boundaries, they are often thinking of this diagram below. The dotted line represents a service boundary, in which both services communicate has their own database, however, they communicate directly with each other.

Microservices gets it WRONG defining Service Boundaries

Now if I were to specifically call out physical boundaries, I would have illustrated with a dotted line that the databases are a physical aspect separately from the service itself.

Microservices gets it WRONG defining Service Boundaries

This is pretty obvious as each service is probably making network calls to a database instance.

So now let’s throw some Client/UI into the mix. How would that fit into the diagram illustrating physical boundaries? It may look something like this:

However, in my experience, most folks aren’t building a dedicated UI/Client for each service, rather they often build a SPA, maybe a mobile app, etc.

Now I’m excluding an API gateway or BFF (Backend for frontend) in this diagram, but that could exist as well, in which the Client/UI would communicate with that, and it in turn would communicate with the various services.

What I’m really illustrating here is the idea that there is usually a frontend and a backend. The backend is represented by possibly many different services.

Based on the diagram, if I were to ask how many logical boundaries there are, you would likely say 3. If you are, it’s because you’re assuming my illustrating of logical boundaries is also represented in the same way as physical boundaries. The dotted lines represent physical boundaries, not logical boundaries. However, our industry has standardized physical boundaries being the same as logical boundaries. While they can be, they don’t have to be.

Service Boundaries

What’s a service? What are its boundaries?

A service is the authority of a set of business capabilities.

Behind those capabilities are the data that it owns. It’s a grouping of functionality that serves the business. Notice I have said nothing about technology? How you choose to develop and physically deploy those capabilities has nothing to do with defining a logical boundary.

So if we take the earlier diagram, and outline what the logical boundaries are, they are now vertical, not horizontal.

Microservices gets it WRONG defining Service Boundaries

A portion of the UI is owned by a local service boundary. The UI, the actual behaviors and capabilities, and the data all together define the logical boundary.

The issue is that there are multiple different ways to view a system. Below is a diagram that illustrates the 4+1 Architectural View Model by Philippe Kruchten

Without going into full detail about what each view represents, what I’ve been illustrating is that the logical view and physical views can be different. They do not have to be exactly the same.

If you were to think of a typical microservices implementation, the logical view and physical view are often the same. And if you’re not using a mono-repo, and have a git repo per each service, you then likely have the logical, development, and physical views all exactly the same.

A service boundary should own the entire vertical. This is why the UI is included when I’m illustrating the logical boundaries. The frontend isn’t its own logical service.

Microservices gets it WRONG defining Service Boundaries

This means that you can deploy your logical boundaries in many different ways. As an example of physical boundaries, you could have a UI that’s composed of multiple logical boundaries. The actual Service API could be composted together into a single process/container. A single database instance could hold the schema and data for each logical service boundary.

Microservices gets it WRONG defining Service Boundaries

So if we again jump back to the logical view, this turns into slicing everything vertically.

Microservices gets it WRONG defining Service Boundaries

Why does it matter?

So who cares? What’s the big deal if we treat physical boundaries as local boundaries? Unneeded complexity.

Boundaries are hard to define. When you focus on business capabilities and not technical concerns, you have to get deeper insights into the actual business domain. Understanding the capabilities that a service must provide and the data ownership behind those capabilities isn’t trivial to figure out.

If you treat local boundaries and physical boundaries as being the same thing, and you get these capabilities wrong (which you likely will), you’ll end up doing a lot of service-to-service communication, likely over a network via RPC. This path leads you to a distributed monolith, that is often worst than a traditional monolith. For more on this specifically, check out my post: REST APIs for Microservices? Beware!

A distributed monolith can be horrific because you’ll lose consistency if you really need it when performing commands. If you’re reaching out over RPC to another service boundary to get data to perform a command, the moment you get that data it’s inconsistent/stale. There is no distributed transaction.

Another issue is versioning and deployment. If you make a change to a service, but need all other services to make the appropriate changes as well, and they all need to get deployed together, you’re losing one of the primary benefits. You lost the ability to do independent deployments. Your services aren’t autonomous.

Logical boundaries do not need to be physical boundaries. They can be, but they do not have to be. You can have multiple services that are all composed and deployed in the same process. They could all live within the same git repo. Should you do this? Maybe that is actually best in your context. Maybe it’s not. But don’t get stuck thinking they’re the same thing.

Defining logical boundaries is hard. You’ll likely get it wrong. If you’re developing a new system and are unsure of where boundaries may lie, don’t put the added constraints of making your logical and physical boundaries the same as it will be much more difficult to change and manage.

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design as well as access to source code for any working demo application that I post on my blog or YouTube. Check out the YouTube Membership or Patreon for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design

Real-World Event Driven Architecture! 4 Practical Examples

Event Driven Architecture has many benefits. Loose and temporal coupling, scaling, resilience, and more. But what does that mean? What are some actual use-cases? I’m going to describe 4 practical examples of real-world event driven architecture.

YouTube

Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.


External Integrations

The first practical use-case for real-world event driven architecture is integrating with external 3rd party services. In a lot of systems, when something occurs (an event) you need to interact and exchange data with a 3rd party service. This is where Event Driven Architecture can really shine.

As an example, let’s say we have a Sales boundary that when an order is placed we need to send the customer an order confirmation email, send them a text message (SMS), and possibly also have a Webhooks system to make calls to other HTTP APIs.

Real-World Event Driven Architecture! 4 Practical Examples

The beauty of event driven architecture is that each one of these concerns (email, SMS, webhooks) is decoupled from the action of placing an order. Each consumer has their own responsibility for handling each concern.

One consumer is responsible for generating an email and sending it to AWS Simple Email Service (SES).

Real-World Event Driven Architecture! 4 Practical Examples

Another consumer, concurrently but in complete isolation can be responsible for making HTTP calls to external systems in the form of webhooks.

Real-World Event Driven Architecture! 4 Practical Examples

Lastly, there could be another consumer responsible for sending a text message (SMS) to Twilio.

Real-World Event Driven Architecture! 4 Practical Examples

Each one of these consumers is independent of each other as well as the initial publisher (Sales) of when an order was placed. Each is executed independently without interference from the other.

Since you don’t own external services, it’s hard to guarantee any type of availability. This means if one consumer is having availability issues downstream (AWS SES, Twilio, etc) all other consumers responsible for different external integrations are completely unaffected.

This allows you to integrate all different ways in a plug-in-play fashion. Need to add some new functionality? Add a new consumer.

Workflow

Long-running business processes and workflows can be difficult to manage. Event Driven Architecture allows many different logical boundaries that perform specific behaviors apart of a long-running business process.

Let’s say the Sales boundary publishes an Order Placed event.

Real-World Event Driven Architecture! 4 Practical Examples

Our Billing service needs to know when this occurs so that it can Bill the customer or create an invoice.

Real-World Event Driven Architecture! 4 Practical Examples

Once the order has been billed, we then want to allocate the product in the Warehouse and create a shipping label. For this to happen the Billing service will publish an Order Billed event.

Real-World Event Driven Architecture! 4 Practical Examples

The Warehouse will consume that Order Billed event to then allocate the product and create a shipping label.

Real-World Event Driven Architecture! 4 Practical Examples

The last portion of this workflow now is for Sales to know when the Shipping Label has been created by the warehouse, so it can update the Order Status from “Order Placed” to “Order Ready to Ship”.

Real-World Event Driven Architecture! 4 Practical Examples
Real-World Event Driven Architecture! 4 Practical Examples

There are different ways to achieve this, but specifically outlined above is called Event Choreography. You can also manage this workflow with Orchestration.

Each service is responsible for a portion of the workflow, without realizing they are. The Billing service doesn’t care about a shipping label being created within the Warehouse. All it cares about is when an Order Placed event occurs from Sales, it needs to Bill the customer and publish an Order Billed event.

Each service boundary is doing its part in this long-running business process.

State Transfer

Changing the shape of data for different use cases can be really helpful as an example of real-world event driven architecture.

We can leverage Event Driven Architecture to tell consumers when data has changed so they can update their copy of the data.

Let’s say we have three different types of data stores. One is a cache, another is a data warehouse we use for Reporting and BI, and the other is a data lake we dump data to for future use.

In order to keep all these data stores updated, we can publish an event that contains the data that changed.

Real-World Event Driven Architecture! 4 Practical Examples

From there we can have various consumers that take the event and mutate the data in their respective data stores. We may have a cache that needs to be invalidated as well as a data warehouse that needs to be updated.

Real-World Event Driven Architecture! 4 Practical Examples

Also independently we can have a consumer that dumps that in a data lake for future use.

What this allows you to do is be more real-time about various read models that your system uses. You likely won’t always have a single type of database as a system grows and has different needs and requirements. A common example is OLAP for reporting and OLTP for business processes.

Temporal Coupling

Lastly, removing temporal coupling between your own internal systems is a major benefit to Event Driven Architecture.

When making synchronous blocking request-response calls between services, we’re adding temporal coupling. When an order is placed in the system, if we’re using HTTP calls or gRPC to call between services, we’re temporally coupled. This means that all the services need to be available at the same time in order for everything to be functioning correctly.

In the example above, Sales needs to call Billing to charge the customer when an order is being placed.

After that is done, it then has to make another blocking synchronous call to the Warehouse. But what happens if that call fails or the Warehouse service is unavailable? We’ve already charged the customer in the previous call to the Billing service. We have no distributed transaction so there is no rollback.

This is why in the example earlier I illustrated the workflow using a message broker because we remove the temporal coupling. Each service operates independently without being constrained by the availability of other services.

When an order is placed, if the Billing service is unavailable, nothing fails. When it comes back online and picks up the Order Placed event from the broker, it will continue the workflow.

Once you remove temporal coupling, you start treating your internal services, just like the external services I was mentioning at the very beginning. When you move to an event driven architecture because everything is independent, it doesn’t require everything to be online and available to complete long-running business processes and workflows.

Real-World Event Driven Architecture

Hopefully, this illustrated the different ways you can leverage an event driven architecture. Integrating with 3rd party external services, handling long-running business processes and workflows, changing the shape of data in real-time for analytics and reporting, and removing temporal coupling to add resilience.

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design. As well as access to source code for any working demo application that I post on my blog or YouTube. Check out the YouTube Membership or Patreon for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design