Skip to content

[#4060] Align completeness between old Aggregate and new Entity description#4562

Open
laura-devriendt-lemon wants to merge 11 commits into
mainfrom
documentation/4060/docs
Open

[#4060] Align completeness between old Aggregate and new Entity description#4562
laura-devriendt-lemon wants to merge 11 commits into
mainfrom
documentation/4060/docs

Conversation

@laura-devriendt-lemon
Copy link
Copy Markdown
Contributor

@laura-devriendt-lemon laura-devriendt-lemon commented May 11, 2026

This PR resolves #4060.

@laura-devriendt-lemon laura-devriendt-lemon added this to the Release 5.2.0 milestone May 11, 2026
@laura-devriendt-lemon laura-devriendt-lemon self-assigned this May 11, 2026
@laura-devriendt-lemon laura-devriendt-lemon added Priority 1: Must Highest priority. A release cannot be made if this issue isn’t resolved. Type: Documentation Use to signal issues that describe documentation work. labels May 11, 2026
@laura-devriendt-lemon laura-devriendt-lemon linked an issue May 11, 2026 that may be closed by this pull request
@laura-devriendt-lemon laura-devriendt-lemon marked this pull request as ready for review May 11, 2026 06:34
@laura-devriendt-lemon laura-devriendt-lemon requested a review from a team as a code owner May 11, 2026 06:34
@laura-devriendt-lemon laura-devriendt-lemon requested review from MateuszNaKodach, hjohn and jangalinski and removed request for a team May 11, 2026 06:34
@laura-devriendt-lemon laura-devriendt-lemon changed the title 4060: put back changes for entities documentation [#4060]: put back changes for entities documentation May 11, 2026
@smcvb smcvb changed the title [#4060]: put back changes for entities documentation [#4060] Align completeness between old Aggregate and new Entity description May 11, 2026
Copy link
Copy Markdown
Contributor

@smcvb smcvb left a comment

Choose a reason for hiding this comment

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

Thanks for picking up this task, @laura-devriendt-lemon! I do have quite some comments to address, though. Arguable quite some I could've clarified on the original issue...so my apologies for that. So, please bare with me here! 🙏

Comment thread docs/reference-guide/modules/commands/pages/command-handlers.adoc Outdated
Comment thread docs/reference-guide/modules/commands/pages/entities/index.adoc
Comment thread docs/reference-guide/modules/commands/pages/command-handlers.adoc
Comment thread docs/reference-guide/modules/commands/pages/entities/index.adoc Outdated
Comment thread docs/reference-guide/modules/commands/pages/entities/event-sourced-entity.adoc Outdated
Comment thread docs/reference-guide/modules/commands/pages/entities/entity-hierarchies.adoc Outdated
Comment thread docs/reference-guide/modules/commands/pages/entities/entity-hierarchies.adoc Outdated
Comment thread docs/reference-guide/modules/commands/pages/entities/entity-polymorphism.adoc Outdated
Comment thread docs/reference-guide/modules/commands/pages/entities/entity-polymorphism.adoc Outdated
Copy link
Copy Markdown
Contributor

@hatzlj hatzlj left a comment

Choose a reason for hiding this comment

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

I have one concern regarding the handling of sealed types for polymorphic models and some minor improvement suggestions. Overall it reads great and easy to follow.

Comment on lines +305 to +306
- xref:commands:entities/event-sourced-entity.adoc[Event-Sourced Entities]: full reference for defining, configuring, and testing event-sourced entities in Axon Framework 5
- xref:commands:entities/index.adoc[Entities]: overview of entity patterns (event-sourced, hierarchies, polymorphism)
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 would flip the order for these two


[TIP]
====
The command-centric approach aligns closest with [Vertical Slice-based Architecture](https://www.baeldung.com/java-vertical-slice-architecture). This style is useful when:
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.

this refers to the enumeration of the different approaches for stateful commandhandlers ("command-centric approach" vs "entity-centric") that has been removed, would rephrase it a bit

Suggested change
The command-centric approach aligns closest with [Vertical Slice-based Architecture](https://www.baeldung.com/java-vertical-slice-architecture). This style is useful when:
Keeping commands in a separate component and receive the loaded entity as a parameter aligns closest with [Vertical Slice-based Architecture](https://www.baeldung.com/java-vertical-slice-architecture). This style is useful when:

@CommandHandler
public GiftCard(IssueCardCommand cmd, EventAppender eventAppender) { // <1>
// Validate command
public String handle(IssueCardCommand cmd, EventAppender eventAppender) { // <1>
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.

as a creational handler this would rather be

Suggested change
public String handle(IssueCardCommand cmd, EventAppender eventAppender) { // <1>
public static String issue(IssueCardCommand cmd, EventAppender eventAppender) { // <1>

* Creating a resource with a client-supplied identifier, with no existing state to check.
* Forwarding a command to an external system that owns the state.

These are xref:commands:command-handlers.adoc[stateless command handlers]—simpler and cheaper to run.
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 could directly reference the stateless command handlers heading by adding an anchor (for example [#stateless-command-handlers]) to it in command-handlers.adoc and referencing it here like

Suggested change
These are xref:commands:command-handlers.adoc[stateless command handlers]—simpler and cheaper to run.
These are xref:commands:command-handlers.adoc#stateless-command-handlers[stateless command handlers]—simpler and cheaper to run.

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.

Agreed!


[TIP]
====
If you are unfamiliar with event sourcing, see xref:events:index.adoc[Events] for an introduction to the concept.
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.

Either reference the Core concepts section on the events index page here like (requires to introduce a [#core-concepts] anchor at the heading there):

Suggested change
If you are unfamiliar with event sourcing, see xref:events:index.adoc[Events] for an introduction to the concept.
If you are unfamiliar with event sourcing, see xref:events:index.adoc#core-concepts[Events core concepts] for an introduction to the concept.

Or alternatively directy link to the event sourcing page referenced there by changing it to:

Suggested change
If you are unfamiliar with event sourcing, see xref:events:index.adoc[Events] for an introduction to the concept.
If you are unfamiliar with event sourcing, see our explanation on link:https://www.axoniq.io/concepts/event-sourcing[**Event Sourcing**] for an introduction to the concept.

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.

Agreed with @hatzlj here.

Comment on lines +35 to +44
----
import org.axonframework.eventsourcing.annotation.EventTag;

public record CourseCreatedEvent(
@EventTag String courseId, // <1>
String title,
int capacity
) {}
----

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.

would be great to also include the entity with the tagKey set on the entity annotation in the example (similar as @MateuszNaKodach did in his Event tag migration guide PR

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.

Ah shortened entity, just showing that part, instead of a full-fledged solution. But otherwise, agreed with this point!

EventSourcedEntityModule.declarative(String.class, CourseEntity.class)
.messagingModel((config, model) -> model
.creationalCommandHandler( // <1>
new QualifiedName(CreateCourseCommand.class),
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 users tend to copy from examples, i would make the following the default as it convers both custom names/namespaces and the FQCN fallback (also below). Instead of the note below you can explain the reason for it in an additional callout:

Suggested change
new QualifiedName(CreateCourseCommand.class),
config.getComponent(MessageTypeResolver.class).resolveOrThrow(CreateCourseCommand.class).qualifiedName(), // <2>

=== Autodetected

Use `@EventSourcedEntity(concreteTypes = {...})` and register with `EventSourcedEntityModule.autodetected()`.
Axon reads `concreteTypes` from the annotation and scans all subtypes automatically.
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.

we can have an additional note here that for sealed types the subtypes are collected automatically without the need for specifying the concrete types (see org.axonframework.eventsourcing.configuration.AnnotatedEventSourcedEntityModule.getConcreteEntityTypes for how this is done), same applies for the spring boot configuration

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.

Loving this addition! It's a great feature that we have, so is good to show off!


== Constraints

* Subtypes not listed in `concreteTypes` are silently ignored; their command and event handlers will not be registered and commands targeting them will fail at runtime.
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.

not true for sealed types, they are detected automatically in the autodection/spring-boot case

Copy link
Copy Markdown
Contributor

@smcvb smcvb left a comment

Choose a reason for hiding this comment

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

Sorry, bunch of comments to cover again. I think we need to do a bit more restructuring of the command-handlers.adoc and event-sourced-entity.adoc pages mostly.


Axon supports two approaches to entity state management:
Axon supports **event-sourced entities**, where state is derived from a sequence of events.
State-stored entities are planned for a future release.
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.

We do not have to mention if other things are planned for a future release here.

Suggested change
State-stored entities are planned for a future release.

* Creating a resource with a client-supplied identifier, with no existing state to check.
* Forwarding a command to an external system that owns the state.

These are xref:commands:command-handlers.adoc[stateless command handlers]—simpler and cheaper to run.
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.

Agreed!

= Entities
:navtitle: Entities

An entity maintains state across command invocations and uses that state to enforce business rules.
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.

This sentence is true in the context of command handling, but that's not clearly stated in this sentence. Given that entity is a rather generic term, I'd prefer we make this sentence specific towards the fact we're talking about entities used for business validation / command handling. As not to overburden this notion for new people.


[TIP]
====
If you are unfamiliar with event sourcing, see xref:events:index.adoc[Events] for an introduction to the concept.
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.

Agreed with @hatzlj here.

== Entity identification

Every command that targets an entity instance must identify which instance to load.
Annotate the identifier field on the command with `@TargetEntityId`:
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.

Suggested change
Annotate the identifier field on the command with `@TargetEntityId`:
To that end, we can annotate the identifier field on the command with `@TargetEntityId`:

====

[#entity-creation-with-entitycreator]
=== Entity creator
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.

Sorry, I think I wasn't clear in my earlier comment. I was thinking of making this a separate .adoc file instead of moving it in here! I mean, we should definitely reference it from this file, that's for sure. But, these three styles allow for different modes of constructing your entity. Modes that deserve space, on their own page.

Added, if we move this to it's own page, we can get to the most important part of this page, which is the basics of what an entity looks like.

But, let me know if you agree. If you don't we can discuss, of course!

<1> Axon invokes this constructor with the first event when sourcing the entity.
Can be combined with an `@InjectEntityId`-annotated parameter if both the event and identifier are needed.

== Configuring an entity
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.

Perhaps we can word and structure section slightly differently. Although technically it's about configuring, for the user it's about their entity.

So, if we start with a domain-centric Course entity class with everything in it, we can from their slowly show the configuration options.
E.g., we take the annotated example, but strip away all the annotations. Without the annotations, that class can be the input for a declarative configuration! In your example right now, you essentially do the assertion in side the (for example) instanceCommandHandler registration for the EnrollStudentCommand. But ideally, that registration invokes a method on the Course. It is that method which is the command handler. That is the layering Axon Framework provides around the Course just by having the annotations.

So, long story short, I think we could have:

  1. A show case of a bare Course entity. With command handlers, event sourcing handlers, state, and creation logic. Just no annotations.
  2. Then that is followed up by explaining how we can configure that, taking the three options as drafted below.
  3. Firstly, the declarative configuration section would thus only show the EventSourcingConfigurer#registerEntity stuff, not the Course entity. Internally, it would refer to the methods of the Course entity that have been shown earlier.
  4. Second, we'd add the automated configuration through annotations. This would, of course, require a copy of the Course entity, but now with annotations.
  5. Lastly, we'd do the Spring configuration version. For this one, I'd clarify we only need to switch the @EventSourcedEntity annotation for @EventSourced.

Sorry for the long-winded explanation here... If you want to chat about it face-to-face before proceeding, let me know!

import org.axonframework.messaging.eventstreaming.EventCriteria;
import org.axonframework.messaging.eventstreaming.Tag;

EventSourcingConfigurer configurer = EventSourcingConfigurer.create();
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 think it's better we move the configurer invocation logic inside a method, which is given the EventSourcingConfigurer as a parameter. Reasoning: I do not want to give the impression users need to create an EventSourcingConfigurer. Instead, they'll make the EventSourcingConfigurer once and pass it along from their public static void main. By ensuring our samples do not create an EventSourcingConfigurer every time, we make it so that people do not think that's the way to go.

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.

Furthermore, it would make for a compiling code sample, which this isn't as far as I know.

=== Autodetected

Use `@EventSourcedEntity(concreteTypes = {...})` and register with `EventSourcedEntityModule.autodetected()`.
Axon reads `concreteTypes` from the annotation and scans all subtypes automatically.
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.

Loving this addition! It's a great feature that we have, so is good to show off!


@CommandHandler
public void handle(RedeemCardCommand cmd,
@InjectEntity GiftCard card, // <3>
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 prefer this example to be a separate chapter as well, actually 👼
It is the new way of handling commands and entities, allowing users to abstract out command handling logic from an entity object while still using annotations. Added, it is the way how to do Vertical Slice Architecture - to take a slice of your Event Model and recreate that in Axon Framework. That deserves it's own chapter entirely.

Granted, this is new, of course. We can either do that as part of this PR (which means you need to move the stateful command handler logic into a dedicated page) or in a follow-up PR.

Concluding, that would live the command-handlers.adoc devoid of entity-details, which I think is a good thing. For entity-details, we have different pages. That leaves the plain description of Command Handlers super straightforward and to the point, making it an easily digestible topic.

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: Documentation Use to signal issues that describe documentation work.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate completeness of aggregate-to-entity rewrite

3 participants