Skip to content

[#4991] Rework SagaLifecycle to resolvable parameter - #4997

Merged
MateuszNaKodach merged 12 commits into
mainfrom
enhancement/4991/saga-lifecycle-replacement
Sep 3, 2026
Merged

[#4991] Rework SagaLifecycle to resolvable parameter#4997
MateuszNaKodach merged 12 commits into
mainfrom
enhancement/4991/saga-lifecycle-replacement

Conversation

@smcvb

@smcvb smcvb commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

This pull request reworks the SagaLifecycle to be a resolvable parameter instead of a statically invoked infrastructure component.

This is required, as the old approach used the ThreadLocal logic of the previous UnitOfWork to access the Saga, which is no longer an option with AF5.
Instead, it's something that's injected, the ProcessingContext.

Hence, this can be seen as one of the few breaking changes for users moving towards axon-legacy in 5.4.0, which is sadly mandatory.
To limit the break, the package of the SagaLifecycle is the same as well as the name.

The main difference for users is two-fold:

  1. It no longer has any static methods.
  2. It is injected as a parameter i.o. (statically) imported.

From a technical stance, the only SagaLifecycle implementation was the AnnotatedSaga.
Hence, this PR also pulls in that class, immediately adjusting it to be an EventHandlingComponent instead of an EventHandlerInvoker.

This portion has a large scoped outcome on this PR, as it had to pull in:

  1. The Saga interface, as it's used by the AnnotatedSaga.
  2. The @StartSaga, @SagaEventHandler and @EndSaga annotations, as we're dealing with the AnnotatedSaga
  3. The SagaMethodMessageHandlerDefinition to be able to wire the @SagaEventHandler correctly
  4. The AssociationResolver plus its implementations, as those are used by the SagaEventHandler and SagaMethodMessageHandlerDefinition alike
  5. The SagaCreationPolicy, as it's by the SagaMethodMessageHandlerDefinition
  6. The SagaModel and it's creation, as those are used by the AnnotatedSaga

Note that this PR does not fully implement all the EventHandlingComponent operations yet!
This PR is already bigger than I wanted, so I decided against doing that part here as well.
Expect a follow-up PR instead.

In doing the above, this PR resolves #4991.

Rough sketch to port the SagaLifecycle. Do so by adjusting the
SagaLifecycle by not having static methods, but plain methods.
Furthermore, let the Saga be an implementation of the SagaLifecycle.
From there, we should add the SagaLifecycle to the ProcessingContext
when handling a SagaEventHandler in the (Annotated)Saga. To wire the
SagaLifecycle, users can simply add a parameter for it with their
SagaEventHandler. Lastly, move other annotation-specific components from
 the stash to this project, as those are utilized by the annotation
 solution (in)directly.

#4991
@smcvb smcvb added this to the Release 5.4.0 milestone Sep 2, 2026
@smcvb smcvb self-assigned this Sep 2, 2026
@smcvb smcvb added Type: Feature Use to signal an issue is completely new to the project. Priority 1: Must Highest priority. A release cannot be made if this issue isn’t resolved. labels Sep 2, 2026
Remove the files that were now moved to axon-legacy

#4991
Move applicable tests and StartSaga

#4991
@smcvb
smcvb marked this pull request as ready for review September 2, 2026 08:57
@smcvb
smcvb requested a review from a team as a code owner September 2, 2026 08:57
@smcvb
smcvb requested review from MateuszNaKodach, jangalinski and zambrovski and removed request for a team September 2, 2026 08:57
Expand api-changes

#4991

@MateuszNaKodach MateuszNaKodach left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits, generally good job. Thanks for the PR! :)
I think we should wait with merging that till featsaga-store merge.

Comment on lines +79 to +80
// TODO
// TODO

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are those TODO about? I thinks supportsReset returning false is appropriate here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, totally right. This is a leftover that I missed to fix. Adjusted it, by overriding the supportsReset to return false.
Added, I defaulted the handle(ResetContext, ProcessingContext) method here as well, throwing the throw new ResetNotSupportedException("Sagas do not support reset"); exception from the AF4-version of that method.

private final AssociationValues associationValues;
private final String sagaId;
private final T sagaInstance;
private volatile boolean isActive = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is saga always accessed by single thread? I believe the LockingSagaRepository needs to care about that, am I right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, correct! The LockingSagaRepository will take care of that :-)

Comment on lines +29 to +30
* This is the {@link ProcessingContext}-scoped replacement for the Axon Framework 4 {@code SagaLifecycle}, which
* exposed the very same operations as {@code static} methods resolved through a {@code ThreadLocal}. Axon Framework

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's a replacement should it be since 5.4.0, or earlier version?
Regardless, I think the name "SagaLifecycle" is nice, just use instance methods instead of the static ones :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a quick doc-draft as the old version had practically no JavaDoc...I've fine-tuned it a little right now.
I've also marked it as since 3.0.0, as that's the accurate since tag.


@Override
public Set<QualifiedName> supportedEvents() {
throw new UnsupportedOperationException("TODO");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd place TODOs here with the issue number.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will do!

* @author Steven van Beelen
* @since 5.4.0
*/
public class SagaLifecycleParameterResolverFactory implements ParameterResolverFactory {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see tests for that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will add!

Comment on lines +77 to +81
testSubject.handle(matchingEvent, StubProcessingContext.forMessage(matchingEvent)).asCompletableFuture().join();
var nonMatchingEvent = new GenericEventMessage(new MessageType("event"), new RegularEvent("wrongId"));
testSubject.handle(nonMatchingEvent, StubProcessingContext.forMessage(nonMatchingEvent)).asCompletableFuture().join();
var unhandledEvent = new GenericEventMessage(new MessageType("event"), new Object());
testSubject.handle(unhandledEvent, StubProcessingContext.forMessage(unhandledEvent)).asCompletableFuture().join();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a few join() without timeouts here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will do!

Add deprecation warning to StartSaga annotaion

#4991
Remove accidental TODO

#4991
Base automatically changed from featsaga-store to main September 3, 2026 08:47
Correct reset support on Saga interface

#4991
Improve JavaDoc

#4991
Added issue numbers to todo

#4991
Add tests for the SagaLifecycleParameterResolverFactory

#4991
Ensure joins have a timeout

#4991
@MateuszNaKodach
MateuszNaKodach merged commit b7b562d into main Sep 3, 2026
6 checks passed
@MateuszNaKodach
MateuszNaKodach deleted the enhancement/4991/saga-lifecycle-replacement branch September 3, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority 1: Must Highest priority. A release cannot be made if this issue isn’t resolved. Type: Feature Use to signal an issue is completely new to the project.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Legacy support] Replacement for SagaLifecycle operations

2 participants