All the existing answers seem to be quite comprehensive, but there's a terminology issue, which I'd like to resolve in my answer.
What's Event Sourcing?
It seems like if you look at five different places, you get five different answers to that question.
However, if you look at Greg Young's paper from 2010, it summarises the idea quite nicely, from page 32 onwards, but it doesn't contain the ultimate definition, so I dare formulate it myself.
Event Sourcing is a way to persist state. Instead of replacing one state with another as a result of a state mutation, you persist an event that represents that mutation. Therefore, you can always get the current state of the entity by reading all the entity events and applying those state mutations in sequence. By doing that, the current entity state becomes a left fold of all the events for that entity.
What means a "good" event store (database)?
Any persistence mechanism needs to perform two basic operations:
- Save the new entity state to the database
- Retrieve the entity state from the database
That's where Greg talks about the concept of entity streams, where each entity has its own stream of events, uniquely identified by the entity id. When you have a database, which is capable of reading all the entity events by the entity id (read the stream), using Event Sourcing is not a hard problem.
As Greg's paper mentions Event Sourcing in the context of CQRS, he explains why those two concepts play nicely with each other. Although, you have a database full of atomic state mutations for a bunch of entities, querying across the current state of multiple entities is hard work. The issue is solved by separating the transactional (event-sourced) store that is used as the source of truth, and the reporting (query, read) store, which is used for reports and queries of the current system state across multiple entities. The query store doesn't contain any events, it contains the projected state of multiple entities, composed based on the needs for querying data. It doesn't necessarily need to contain snapshots of each entity, you are free to choose the shape and form of the query model, as long as you can project your events to that model.
For that reason, a "proper" event database would need to support what we call _real-time subscriptions that would deliver new (and historical, if we need to replay) events to the query model to project.
We also know that we need the entity state in hand when making decisions about its allowed state transition. For example, a money transfer that has already been executed, should not be executed twice. As the query model is by definition stale (even for milliseconds), it becomes dangerous when you make decisions on stale data. Therefore, we use the most recent, and totally consistent state from the transactional (event) store to reconstruct the entity state when executing operations on the entity.
Sometimes, you also want to remove the whole entity from the database, meaning deleting all its events. That could be a requirement, for example, to be GDPR-compliant.
So, what attributes would then be needed for a database sued as an event store to get a decent event-sourced system working? Just a few:
- Append events to the ordered, append-only log, using entity id as a key
- Load all the events for a single entity, in an ordered sequence, using the entity id as a key
- Delete all the events for a given entity, using the entity id as a key
- Support real-time subscriptions to project events to query models
What is Kafka?
Kafka is a highly-scalable message broker, based on an append-only log. Messages in Kafka are produced to topics, and one topic nowadays often contains a single message type to play nicely with the schema registry. A topic could be something like cpu-load where we produce time-series measurements of the CPU load for many servers.
Kafka topics can be partitioned. Partitioning allows you to produce and consume messages in parallel. Messages are ordered only within a single partition, and you'd normally need to use a predictable partition key, so Kafka can distribute messages across the partitions.
Now, let's go through the checklist:
- Can you append events to Kafka? Yes, it's called produce. Can you append events with the entity id as a key? Not really, as the partition key is used to distribute messages across partitions, so it's really just a partition key. One thing mentioned in another answer is optimistic concurrency. If you worked with a relational database, you probably used the
Version
column. For NoSQL databases you might have used the document eTag. Both allow you to ensure that you update the entity that is in the state that you know about, and it hasn't been mutated during your operation. Kafka does not provide you with anything to support optimistic concurrency for such state transitions.
- Can you read all the events for a single entity from a Kafka topic, using the entity id as a key? No, you can't. As Kafka is not a database, it has no index on its topics, so the only way to retrieve messages from a topic is to consume them.
- Can you delete events from Kafka using the entity id as a key? No, it's impossible. Messages get removed from the topic only after their retention period expires.
- Can you subscribe to a Kafka topic to receive live (and historical) events in order, so you can project them to your query models? Yes, and because topics are partitioned, you can scale out your projections to increase performance.
What about Kafka and Sansa (or Kafka Streams)?
The ability to fold events to some representation of state, and store this state in another database, asynchronously, is a side feature of Event Sourcing. We usually call these operations "projections" as you can fold events to state in many different ways. It is a useful feature as you can build use case-specific query models at will, and rebuild them from the beginning of times or from a certain point in time as you have the full history of events in the log at your disposal.
However, this is not what Event Sourcing is about as you can do exactly the same using queues, and no one ever said that passing messages through a queue and updating database records in the message consumer is "Event Sourcing".
Other things to consider
- Projecting events to persisted state is one-way operation. If you have made a mistake, you can't revert it as the state is already persisted. You have to stop the system, re-project everything, and start the system again. It can take hours, or even weeks.
- You cannot see the history of a single entity simply because all the events for, potentially, millions of entities are stored in a single topic. You'd need to scan the whole topic to figure out what happened with the entity.
- When you receive a request from the user to delete their data, you won't be able to do it without re-shovelling events between topics. You, basically, must think about it upfront and apply rather complex patterns like crypto-shredding in advance or risk being not compliant with local privacy regulations. Ignoring these regulations might eventually drive your company out of business.
So, why people keep doing it?
I believe that the reason why a lot of people claim that Kafka is a good choice to be an event store for event-sourced systems is that they confuse Event Sourcing with simple pub-sub (you can use a hype word "EDA", or Event-Driven Architecture instead). Using message brokers to fan out events to other system components is a pattern known for decades. The issue with "classic" brokers as that messages are gone as soon as they are consumed, so you cannot build something like a query model that would be built from history. Another issue is that when projecting events, you want them to be consumed in the same order as they are produced, and "classic" brokers normally aim to support the competing consumers pattern, which doesn't support ordered message processing by definition. Make no mistake, Kafka does not support competing consumers, it has a limitation of one consumer per one or more partitions, but not the other way around. Kafka solved the ordering issue, and historical messages retention issue quite nicely. So, you can now build query models from events you push through Kafka. But that's not what the original idea of Event Sourcing is about, it's what we today call EDA. As soon as this separation is clear, we, hopefully, stop seeing claims that any append-only event log is a good candidate to be an event store database for event-sourced systems.