Your event log is your only source of truth. You cannot trust the data in the projections, because it is eventually consistent and does not have to be up-to-date. As you have mentioned, you can indeed get the latest state of your domain entity by reading all events related to that one specific entity and flatting them to your final representation.
So if an account can never be overdrawn, you need to know its balance, you need to calculate it in your domain object from the event stream. This is not only recommended but necessary.
Of course, if your projections are as simple as being the latest (eventually consistent) state representation of your entities, it might seem you are duplicating logic. However, the latest state of an entity is in most cases not the only representation of the entity, i.e. how you'd like to view it the data.
For different purposes you may (and with large systems are likely to) have different projections. Some can be entities, but other projections may be entire aggregations. With these aggregations you could then use an
MoneyWasDeposited event from all
BankAccount entities to update a monthly cashflow to see how much money is actually flowing through your system.
The strength of projections comes at the time when you find out you need different representations of - internally - the same data. Thanks to projection pre-building, increasing complexity of the view does not slow down load times for end users.
To followup on VoiceOfUnreason's answer, while e.g. sharing the logic for balance calculation makes sense in terms of DRY principle (so that you don't repeat yourself), when actually practicing ES, I have learned it mostly does not.
An event might look like this:
class MoneyWasDeposited: IEvent
public BankAccountId ToBankAccountId;
public Money Amount;
An event sourced aggregate then usually processed this event using a similar mechanism to this:
private BankAccountId id;
private Money currentBalance;
public void ReplayFromEvents(List<IEvent> events)
for (var event in Events)
private void ApplyEvent(IEvent event)
if (event is MoneyWasDeposited)
currentBalance = currentBalance.Add(event.Amount);
while your projection is actually likely to look something like this:
private MySqlConnection database;
public void HandleMoneyWasDeposited(MoneyWasDeposited event)
var statement = database.Prepare("
SET balance = balance + :depositedAmount
WHERE id = :id
So while your aggregate calculates the value in code from the event, the projection usually applies the change directly to your read-model store and let's the storage engine take care of the data modification.
As you can see, there's really no way to reduce duplication with this anymore, because the aggregate and projection use vastly different mechanisms to achieve the desired result.
Now, I am not saying you couldn't pull out the entire projection from the read-model store, update the read-model in memory and dump it back for update, but it's usually not the way how it's done, as it adds one unnecessary
SELECT and achieves virtually the same result anyway.
You may ask: The balance calculation logic is not in a single place. So what if someone, by accident, makes the logic in the projection incorrect?
Consider the scenario that somebody wrote a minus sign to the projections' balance update when money was deposited. This would mean that every time you deposited money to your account, you would suddenly have less.
You would then do the following steps:
- change the minus sign to a plus sign in the projection method,
- push the change immediately to production, so that all changes from now on are ok,
- create a new projection index in your read-model store, e.g.
- run another instance of bank account projection manager from the inception of your system and insert data into
- after the local projection finishes running, swap configuration on production to use the
bank_accounts_fixed data in place of the
- delete the original
And all your user's will suddenly see correct balances. You can do this because you know that the data in your event store is guaranteed to be correct (it should be).