tl;dr. Event Sourcing is this...
f1(state, command) -> events f2(state, events) -> state // the golden function
instead of this..
f(state, command) -> state
Event Sourcing (ES) has been steadily rising in popularity as a result of recently trending topics including Domain Driven Design (DDD), microservices, distributed logs, and others. There are many articles that explain ES in terms of these topics, but I felt the need to explain it a different state, agnostic to any particular tech choice or context.
Fundamentally, ES is an alternate way to represent state. That's it.
Take a moment to think about any system that is familiar to you. Write down one or two commands (actions) you can perform and what you observe to be the effect of that command. From a user's perspective this makes sense. I have an intent, I send a command expressing my intent, and I observe the effect of that command on the state.
This abstraction can be expressed as a function that takes the current state of the system and a command and returns the new state of the system (errors or rejections being omitted for brevity).
f(state, command) -> state
What I now have to observe is the new state. How you observe the state is determined by what the API provides (list of objects, object by id, etc).
Rather than going from state to state, ES breaks up this state transition into two functions. The first function takes the same arguments, the previous state and the command, but now returns one or more events.
f1(state, command) -> events
Recall the command expressed the intent of the action to be performed. The events also capture the intent of the change, but also express what changed as a result of the command. Events are past tense and thus immutable facts that cannot be changed.
The second part of the transition is taking the current state and applying the events to yield the new state.
f2(state, events) -> state
Applying only the new events to the current state is common for online command processing. However, this function can be used to re-create the state at any point in time if need be by some offline process. This is done by passing the null state and the full history of events up to some point in time.
Why is this useful? Decoupling how the events are produced (by commands) from how events are applied to the state enables changing the implementation of the state or how the events are ultimately applied. Since events capture the intent and what changed, they are the source of the truth for the state. The state is simply a structure expressing the culmination of these events up to some point in time.
Making events first-class provides additional advnatages such as enabling inspection for operational or auditing reasons and making it easy to broadcast events to other interested components in the system for real-time event-driven updates.