Event Sourced sagas?

Hello,

This might seem quite odd but im struggling with saga state persistance.

I know that axon saves saga state to the db as well as a tracking token for it, so as long persisted and events are properly tracked everything works fine.
However when i code i try to do it with purity of event sourcing, meaning the events are pure source of truth of everything. To put it simply i would want to saga be in expected state even if i purge mongo associations tracking processors etc.

Everytime i purge or remove the tracking token the saga will be replayed from the start, which brings few issues:

  1. Sometimes i introduce new aggregate creation within saga, or other unique identifier that is used across aggregates, such identifier needs to be unique and well, randomly generated. Everytime i restart saga it obviously is regenerated hence, every ‘uniqueness’ check is passed which is not ideal for once delivery constraints.
  2. The command handlers are published twice so the consistency checks need to be done inside every receiver of such event.

At this moment im using the ‘aggregate+saga’ approach, which is basically creating aggregate as ‘consistency’ boundary for the saga, it just holds the state and every command i just dispatch there, then i receive confirmation event that i can actually dispatch the desired command and so i do inside the saga.

It seems for me that it could be possible to make such ‘event sourced’ saga, any opinions on this or maybe other ways of solving my desire of making everything event sourced would be appreciated.

Thanks!

Hi Robert,

Interesting idea you have there, that on face value doesn’t sound to weird.
Implementation wise it does have quite some complication I think however.
When Event-Sourcing, you want to have a stream of the deltas that constitute that model.

For an Aggregate, this is simple, as it publishes those deltas as events itself and in doing so those events are tied to a given Aggregate instance through the Aggregate Identifier.
Thus, it’s easy to retrieve a stream of all those events, as you can query them based on the Aggregate Identifier.

Sagas on the other hand handle the right events through Association Values.
The first association value is set for you in the @SagaEventHandler annotation, but subsequent associations you’d require are to be registered by the user him-/herself.
Additionally, those associations come from the internals of the Event payload (e.g. the Event class you’d create yourself), instead of being a dedicated column.

That being said, if you’d thus have to Event-Source a Saga, you’d first have to retrieve all it’s Association Values, and for each of them you’d have to query all the payload columns for the specific key/value pair.

From a SQL perspective, that would not be a simple and quick query to perform I think.
There are likely ways around the approach I’ve just described, or ways to simplify it.
However, I don’t think that’s a trivial thing to do.

A part from the conceptual mombo-jumbo, I do have a suggestion to resolve the dropping of the TrackingToken of your Saga resulting in it’s replay.
You can configure a TrackingEventProcessor’s token to be initialized at a different spot in the stream.
By default, it will create a head token, thus resulting in the replay.
You can however also create a token at a given point in time, or a tail token (at the end of the stream).
I think the latter is a suitable configuration in your scenario to ensure that your saga do not get replayed.

For configuring this, I suggest you have a look at the ‘TrackingEventProcessorConfiguration’ class.

Hope this helps!

Cheers,
Steven

Hey Steven,

Thank you for the response, again, it opened a door to the opportunity for manual token association which may help me in the future.

The issue i had, i resolved by re-modelling few parts of my application so it feels more natural and even if i re-send the command the aggregates ensure the consistency of the system so the events wont be published so at the worst case scenario i would just have bunch of dummy commands. The creation thing i’ve solved by introducing the UUID v3/v5 approach which generates me UUID that is linked to specific namespace which in my cases is basically the ‘parent identifier’ so the creation commands are idempotent this way.

However I’d like to point out another thing which might not be related to the main issue i’ve described but i dont think creating another thread for it would be viable since it still holds to the saga context.
The problem I’m facing right now are past events and their associacions.
My overall idea of saga is that it can used to basically compose list of events in a way so they ‘automate’ intended use-case, because well, i can send commands one by one based on the query model from the application service, but to make it domain automated behavior i should put it inside saga. So every management of for example Player creation when the User has been registered is handled by the saga.

Such automation is clean and nice if event that has the root cause eg. UserCreated is about to be followed by dispatching PlayerCreate which would have idempotent identifier of player composed from user id.
Things start to get messy if some creation event depend on multiple events that do not have predictable associations, let me clarify that by simple example:

Consider Aggregates:

User, Ticket

Consider Events:

UserCreated, TicketCreated

Consider behavior:

Give the Ticket to every user when the Ticket has been created

Now, this fits under the umbrella of BulkUpdate, because i should send out UserGiveTicked(ticketId) to every user that has been created and will be created.
It seems like this behavior fits into domain service that would track the events and persist what has occured in the system which i could obviously do by introducing additional write model but here’s a tricky solution too:

Create saga that ignores the association property, or just accepts first (which is not supported)
The ignore association is simply done by creating association resolver that just accepts all so the saga looks like:

@Start + @Association(userid) -> UserCreated -> Persist User Id
@AcceptAll -> TicketCreated -> GiveTicket(evt.ticket, persistedUser)

Above works for previously created accounts, since it accepts ticket created events after user has been created
To solve for newly created users i need to flip the logic.

@Start + @Association(ticketId) -> TicketCreated -> Persist Ticket Id
@AcceptAll -> UserCreated -> GiveTicket(evt.user, persistedTicket)

Now, the problem with that is first it would create infinite saga that would never end, and second it would create alot of thoose sagas for the previously created users (each user waits for the ticket creation so we can give one to him)
If i had a chance to compose the association properties such that it would start the saga by the UserCreated event or TicketCreated event but then succeeding events would create new saga instance that still has the start state persisted it would be golden.
Because in such way i would have clear solution to the issues where i need to create/modify something in a system using events that are not linked together by any means, it brings coupling to one place which is nice and also the sagas would end as they should.

But yeah, this is me finding myself a problems, i might be horribly wrong so if anyone has a tip on how to compose disconnected events/components together it would make my life alot easier. (But yes i do not want to introduce any new persistance for the handling as i mentioned by the domain service approach)

EDIT: Oh, I’m sorry i just realized that i actually DID create separate thread for it, i guess my brain had a memory leak. Sorry for that.