Event Sourcing in Distributed Systems: A Practical Guide for Software Developers

Learn how Event Sourcing models business change as an ordered history of events. This tutorial explains event streams, commands, state reconstruction, event stores, domain events, aggregates, .NET implementation, concurrency, snapshots, transactions, projections, read models, failures, eventual consistency, versioning, and schema evolution. It closes by examining real technologies and when Event Sourcing is an appropriate design choice.

1. Lesson 1: Why Event Sourcing Exists Demo 2. Lesson 2: The Core Idea of Event Sourcing Demo 3. Lesson 3: Events, Commands, and State Demo 4. Lesson 4: Rebuilding State from Events Demo 5. Lesson 5: Event Stores Demo 6. Lesson 6: Designing Good Domain Events Locked 7. Lesson 7: Event Sourcing with Aggregates Locked 8. Lesson 8: Implementing Event Sourcing in .NET Locked 9. Lesson 9: Concurrency and Optimistic Concurrency Locked 10. Lesson 10: Snapshots Locked 11. Lesson 11: Event Sourcing and Transactions Locked 12. Lesson 12: Event Sourcing in Distributed Systems Locked 13. Lesson 13: Event Sourcing and Event-Driven Architecture Locked 14. Lesson 14: Projections and Read Models Locked 15. Lesson 15: Handling Failures and Eventual Consistency Locked 16. Lesson 16: Event Versioning and Schema Evolution Locked 17. Lesson 17: Event Sourcing with Real Technologies Locked 18. Lesson 18: When to Use Event Sourcing Locked

Lesson 3: Events, Commands, and State

Demo

To work effectively with Event Sourcing, it is important to understand the different roles played by commands, events, and state. These concepts are closely related, but they represent different stages of a business operation. In this lesson, we will follow an order from the moment a customer asks to place it through the point where the resulting state is produced.

Three Different Concepts

When learning Event Sourcing, three concepts can easily become confused: commands, events, and state. They are closely related, but they represent different stages of a business operation. A useful way to remember them is that a command is a request, an event is a fact, and state is the current result of applying those facts.

Consider an online store where a customer wants to purchase a laptop. The customer clicks the "Place Order" button, and the application needs to decide whether the order can actually be created. This simple operation allows us to see how commands, events, and state work together.

Commands Are Requests

A command represents something that someone or something is asking the system to do. It expresses an intention, not something that has already happened. For example, the application might receive a command like this:

public record PlaceOrder(
    Guid OrderId,
    Guid CustomerId,
    decimal Amount);

PlaceOrder means, "Please create this order." The command can be rejected because the customer may not exist, the amount may be invalid, or another business rule may prevent the operation. Therefore, receiving a command does not guarantee that anything will change.

Commands normally come from an external actor, such as a user, another service, or a scheduled process. The application receives the command, validates it against the current state and business rules, and then decides what should happen.

Events Are Facts

If the command is successfully processed, the system produces an event representing what actually happened. For example:

public record OrderPlaced(
    Guid OrderId,
    Guid CustomerId,
    decimal Amount);

OrderPlaced is fundamentally different from PlaceOrder. PlaceOrder says, "I want you to place this order," while OrderPlaced says, "This order was successfully placed." Once OrderPlaced has been stored, it becomes part of the order's permanent history.

This distinction is important because commands can fail, but events represent successful business facts. We should not store an OrderPlaced event if the order failed to satisfy the business rules.

State Is the Current Result

The state represents the current condition of the order after processing its events. For example, after receiving an OrderPlaced event, the application might have this state:

public class OrderState
{
    public Guid Id { get; set; }
    public Guid CustomerId { get; set; }
    public decimal Amount { get; set; }
    public string Status { get; set; } = "";
}

After applying the event, the state could be:

OrderId: 5001
CustomerId: 101
Amount: $80
Status: Placed

The state is therefore not necessarily the historical record. In Event Sourcing, it is typically a representation that can be rebuilt by replaying the events belonging to the order.

How They Work Together

The complete flow can be understood as a simple sequence:

Customer
   |
   | PlaceOrder command
   v
Order Application
   |
   | Validate business rules
   v
OrderPlaced event
   |
   | Store event
   v
Event Store
   |
   | Apply event
   v
Current Order State

Suppose the customer places an order for $80. The application receives PlaceOrder, checks that the request is valid, and produces OrderPlaced. The event is stored in the event stream. When the OrderPlaced event is applied, the order's state becomes Placed with an amount of $80.

Later, the customer pays for the order. The application might receive another command:

PayOrder

If payment succeeds, it produces:

OrderPaid

The order's history now becomes:

1. OrderPlaced($80)
2. OrderPaid($80)

The current state might now be:

Status: Paid
Amount: $80

The Key Mental Model

The easiest way to remember the relationship is: a command asks for a change, an event records a successful change, and state represents the result of those changes. In an Event-Sourced system, events are especially important because they form the historical source from which the state can be reconstructed.

This distinction will become important in the next lessons. Once we understand commands and events, we can examine how an application takes an event stream such as OrderPlaced, OrderPaid, and OrderShipped and uses those events to rebuild the current state of an order.