query projection for given timestamp

Please consider this (thought in progress):
I store key/values for an aggregate to abstract from frequently and commonly used business data (JPA persistence). So for aggregate “1”, I execute AddValueCommand(“foo”, “world”) and can select value “foo” via graphQL from the projection model.
Now I update the value via AddValueCommand(“foo”, “hello”) … the projection is updated as well. So far so good.

But: I have the requirement that I can access historic values, so I query for “give me values for foo and bar for last Monday”.
Of course I have the metadata timestamp with the command/event handlers, but I wonder, what the best implementation would be.

1.) have Map of key,value,timestamp in Memory iterate with filter timestamp < :timestamp and reduce to latest value
2.) have an SQL table projection and do some fancy select foo where timestamp < :timestamp order by timestamp desc

Both would require manual implementation and (unnecessary?) filtering of all the data is not latest or in before given timestamp.

Since I already have the eventstore, maybe this can be done using “replay until timestamp” or “snapshot at midnight” … but its unclear to me
a) how this could be build and
b) if the performance is good enough when I constantly replay aggregate states for timestamps and just use a few of its properties in the graphQL return (not building a high performance system, its more or less a key/value cache, but non-the-less)

Has this been done before using axon eventstore? If so: how? Or is this a job for another tool?

Thanks for pointing me in the right direction.


Hi Jan,

the answer is simple: it depends. But let me try to elaborate.

Essentially, the event store contains all the information you require (important prerequisite), and you have a (client) component that can query using any time as parameter, and probably some other parameters as well. The projector (component building the view model, aka projection) is responsible for ensuring the queries receive a correct response absed on the events that it has seen.

There are basically two ways to do this:

  • pro-actively build a view model, which is updates as events arrive. In this case, you’ll need to build a model against which temporal queries can be executed. You’d have to keep track of all changes and filter them based on the timestamp. Depending on your domain, this may be feasible. If your model is already complex without the aspect of time, adding it is not going to help.
  • the other option is to build a model on-demand. This will simplify the model itself, as it only needs to deal with a single time: the one mentioned in the query. The challenge is now to read the events up to the indicated point in time. Not a challenge per se, as you can simply open a stream on the event store and read them. The challenge may lay in the fact that only a small portion of events is needed to reconstruct this model. If they belong to the same aggregate, you could read that aggregate’s stream, otherwise, you’d have to open the entire stream.

There is also a hybrid solution. You could have a pro-active model that contains very basic information that allows you to optimize the ad-hoc build of the model. For example, store the timestamp (or token) of the creation events of certain components and use that to open the stream at that specific location.

Hope this helps.


Hi Allard,

Could you show an example on how to “open a stream on the event store and read them”? A pointer would also be fine.

If you need a specific setting, how about the Axon-Bank example, with in-memory H2 store?



Hi Xiangming,

I believe what Allard is referring to with the ‘open a stream on the eventstore’, is the actual EventStore#openStream(TrackingToken) function.
This function stems from the StreamableMessageSource interface, which the EventStore implements.

If you’d wire in the EventStore were you need it, you can simply call the function to grab a stream staring at a given token.
Note that this operation opens op a stream of all the events from a given point in time.

If you need a stream for a given aggregate identifier, the EventStore#readEvents(String) function would suffice.

Hope this helps you out Xiangming!


Awesome! EventStore#readEvents(String) is what I am looking for, and it works!

Thank you so much Steven!