Annotation Interface Apply
An @Apply method defines how a specific update modifies an entity. This update is typically the payload of a
command or other message expressing intent. Once validated and applied, the update may be published and/or stored as
an event, depending on the publication configuration.
@Apply can be placed:
- On a method inside the update class (e.g.
UpdateProduct#apply(Product)), which receives the current state of the entity and returns the updated version - On a method or static factory in the entity class itself
(e.g.
Product#update(UpdateProduct)), which describes how the entity processes a given update - On a constructor or static method of the entity, if the update creates a new instance
For deletions, returning null signals that the entity should be removed. The applied event is still stored
and/or published according to the configured publication settings.
For independently stored models, the return value identifies the target model and its resulting state.
An apply may also return an ordered Collection of models; every value then joins the same atomic
model commit. A typed collection is validated against its declared element type. Collection<Object> is
supported when heterogeneous model types are useful and validates every returned value as a model at runtime.
Collection elements must be non-null and each persisted model identity may occur only once. Use
Graph.delete() for deletion instead of a null collection element. A void
apply is invalid for a model. Legacy mutable entities inside aggregates may continue to use void, although
immutable return values are strongly preferred.
When an update has applicable applies on both its payload type and an independently stored model, payload applies
run first. Model applies then receive the complete intermediate state produced by all payload applies, including a
model that the payload has just created. The two phases produce one atomic transition per model identity. Static
model applies remain supported as factories or stateless transformations; an independent static factory is a
fallback when the payload phase did not already produce its target. Explicit per-apply settings compose field by
field,
with an explicit model-side value overriding an explicit payload-side value and DEFAULT inheriting the
earlier value. Aggregates retain their existing entity-first, payload-fallback selection contract.
When the entity is part of a larger aggregate, Fluxzero automatically routes the update to the correct entity
instance using matching identifier fields, typically annotated with EntityId.
@Apply methods are also used during event sourcing to reconstruct an entity's state from past updates.
Method parameters are injected automatically. Supported parameters include:
- The current entity instance (for non-static apply methods)
- Any parent, grandparent, or other ancestor entity in the aggregate hierarchy
- Any independently stored model loaded for the current model commit, either as its value or as
Entity<T> - The update object itself
- The full
Messageor itsMetadata - Other context such as the
Userperforming the update
@Parent relation supplies that identity at the pinned pre-apply boundary,
including when another apply deletes the child in the same atomic commit. An explicit direct write ID retains
precedence; ambiguous ancestors must be qualified with
@Association.
Note that empty entities (where the value of the entity is null) are not injected unless the parameter
is annotated with @Nullable.
Examples
1. Creating a new entity from an @Apply method inside the update class
@Apply
Issue create() {
return Issue.builder()
.issueId(issueId)
.count(1)
.status(IssueStatus.OPEN)
.details(issueDetails)
.firstSeen(lastSeen)
.lastSeen(lastSeen)
.build();
}
2. Updating an entity with a new state
@Apply
Product apply(Product product) {
return product.toBuilder().details(details).build();
}
3. Deleting an entity
@Apply
Product apply(Product product) {
return null;
}
4. Defining apply methods inside the entity class
@Apply
static Product create(CreateProduct update) {
return Product.builder()
.productId(update.getProductId())
.details(update.getDetails())
.build();
}
@Apply
Product update(UpdateProduct update) {
return this.toBuilder().details(update.getDetails()).build();
}
@Apply
Product delete(DeleteProduct update) {
return null;
}
Routing example with aggregates and nested entities
@Aggregate
class ProductCategory {
String categoryId;
@Member
List<Product> products;
}
Updates targeting `Product` will automatically be routed based on `@EntityId` inside `Product`.- See Also:
-
Optional Element Summary
Optional ElementsModifier and TypeOptional ElementDescriptionOverrides whether this apply participates in automatic model command handling.io.fluxzero.common.api.modeling.ModelConflictPolicyOverrides conflict handling for the model produced by this apply.booleanDisables apply-compatibility checking for this method.Controls whether the update should result in a published update, depending on whether the entity was actually modified.Controls how an update event published by this apply method is assigned to a message segment.Overrides command-result completion for graph projections affected by this apply.Controls how the applied update is stored and/or published, and whether publish-only updates advance aggregate state for the owning aggregate type.
-
Element Details
-
conflictPolicy
io.fluxzero.common.api.modeling.ModelConflictPolicy conflictPolicyOverrides conflict handling for the model produced by this apply.- Default:
DEFAULT
-
automaticHandling
AutomaticModelHandling automaticHandlingOverrides whether this apply participates in automatic model command handling.- Default:
DEFAULT
-
graphProjectionCompletion
GraphProjectionCompletion graphProjectionCompletionOverrides command-result completion for graph projections affected by this apply.- Default:
DEFAULT
-
eventPublication
EventPublication eventPublicationControls whether the update should result in a published update, depending on whether the entity was actually modified.This overrides the default from the enclosing aggregate, if set.
- Returns:
- update publication behavior
- Default:
DEFAULT
-
publicationStrategy
EventPublicationStrategy publicationStrategyControls how the applied update is stored and/or published, and whether publish-only updates advance aggregate state for the owning aggregate type. This strategy takes precedence overeventPublication()if explicitly set. A publish-only apply cannot change an event-sourcedModel; Fluxzero rejects such a transition before commit because its stored stream could not reconstruct the new state. Publish-only no-ops and changes to document-loaded models remain supported.- Returns:
- strategy for persisting and/or publishing the applied update
- Default:
DEFAULT
-
eventRouting
AggregateEventRouting eventRoutingControls how an update event published by this apply method is assigned to a message segment.This setting applies to aggregates and defaults to the enclosing aggregate configuration. Events produced by independent
Modelsuse ordinary message routing; annotate their payload with@RoutingKeywhen related events should share a segment.- Returns:
- routing behavior for the applied update event
- Default:
DEFAULT
-
disableCompatibilityCheck
boolean disableCompatibilityCheckDisables apply-compatibility checking for this method.Unless property
fluxzero.assert.legal.apply-compatibilityis explicitly set tofalse, Fluxzero verifies that at least one@Applymethod is compatible with the current entity state.Setting this flag to
trueexempts this method from that check.- Default:
false
-