What I'm not super clear on is why you would ever rehydrate your Aggregates from the Event Store itself.
Because the "events" are the book of record.
If projecting changes to "read" databases is so easy, why not always project changes to a "write" database whose schema perfectly matches your domain model? This would effectively be a snapshot database.
Yes; it's sometimes a useful performance optimization to use a cached copy of aggregate state, rather than regenerating that state from scratch every time. Remember: the first rule of performance optimization is "Don't". It adds extra complexity to the solution, and you'd prefer to avoid that until you have a compelling business motivation.
If this is the case, is the Event Store only useful when rebuilding your "write" database as a result of schema changes? Or am I missing something bigger?
You are missing something bigger.
First point is that if you are considering an event sourced solution, it is because you expect there to be value in preserving the history of what has happened, which is to say that you want to be making non destructive changes.
So that's why we are writing to the event store at all.
In particular, this means that every change needs to be written to the event store.
Competing writers could potentially either destroy each other's writes, or drive the system to an unintended state, if they aren't aware of each other's edits. So the usual approach when you need consistency is to address your writes to a specific position in the journal (analogous to a conditional PUT in an HTTP api). A failed write tells the writer that their current understanding of the journal is out of sync, and that they should recover.
Returning to a known good position then replaying any additional events since that point is a common recovery strategy. That known good position can be a copy of what is in the local cache, or a representation in your snapshot store.
In the happy path, you can keep a snapshot of the aggregate in memory; you only need to reach out to an external store when there is no local copy available.
Furthermore, you don't need to be completely caught up, if you have access to the book of record.
So the usual approach (if using a snapshot repository) is to maintain it asynchronously. That way, if you do need to recover, you can do so without reloading and replaying the entire history of the aggregate.
There are many cases where this complexity is not of interest, because fine grained aggregates with scoped lifetimes don't usually collect enough events for the benefits to exceed the costs of maintaining a snapshot cache.
But when it is the right tool for the problem, loading a stale representation of the aggregate into the write model, then updating it with additional events, is a perfectly reasonable thing to do.
The infrastructure for the event sourcing is an event store which saves the events as documents inside a MongoDB collection and then publish a corresponding message to a service bus, so that with a classic pub-sub pattern all the interested projections are able to subscribe the message and do the proper work in response to it.
(Emphasis added)
This assumption here is the one that you want to push back on. Pub/Sub can work for consumers that only care about a single message in isolation. Consumers that need state should be consuming histories, not events.
In the irredeemably non-optimized case, a consumer reads the entire history of ordered events each time it runs, and then processes them all.
An optimized version of this is that the consumer tracks where in the event history it left off, and runs an "all events since event X" query to find out what has happened. The irredeemably non-optimized case simply being the degenerate case of this: "all events since there were no events".
You might still see the pub/sub pattern applied, not to rebuild the read model, but to wake up the consumer to pull history as described above (in effect, it becomes a latency reduction mechanism).
There's nothing wrong with the consumer also having a bit of clever to recognize that the received event is the immediate successor to what is already known. This is normally accomplished with meta data attached to the event, indicating its position in the history.
So in your competing consumer scenario, you might see two different read behaviors at work. The receiver of E1 determines that E1 is the immediate successor of the previous state, and simply goes to work. The receiver of E2 sees from the metadata that at least one event is missing, so refreshes its copy of the event stream, receiving in return the sequence [E1,E2], which it then consumes.
Some references
At the DDD Europe conference, I realized that the speakers I talked with where avoiding Pub/Sub whenever possible. -- Raymond Rutjes, 2016
Greg Young, Polyglot Data (2014); Greg talks a bit about the benefits of pull.
Best Answer
I can't find any authoritative answer between the difference between the two terms: "event log" and "event store". Martin Fowler uses both terms in this post but it's not clear if there is a difference. I think one cannot compare them, they are somewhat orthogonal or from different domains. An Event store is an Event log, that is, a log/list of all events that happened, ordered by the time they occurred.
An Event store is an event persistence that should have the following characteristics:
An Event log is just a list of events that could have the following characteristics:
So, one could have an event persistence that is an Event store and an Event log, at the same time, at the cost of scalability, of course.
We can notice that an Event log is something that is needed more by the ReadModels because it's easier to design a ReadModel that processes the events in the order they occurred, across all streams.
The "source of truth" is the entity that owns the data, that can guarantee the correctness of the data. For example, the source of truth for the number of items in stock showed on the UI of an online store is the physical warehouse, not it's Event store/database. You cannot say that a something is a source of truth for some consumer; it is or it is not a source of truth, a producer of some truth; it's irrelevant who consumes the truth when deciding that some source is the source of truth.