On Saga's, projections and replays...

Hi,

When implementing replays we noticed that for a processing group which contains at least one saga none of the tracking event processors can be replayed (A combination of AbstractSagaManager.supportsReset() and TrackingEventProcessor.resetTokens(…)).
We did not expect this as we thought an @AllowReplay(false) on the Saga event handling methods would be sufficient.

We have quite a few saga’s that depend on data in projections. These projections are usually specific to the saga’s involved as our processing groups are organized around business concepts/problems.
Storing state into the saga is not an option as a part of the events happen before the saga gets started.

The only way to make these projections replayable would be to extract them to separate processing groups. But this will introduce race conditions as the projection might not be updated when the event is handled by the saga.

What is the recommended way to deal with this problem?

Kind regards,
Robin

Hello Robin,

You should not put your read model access in the saga, saga in my opinion, is a domain service, which controls how core concepts of your domain communicate with each other. It is essentially a process manager for your aggregates, something like a transaction that you can revert if something goes wrong with your business logic. The projection model is just for displaying the view, projecting what your core domain did using events emitted by command handlers of aggregates that were emitted by sagas or application services, it has nothing to do with behavior.

You mention that storing state for saga is not an option because the saga has not been started yet, well, just start it, saga may live from milliseconds to days it is fine to start at any time you want.

Cheers

Hi Robert,

If a saga is not allowed to query a read model then how would you tackle the following problem:

In the system there are the following aggregates:

  • Person with 2 states: active and inactive for a given period.

  • MonthlyPayment: active persons should get paid an amount of money each month. This amount changes each month. This is modelled as a batch process that runs each month, queries for the active persons of that month and produces payments for them.

  • Debt: from time to time corrections can occur in the state of a person. If a persons becomes inactive for a given period but he/she was already paid for that period. We should do a correction. A correction involves many steps: we should create a Debt in the system for this Person, we should notify the person by mail about this Debt. We expect an acknowledgement of receipt of the phyiscal person within 2 weeks. If not another process will kick in etc etc.

We would implement this as one processing group:

  • containing a TEP that builds a projection of all the payments ever done
  • containing a Saga that would start on PersonDeactivated, checks - using the projection - whether this person was paid or not and creates a Debt for the amount paid etc etc

If we would let the saga start earlier (lets say on MontlyPaymentMade) we would have a lot of saga’s (> 1 000 000) in the system - probably never becoming active again as they should only do work once a correction needs to be done.

Robin

Hi Robin,

sorry for the late reply. We’ve been quite busy on several fronts.
I’ve done a quick investigation, and at first, I thought the issue was that the replay was rejected. However, on inspecting the code (and after re-reading your message, I think that’s what you’re seeing too), I noticed that having a replayable component will cause the Saga to also replay all its events.

This is an issue in the MultiEventHandlerInvoker, which does take into account that some component may allow replay, while others don’t. Unfortunately, it doesn’t take into account that components that don’t allow replay, shouldn’t get redelivered messages.

I have created an issue to track the progress: https://github.com/AxonFramework/AxonFramework/issues/1111

Cheers,

Hey Robin,

I’ll try to help you with this problem, but keep in mind that i’m preety new in the DDD concepts.

I’ve said that saga should not depend on read model, the projection because in my opinion (I’ve also seen someone else with vast knowledge about thoose things saying similar thing, but i cannot remember who he was exactly or where it came from), the projections should be separated completely from the write model, the write model is tightly connected with your domains and so the read model should not affect your domain process.
It might be that your language isn’t quite right because as I said, for me, projections, read models are completely separated thing, and might not be in your use case.

I see that your projection contains the state of payments for each users which, from my perspective it is essentially an aggregate, not a read model, it has a state that is used for checking the constraint on for example if person was paid or not.
So instead querying the read model, you would just communicate with it, sending the commands and receiving appropriate events in your saga, it would just communicate between Person and AggregatOfPayments.

It is quite hard for me to model out exactly the solution, but i think we had miscommunication on the ‘projection’, ‘read model’ language, as long as you keep everything consistent in your domain model and manage inconsistency inside saga, its all ok.

Its also a question, whether you use event sourcing approach, it could be quite tricky to model out such system without boilerplate code, as for me i don’t really trust the event processors so i do not use projections inside my model to create some state that will be used in my core domain. Everytime i need something consistent, i create an aggregate, if it requires passing around similar commands between aggregates, i just do it.

To put it simply: Everything is possible with just aggregates and sagas between them, read model, projection, should be done in a way so it can be ran separately.

There’s one link i’ve found about the saga and read model: https://stackoverflow.com/questions/34284697/why-cant-sagas-query-the-read-side