AF5 model inspection - expose AF5 entity inspection over RSocket#146
AF5 model inspection - expose AF5 entity inspection over RSocket#146stefanmirkovic wants to merge 14 commits into
Conversation
…a Axon's CriteriaResolver
…pection # Conflicts: # framework-client/src/main/java/io/axoniq/platform/framework/eventsourcing/AxoniqPlatformModelInspectionEnhancer.java # framework-client/src/main/java/io/axoniq/platform/framework/eventsourcing/RSocketModelInspectionResponder.kt # framework-client/src/main/resources/META-INF/services/org.axonframework.messaging.core.annotation.HandlerEnhancerDefinition
CodeDrivenMitch
left a comment
There was a problem hiding this comment.
It's heading in the right direction, but I still see some problem with how the id types would be used in the frontend. There's a big assumption that there will only be one id, which is not the case.
This is a proposal PR to the original PR posed by Mirko, #146. It takes a different approach to the same problem, and functionally nothing has changed ## Why the proposal The original approach was very reflection-heavy, which is partly to be expected, but can also be improved upon. The more inner reflection is present, the more likely it's to break. In addition, the approach didn't account for entites in submodules for hierarchic contexts. As such, only top-level entities would be discovered. As this all requires very deep knowledge about how the Configuration of Axon Framework works, I decided to make this proposal PR to guide by example, instead of separate comments. ## Basics of the rewrite 1. The `UnitOfWorkFeactory.create()` is used during all sourcing and read operations. This way all parameter resolvers will work correctly. This takes away the need of manually invoking methods. 2. Repository instances are discovered at boot time. If it's an EventSourcingRepository, it's registeredd to the `RSocketModelInspectionResponder` 3. The `EventSourcingRepository` is rebuilt using reflection to be able to decorate the `EntityEvolver` with the `AxoniqPlatformEntityEvolver`. 4. Reading the event stream and entity state is now a `Repository.load(...)` operation. Several resources are put in the `ProcessingContext`, which the decorators respond to. a. `AxoniqPlatformEntityEvolver.BEFORE_CONSUMER`: If present, calls with the entity state before any evolve b. `AxoniqPlatformEntityEvolver.AFTER_CONSUMER`: If present, calls with the entity state after any evolve c. AxoniqPlatformEntityEvolver.MAX_INDEX: Stops evolving the entity after a certain index. ## Result With the new code a lot of the reflection-based code could be deleted. The RSocketModelInspectionResponder.kt shrunk by half its size. We have programmed against the interfaces of the framework, which are less likely to change. In addition I added tests that confirm it works.
…hell-proposal Drive model inspection through real UnitOfWork and support modules
… page-of-100 stays gzip-friendly.
|
…FE rehydrates it from the previous entry's stateAfter.
|
@stefanmirkovic Looks good to me. However, there's a failing test. Please resolve that before merging. |
CodeDrivenMitch
left a comment
There was a problem hiding this comment.
I just realized we can't merge this yet; We are missing the privacy options that we had in AF4.
… so payloads and state are redacted unless the operator opts in, matching the AF4 privacy contract.
|
@stefanmirkovic I'm good with the PR, when tests are succeeding we can merge |
Without an explicitly registered DomainEventAccessMode, the responder defaults to NONE and nulls out reconstructed state — making the two state-at-sequence assertions in the nested-module integration test fail. Mirror the sibling integration test's setUp and register FULL.
d69278d to
8634463
Compare
|



Adds RSocketModelInspectionResponder with four endpoints backed by AF5's StateManager and EventStorageEngine:
Tag keys are resolved from @eventsourced / @EventSourcedEntity annotations (including meta-annotations), falling back to the class's simple name. State reconstruction walks the full event stream to keep stateBefore accurate for the first event in the requested window, but only serializes snapshots for entries in [offset, offset + limit), bounding payload size regardless of stream length.
AxoniqPlatformModelInspectionEnhancer registers the responder via the ConfigurationEnhancer SPI. SetupPayloadCreator now reports hasStateManager so the platform can detect model inspection support.
Compound id + multi-tag support
Replaces the hand-crafted
EventCriteria.havingTags(Tag.of(tagKey, entityId))(single-tag only, breaks for multi-tag entities) with a delegation to the
framework's own
CriteriaResolver:resolveCriteria(entityType, entityId)extracts the live resolver from theregistered
EventSourcingRepositoryvia reflection, honoring any customresolver the application has configured. Falls back to constructing a fresh
AnnotationBasedEventCriteriaResolver(entityClass, idClass, configuration)lookup
(entityClass, idClass)pairdescribeIdFields(idClass)introspects records, Kotlin data classes / POJOs,and
@JvmInline value classwrappers. Simple types (String/primitives/UUID/enums/BigInteger/BigDecimal) return an empty descriptor list so the frontend
keeps a single input
deserializeEntityIdhandles primitives directly, unwraps Kotlin valueclasses through their public constructor, and lets Jackson deserialize
JSON-encoded compound ids into the real typed id before invocation
Configurationparameter (wired through theenhancer) so it can build resolvers on demand
ProcessingContextis passed asnull- verified safe for the defaultannotation-based resolver which doesn't read it; custom resolvers that do
throw NPE, which is caught and falls back to the legacy path (no regression)
Relates: https://github.com/AxonIQ/axoniq-platform/pull/538