Event sourcing makes the current state easy to ask for. The more useful question is what the state looked like at a specific moment.
I ran into this recently in an Accounts Receivable module. A customer invoice is born when a sales order ships. At that moment, every line cost is an estimate pulled from the lots on the order. Later, the vendor invoice arrives, we record the actual cost for those lots, and the invoice’s total cost shifts.
That means the same invoice has different costs at different points in its event stream. For the users, the difference matters.
Why invoice cost is not static
When the invoice is first created, the business does not yet know the true cost. The system records the best estimate available from the lots on the sales order. Later, after the vendor invoice is processed, the actual costs for those lots become known. The invoice receives a cost-updated event and the item lines are adjusted.
This is not a bug. It is how the business actually works. An invoice’s cost depends on information that arrives over time.
The problem is that most read models only show the final answer. They give you today’s cost. They do not give you the cost as it existed before the latest vendor invoice landed.
Rebuilding the projection at a boundary
Marten can rebuild an aggregate up to a specific event version. You stop at a given version number. Events after that point do not apply.
We used this in the invoice cost query. The query accepts an optional version. When the version is supplied, the projection is rebuilt only up to that event boundary. When it is omitted, the aggregate is hydrated to its latest state. The same read model answers both questions.
A small query parameter in the URL changes what the cost screen can show.
Why the UI matters
Without the ability to load a projection at a specific version, the cost screen could only display today’s numbers. A user who wanted to know why a cost changed would have to dig through raw events or trust the current figure and move on.
With versioned loading, the screen can answer ordinary AR questions directly. What was the cost before the vendor invoice was applied? Why did this invoice cost change? Those questions become a matter of pointing the same query at a different event boundary.
A narrow but real use case
The technique is generic. The event store already exposes it. In this codebase, though, only the invoice-cost feature needed it.
That is the part that stuck with me. The capability has to be there, but the business domain has to ask the question before it becomes useful. In Accounts Receivable, the question is real. Users need to see the same invoice at different points in time and understand why the numbers moved. The feature is not clever for clever’s sake. It exists because the domain genuinely needs it.
What I’m learning is that event sourcing is often framed as an audit story. It is. But its more practical value is letting a user look at the same aggregate at two different points in time and make sense of a changing number.





Leave a Reply