How to Step Back and Forth Through History?

Hi,

I am still somewhat new to the CQRS/ES patterns and the Axon framework. I can see that the Axon framework gives us a great deal of support in terms of implementing these patterns within the context of Java Spring based REST services.

Various articles and postings I have come across seem to suggest that one of the advantages of using CQRS/ES is that it is easy to step through the history of an entity by re-applying events one by one to that entity. This lead me to think that this ability to step through an entity’s history may be reflected by specific constructs within the Axon framework. However, I don’t think I found such constructs.

In exploring how we might go about providing the ability to step through the history of (for example) a case, I ended up doing it like this:

  1. REST endpoint creates a Query object that includes an event sequence number and aggregate ID amongst its query parameters
  2. REST endpoint sends the Query to the query gateway and waits for the case to be returned to it, at which point it returns the case to ts client (as JSON)
  3. @QueryHandler annotated method on a Handler class calls readEvents(aggregate ID) on the EventStore to obtain the stream of event messages that have occurred so far for the case identified
  4. @QueryHandler method iterates through the event messages read until it reaches the event with the sequence number specified and directly applies the event payload of each such message to a Case object read model view of the case that is built up in memory
  5. Once the iteration is complete, the @QueryHandler method returns the Case object so built to its client
  6. The Case object is returned back through the call chain ultimately to the REST client

REST Query Gateway Handler EventStore Case

create | | | | |
--------->| | | | |

send | | | |
----------------------->| | | |

invoke | | |
-------------------------------------->| | |

create |
---------------------------->|

events = | |
readEvents | |
----------->| |

for | |
each | |
event | |

applyEvent |
---------------------------->|
Case | | | | |
<--------------------------------------| | |

With hindsight, this solution does not seem that complex, but I wonder whether I may have missed anything in Axon that could make it even simpler?

Thanks,
Vaughan Jackson.

Corrected diagram :):

REST Query Gateway Handler EventStore Case

Hi Vaughan,

in my opinion, the “stepping through history” is more a figure of speech. Showing the different states that an Aggregate went through in the UI, is actually a design smell. Effectively, you’re using the Command Model to provide information (i.e. do a Query). In CQRS, this is what query models are for.

What happens, in general, is that a specific view model is created, which shows different states during the lifecycle of the application. This is just a model (designed to maintain historic state as well) that is projected from the event streams.
The advantage of Event Sourcing, is that you’re guaranteed to have a correct Event Stream from which you’ll always be able to generate these projections, even in retrospect.

Hope this helps.
Cheers,

Allard

Hi Allard,

Thank you for your help. I may have misunderstood your point, but it seems to me from what you have said that I am examining the state of an entity at different points in the sequence of events correctly. The Case I refer to is an entity class that is otherwise used to represent the state in a read (query) model projection too. It is not an aggregate. The Case object is built up in memory by code invoked by a @QueryHandler annotated method.

Thanks again,
Vaughan.

Hi Vaughan,

that was my misunderstanding: using a view model for this is perfectly reasonable.
The reason it’s not there in Axon yet, just out of the box, is most likely because it hasn’t managed to climb up far enough on the priorities list. Also, certain things are really easy when building a project-specific solution, but become very hard to develop for the “general case”.

Cheers,

Allard

Hi Allard,

OK, thank you for clarifying that. I appreciate that coming up with a general solution in these cases is always much harder than just doing a one-off.

Regards,
Vaughan.