[#4291] feat(eventsourcing): EventStoreTransaction - allow overriding AppendCondition calculated from sourcing#4295
Open
MateuszNaKodach wants to merge 8 commits intomainfrom
Open
Conversation
…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()
…ltEventStoreTransaction`
…veAppendCriteria method
… 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.
EventStoreTransaction - allow overriding AppendCondition calculated from sourcingEventStoreTransaction - allow overriding AppendCondition calculated from sourcing
… 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.
…efault method and improve `NoAppendCondition` composability - Updated `replacingCriteria` in `AppendCondition` to a default implementation returning `DefaultAppendCondition`. - Modified `NoAppendCondition` to support composable operations like `withMarker` and `replacingCriteria`. - Adjusted documentation and method references for consistency and clarity.
…` in `InterceptingEventStoreTransaction`
…e() and improve test coverage Production: resolveAppendCondition now treats a null return from the override operator as AppendCondition.none(), bypassing conflict detection rather than propagating a NullPointerException to appendEvents. Test fixes: - Remove isInstanceOf(DefaultAppendCondition.class) assertions from NoAppendConditionTest — tests behavior via API (marker/criteria), not internal implementation types - Pre-populate events in overrideAfterSourcingReceivesDerivedCondition and overrideReplacingCriteriaPreservesMarker so sourcing produces a non-ORIGIN marker, making the marker assertions meaningful - Add overrideReturningNullIsTreatedAsNone test verifying the null handling
|
EventStoreTransaction - allow overriding AppendCondition calculated from sourcingEventStoreTransaction - allow overriding AppendCondition calculated from sourcing
jangalinski
reviewed
Mar 18, 2026
| extends StorageEngineBackedEventStoreTestSuite<E> { | ||
|
|
||
| @Nested | ||
| protected class OverrideAppendCondition { |
Collaborator
There was a problem hiding this comment.
Sonarqube says: JUnit5 test classes and methods should have default package visibility
jangalinski
approved these changes
Mar 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



DO NOT MERGE TO MAIN BEFORE 5.1.0 RELEASE.