Skip to content

Port/axon 5.1.x main#4617

Merged
hatzlj merged 85 commits into
mainfrom
port/axon-5.1.x-main
May 29, 2026
Merged

Port/axon 5.1.x main#4617
hatzlj merged 85 commits into
mainfrom
port/axon-5.1.x-main

Conversation

@hatzlj
Copy link
Copy Markdown
Contributor

@hatzlj hatzlj commented May 27, 2026

Port of 5.1.x development to main, covering:

Conflicts:

  • axon-5/api-changes/11-class-reference.md
  • docs/reference-guide/modules/migration/pages/paths/snapshotting.adoc
  • eventsourcing/src/main/java/org/axonframework/eventsourcing/eventstore/DefaultEventStoreTransaction.java
  • eventsourcing/src/main/java/org/axonframework/eventsourcing/eventstore/SourcingCondition.java
  • eventsourcing/src/main/java/org/axonframework/eventsourcing/handler/SnapshottingEntityLifecycleHandler.java
  • eventsourcing/src/test/java/org/axonframework/eventsourcing/eventstore/DefaultEventStoreTransactionTest.java
  • eventsourcing/src/test/java/org/axonframework/eventsourcing/handler/SnapshottingEntityLifecycleHandlerTest.java
  • messaging/src/main/java/org/axonframework/messaging/eventhandling/processing/streaming/pooled/MergeTask.java
  • pom.xml

MateuszNaKodach and others added 30 commits May 11, 2026 23:26
…es (#4558)

Split the monolithic axon-5/api-changes.md into 12 focused files under
  axon-5/api-changes/, easier to process by people and AI Agents.
…n 4.x to 5.x migration

- Add `ConfigureEventSourcedAnnotationKotlinTest` to verify Kotlin-specific behaviors in migration recipes.
- Introduce `ConvertCommandHandlerConstructorToCompanionObject` recipe to rewrite Kotlin `@CommandHandler` constructors into companion object methods meeting Axon 5.x requirements.
- Add `ConvertCommandHandlerConstructorToCompanionObjectTest` to validate the recipe.
- Add `MigrateAggregateTestFixtureSetupKotlinTest` to confirm Kotlin-compatible test fixture setup migration with proper class literal handling.
- Replace fully qualified references with simple imports for `@CommandHandler` and `EventAppender`.
- Update Kotlin migration recipes to ensure imports are correctly added for compatibility with Axon 5.x.
- Introduce Kotlin upgrade step in migration YAML to support dependency and plugin alignment with Kotlin 2.x.
- Improve handling of `CommandHandler` in companion object transformations to prevent import clashes during migration.
- Introduce `AddEventTagAnnotationKotlinTest` to validate annotation of Kotlin primary constructor parameters.
- Update `AddEventTagAnnotation` recipe to handle Kotlin-specific constructs such as `data class` primary constructors and `val/var` fields.
- Improve annotation handling for Kotlin sources to ensure `@EventTag` is correctly applied and formatted.
- Add test to validate fallback annotation placement for mismatched Kotlin field names.
- Refactor `AddEventTagAnnotation` to properly handle Kotlin `data class` field declaration order.
- Fix `isFirstFieldInClass` implementation to ensure correct field lookup in Kotlin and Java classes.
Closes the Gradle gap in `axon-migration` so the BOM rename + version
pin works end-to-end on Maven *and* Gradle projects (Groovy DSL,
Kotlin DSL, plain `platform(...)`, Spring Dependency Management
`mavenBom(...)`, and the Cinema-style `extra["axonFrameworkVersion"]`
+ `${property("...")}` indirection).

- Ship `migration/init.gradle` and document the Gradle invocation
  (`gradle rewriteRun --init-script ...`) in the module README.
- Compose `gradle.ChangeManagedDependency` and
  `gradle.ChangeExtraProperty` into the BOM YAMLs so Spring DM imports
  and Groovy `ext.*` version properties get rewritten alongside the
  existing Maven and `gradle.ChangeDependency` legs.
- Add `MigrateKotlinDslBomImport`, a small Kotlin-DSL-only recipe that
  fills two upstream gaps: `SpringDependencyManagementPluginEntry`
  doesn't extract version variables from `K.StringTemplate` whose
  embedded expression is `property("...")`, and `ExtraProperty` doesn't
  match the Kotlin-subscript `extra["..."] = "..."` shape. The recipe
  rewrites the `groupId:artifactId:` prefix in `mavenBom(...)` while
  preserving the `${property(...)}` indirection, and updates the
  matching `extra["..."]` literal in the same pass.
- Cover the new Gradle paths with `Axon4ToAxon5BomGradleTest` (Groovy
  + Kotlin DSL, both free and commercial legs, including the
  Cinema-style fixture). Tests attach a synthetic `GradleProject`
  marker rather than spinning up a Gradle daemon via `withToolingApi()`,
  keeping them lightweight (no `gradle-tooling-api` test dep needed).
- Expand annotation rewrites: add `@AggregateRoot` → `@EventSourcedEntity`, and clarify the removal of `@AggregateIdentifier` in favor of `tagKey` + `@EventTag`.
- Document unconditional removal of `@CreationPolicy`, including `AggregateCreationPolicy`, and explain handling for `CREATE_IF_MISSING` and `ALWAYS`.
- Provide additional guidance on lifted `@RoutingKey` and placement of `@EntityCreator` in constructors.
…nTestFixture` cleanup

- Introduce `AddAxonTestFixtureTearDown` recipe to automatically generate an `@AfterEach tearDown()` method calling `fixture.stop()`, ensuring proper lifecycle management for `AxonTestFixture` in JUnit 5 tests.
- Skip classes with existing `@AfterEach` methods or methods named `tearDown` to avoid duplication.
- Limit changes to Java sources only; Kotlin support to be addressed separately.
- Add comprehensive tests to verify correct migration behavior and idempotence.
…@InjectEntity` in GiftCard example

Replace incorrect annotation in migration guide example to ensure proper entity injection.
fix(docs/migration-paths): update annotation from `@InjectState` to `@InjectEntity` in GiftCard example
The way a ComponentDescriptor describes a certain component, and whether it fits in a String or not, is entirely implementation dependent. Therefore, the describe() method should not be placed on the ComponentDescriptor interface.
Co-authored-by: Laura Devriendt <94172218+laura-devriendt-lemon@users.noreply.github.com>
…describe

Remove describe method from ComponentDescriptor interface.
Rework snapshotting to allow single request snapshot + events streams
Replace placeholder `@author Axon Framework Team` and `@since 5.2.0`
across all migration recipe classes with the correct values
(`@author Mateusz Nowak`, `@since 5.1.1`). Also renames the test class
`Axon4ToAxon5TestTest` to `Axon4ToAxon5TestFixtureTest` to read more
naturally (reviewer suggestion); the corresponding file move ships in
the same change so the new file is compilable at HEAD.
The Gradle init script was missing the standard Axon Framework header.
Aligns with the rest of the module.
…ming into umbrella

- `axon4-to-axon5-extension-tracing-opentelemetry.yml`: reduced to a
  placeholder no-op. Revisioning of the distributed-tracing extension
  is slated for a later release (issue #3594); doing the package move
  now would land ahead of the AF5 integration.
- `axon4-to-axoniq5-event-streaming.yml`: corrected the description.
  `MultiStreamableMessageSource` has existed since AF 4.2; the new
  Axoniq Framework 5 module consolidates that pattern rather than
  introducing a wholly new concept without an AF4 counterpart.
- `axon4-to-axoniq5-postgresql.yml`: removed entirely. The Axoniq
  PostgreSQL connector is new in AF5 with no AF4 source to rewrite,
  so an OpenRewrite recipe adds no value. README updated accordingly.
- `axon4-to-axoniq5.yml`: wired `Axon4ToAxoniq5EventStreaming` into the
  commercial umbrella so the placeholder runs in the right order once
  it gains class-level mappings.
…all sites

`AddEventTagAnnotation`'s scanner previously discovered event payload
types from `@EventSourcingHandler` method parameters only. That misses
a valid AF4 pattern: an event published via `AggregateLifecycle.apply`
but never re-sourced in the entity (no matching `@EventSourcingHandler`).
Without `@EventTag`, AF5 cannot determine the aggregate-id tag for that
event at runtime.

The scanner now also walks `AggregateLifecycle.apply(...)` invocations
anywhere in the entity body and adds the first argument's payload type
to the accumulator alongside the handler-based entries. The two sources
union into the same map, so events that are both applied AND sourced
remain handled exactly as before.

New test `Axon4ToAxon5EventSourcingTest#taggsEventPublishedViaApply\
EvenWithoutEventSourcingHandler` covers the apply-but-not-sourced case.
…ension tests

- BOM tests: bump sample starting version from 4.11.0 to 4.13.0 to
  match the current AF4 LTS line.
- Reactor / spring-aot extension tests: bump sample version to 4.12.0
  (the latest release of each extension on Maven Central).
- Micrometer extension test: replace `GlobalMetricRegistry` (which does
  not exist in AF5) with `MessageCountingMonitor` so the asserted
  package rename references a class that actually ships in the AF5
  metrics-micrometer module.
…chInterceptor` signatures

`MessageHandlerInterceptor.handle(UoW, InterceptorChain) -> Object` becomes
`interceptOnHandle(M, ProcessingContext, MessageHandlerInterceptorChain<M>)
-> MessageStream<?>` (and likewise for the dispatch interface). The method
body is intentionally left untouched: references to the dropped `unitOfWork`
/ `interceptorChain` / `messages` parameters turn into unresolved-symbol
compile errors, surfacing every call site that needs human review. A
class-level `// TODO #LLM` comment points to the migration path doc.

The signature swap goes through a sub-parsed stub class so the rewriter can
take real `J.MethodDeclaration` LST nodes for the new return type, name,
and parameter list. The unused `UnitOfWork` import is force-removed even
though the body still references its identifier — the body's `uow.X(...)`
is a compile error anyway, and the user's IDE will clean up further unused
imports during the manual body migration.

Lifecycle-hook semantics (`onCommit` vs `runOnAfterCommit`, no AF5
equivalent for `onRollback`) and the `interceptorChain.proceed()` → `chain
.proceed(message, context)` shift are intentionally NOT mechanized — a
blind rewrite would silently land semantically wrong code in non-trivial
interceptors.

Scoped to fire only on classes that actually carry the AF4 `handle` method,
so empty-body fixtures and abstract bases stay untouched.
…ng `AppendCondition` calculated from sourcing

Allow users to control the AppendCondition used at commit time, beyond
what automatic sourcing provides. This enables two primary use cases:

1. Appending without sourcing — enforcing uniqueness constraints without
   first sourcing events (e.g., ensuring a course name is unique by
   checking that no matching event exists since ORIGIN).

2. Narrowing (or broadening) the append condition — sourcing broad
   criteria for state but restricting which events are considered
   conflicting at commit time.

Production changes:

- AppendCondition: add replacingCriteria(EventCriteria) abstract method
  that preserves the consistency marker while replacing the criteria.
  Named "replacingCriteria" rather than "withCriteria" (as originally
  planned) because the interface already has a static factory method
  withCriteria(EventCriteria) and Java prohibits a static and abstract
  instance method with the same signature on a sealed interface.

- NoAppendCondition: both wither methods (withMarker, replacingCriteria)
  now return DefaultAppendCondition instead of throwing. withMarker
  previously threw UnsupportedOperationException, but havingAnyTag()
  with a real marker is a valid, useful condition meaning "any event
  after this marker is a conflict." This makes NoAppendCondition
  composable (e.g., AppendCondition.none().withMarker(m) works).

- DefaultAppendCondition: implement replacingCriteria mirroring the
  existing withMarker pattern (returns this if criteria unchanged).

- EventStoreTransaction: add overrideAppendCondition(UnaryOperator)
  interface method. Input is AppendCondition.none() when no sourcing
  happened; returning none() bypasses conflict detection. Multiple
  calls compose (each receives the previous call's output).

- DefaultEventStoreTransaction: implement override using a ResourceKey
  stored in ProcessingContext (consistent with appendConditionKey,
  eventQueueKey, appendPositionKey). Uses updateResource for atomic
  composition. Override applied in attachAppendEventsStep after marker
  resolution but before appendEvents, with DEBUG logging.

- InterceptingEventStore: delegate overrideAppendCondition through
  InterceptingEventStoreTransaction.

Test coverage:

- DefaultAppendConditionTest: 2 tests for replacingCriteria
- NoAppendConditionTest: 3 tests replacing the old throwing test,
  verifying composability of withMarker and replacingCriteria
- DefaultEventStoreTransactionTest: 7 unit tests covering override
  without sourcing, after sourcing, chaining, criteria replacement,
  bypass, normal flow, and null rejection
- StorageEngineBackedEventStoreTestSuite: 3 integration tests covering
  append-without-sourcing with conflict detection, narrowed criteria
  avoiding false conflicts, and bypass via none()

(cherry picked from commit f89b691)
…ltEventStoreTransaction`

(cherry picked from commit 526a252)
…veAppendCriteria method

(cherry picked from commit 94b5990)
… integration tests

Replace generic CourseUpdated with CourseCreated, StudentSubscribedToCourse, and
StudentUnsubscribedFromCourse event types to better illustrate real-world use cases:

- shouldAppendWithOverriddenConditionWithoutSourcing: course name uniqueness
- narrowedCriteriaShouldAvoidFalseConflict: subscription/unsubscription narrowing
- overrideReturningNoneShouldBypassConflictDetection: duplicate course name bypass

Add sourceCount() helper to avoid CourseUpdated-specific payload conversion.

(cherry picked from commit 28616b9)
… vs Aggregate hierarchy

The OverrideAppendCondition integration tests use DCB-specific patterns
(ORIGIN-based uniqueness, criteria narrowing by event type, conflict bypass)
that are not supported by aggregate-based storage engines. The aggregate-based
JPA engine uses sequence-number conflict detection via unique constraints,
ignoring AppendCondition criteria entirely.

Introduce a test suite hierarchy:

  StorageEngineBackedEventStoreTestSuite        (common: sourcing, streaming, tokens, conflicts)
  ├── DcbBasedStorageEngineBackedEventStoreTestSuite   (DCB: overrideAppendCondition tests)
  │   ├── InMemoryStorageEngineBackedEventStoreTest
  │   └── AxonServerStorageEngineBackedEventStoreIT
  └── AggregateBasedStorageEngineBackedEventStoreTestSuite  (aggregate: placeholder)
      └── AggregateBasedJpaStorageEngineBackedEventStoreIT

The 3 OverrideAppendCondition tests and DCB event records (CourseCreated,
StudentSubscribedToCourse, StudentUnsubscribedFromCourse) move from the base
suite to DcbBasedStorageEngineBackedEventStoreTestSuite. The base suite
exposes eventStore, awaitLatch, and RESOLVER as protected for subclass access.

(cherry picked from commit 8f0cd43)
smcvb and others added 16 commits May 22, 2026 13:07
[Port | #4594] Validate `ProcessingContext` is committed in `SimpleQueryBus` on `emitUpdate`, `completeSubscriptions`, and `completeSubscriptionsExceptionally` invocations
Move AF4 dependencies inside a profile. The release flow will otherwise
spot the framework dependencies, which have the same groupId, and expect
 the version of those dependencies to align with the version to be
 released.
Combine all the ListAppender using tests. If the tests are spread out,
they will interfere with one another and fail.
…es-under-profile

Move AF4 dependencies inside profile for `axon-migration` module
Combine all the `ListAppender` using Snapshotting tests in single `@Nested` class
Copy AF4 dependencies during generate-test-resources. By doing so, we ensure that the release process cannot find the AF4 dependencies, on which it will trip, because we didn't upgrade the versions to the release version.
…gh-copy-in-build-plugin

Copy AF4 dependencies during generate-test-resources
…oblem

Axon4ToAxon5BomTest and Axon4ToAxon5BomGradleTest required OpenRewrite
to resolve axon-framework-bom and axoniq-framework-bom at the
to-be-released version. During a Maven release those artifacts don't
exist yet, causing the tests to hang for a long time.

The BOM coordinate rename is a mechanical text transformation driven
entirely by the recipe YAML definitions; there is no custom logic to
test. Testing that an existing OpenRewrite recipe executes correctly
is not the responsibility of this module.
ConfigurerModule and ProcessingGroup both live in axon-configuration,
not axon-messaging. Without it on the test classpath, OpenRewrite cannot
resolve their types and Axon4ToAxon5CommonTest fails with
"LST contains missing or invalid type information".
…-test

fix(migration): remove BOM tests that fails due to chicken-and-egg problem
Add release notes for the 5.1.1 release to the reference guide
@hatzlj hatzlj added this to the Release 5.2.0 milestone May 27, 2026
@hatzlj hatzlj self-assigned this May 27, 2026
@hatzlj hatzlj requested a review from a team as a code owner May 27, 2026 16:23
@hatzlj hatzlj added Priority 1: Must Highest priority. A release cannot be made if this issue isn’t resolved. Type: Port Use to refer to an issue or pull request that ports logic from one branch to another. labels May 27, 2026
@hatzlj hatzlj requested review from MateuszNaKodach, hjohn and zambrovski and removed request for a team May 27, 2026 16:23
@sonarqubecloud
Copy link
Copy Markdown

@hatzlj hatzlj requested a review from abuijze May 27, 2026 16:42
Copy link
Copy Markdown
Contributor

@MateuszNaKodach MateuszNaKodach left a comment

Choose a reason for hiding this comment

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

I scanned it and I hope it's OK!

@hatzlj hatzlj merged commit 7be1ef4 into main May 29, 2026
11 checks passed
@hatzlj hatzlj deleted the port/axon-5.1.x-main branch May 29, 2026 06:01
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: Port Use to refer to an issue or pull request that ports logic from one branch to another.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants