Annotation Interface Model
Unlike an Aggregate, a model is its own persistence and lifecycle boundary. Loading or updating it does not
require loading a parent, sibling, child, or an artificial aggregate root. A model may still contain embedded
entities declared with @Member; those members share the model's stream, cache, search document,
snapshots, and lifecycle.
Choose this boundary from domain lifecycle first: state that can be created, changed, retained, deleted, or whose
history matters independently is a separate model, even when it is normally displayed in a parent's collection.
Connect such a child with @Parent. A meaningful domain identity is strong evidence for that boundary,
not an additional gate: an independently living child may use a globally unique ID or a
parent-scoped ID. Collection shape, searchability, storage format, update frequency,
and convenient embedding do not make independently living state a Member.
Model identity is the repository representation of its @EntityId. Applications can use a typed
Id, annotation-level prefix/postfix affixes, or both to isolate otherwise equal functional identifiers.
An @Apply method targets a model by returning that model. Returning null deletes the targeted
model while retaining the applied event according to the configured publication settings. Returning void is
invalid for model applies because it does not identify a stored result. Legacy mutable aggregate applies remain
supported.
Persistence
persistence() makes the durable representations and authoritative load path explicit. Event-sourced models
are reconstructed from their model stream, optionally from a snapshot. Adding ModelPersistence.DOCUMENT
maintains a current document as well; when event sourcing is absent, that document is authoritative. Event storage
and publication remain independent and are controlled by eventPublication(),
publicationStrategy(), and per-apply overrides. Internal component documents used for Graph composition are
likewise orthogonal and never change the selected load path.
Example
@Model(persistence = {
ModelPersistence.EVENT_SOURCED,
ModelPersistence.DOCUMENT
})
public record Product(@EntityId ProductId productId, String name) {
@Apply
Product rename(RenameProduct command) {
return new Product(productId, command.name());
}
}
An update may instead create or update the model from a payload-side @Apply. When both sides define an
applicable apply, Fluxzero applies the payload first and invokes the model method against that intermediate state.
This lets one instance method consistently enforce model-owned behavior for both creation and later updates.- See Also:
-
Optional Element Summary
Optional ElementsModifier and TypeOptional ElementDescriptionControls whether applies producing this model may be exposed as automatic command handlers.booleanWhether the latest model state should be stored in the shared application cache.intNumber of older model versions retained in the shared cache.intFrequency at which intermediate states are checkpointed within one reconstruction session.Controls when model changes are committed and whether completion-phase commits may run concurrently.io.fluxzero.common.api.modeling.ModelConflictPolicyConflict handling for this model when an apply does not provide an explicit override.Advanced configuration for the Model's direct current document.Controls whether an applied update produces an event when the returned model is unchanged.Advanced configuration for the materialized whole-graph search document.booleanWhether unknown events should be ignored while reconstructing an event-sourced model.booleanWhether Fluxzero should asynchronously materialize the complete model graph as a separate search document.intMaximum number of snapshots retained for this model.Durable representations and authoritative load path for this Model.Controls whether applied events are stored, published, or both.intNumber of stored model events between snapshots.
-
Element Details
-
conflictPolicy
io.fluxzero.common.api.modeling.ModelConflictPolicy conflictPolicyConflict handling for this model when an apply does not provide an explicit override.- Default:
DEFAULT
-
automaticHandling
AutomaticModelHandling automaticHandlingControls whether applies producing this model may be exposed as automatic command handlers.- Default:
DEFAULT
-
persistence
ModelPersistence[] persistenceDurable representations and authoritative load path for this Model.The set must contain at least one unique value. When
EVENT_SOURCEDis present, the event stream is authoritative. OtherwiseDOCUMENTis authoritative.This setting does not suppress storing or publishing events produced by
Applymethods. A state-changing event-sourced Model apply must store its reconstructing event; aPUBLISH_ONLYorNEVERtransition that would change state is rejected before commit. A publish-only no-op remains a valid domain notification when publication is explicitly set toALWAYS.- Default:
{EVENT_SOURCED}
-
ignoreUnknownEvents
boolean ignoreUnknownEventsWhether unknown events should be ignored while reconstructing an event-sourced model.- Default:
false
-
snapshotPeriod
int snapshotPeriodNumber of stored model events between snapshots.0disables periodic snapshots.- Default:
0
-
maxSnapshotCount
int maxSnapshotCountMaximum number of snapshots retained for this model. Values below1are treated as1.- Default:
1
-
cached
boolean cachedWhether the latest model state should be stored in the shared application cache.Models participating in a commit may additionally be retained in a commit-local cache until the commit completes.
- Default:
true
-
cachingDepth
int cachingDepthNumber of older model versions retained in the shared cache.-1retains all available cached versions;0retains only the latest version.Independent models retain one previous version by default so event handlers can compare the event-visible model with
Entity.previous(). Retaining an unbounded revision chain must be an explicit choice because model caches are expected to contain far more keys than aggregate caches.- Default:
1
-
checkpointPeriod
int checkpointPeriodFrequency at which intermediate states are checkpointed within one reconstruction session.Checkpoints avoid replaying the same prefix for repeated historical dependency loads. They are bounded by the reconstruction session and are not retained as document revisions.
- Default:
100
-
commitPolicy
ModelCommitPolicy commitPolicyControls when model changes are committed and whether completion-phase commits may run concurrently.The default value resolves from
fluxzero.model.commitPolicywhen present and otherwise usesModelCommitPolicy.ASYNC_AFTER_HANDLER_AWAIT_AFTER_BATCH. Independent models were introduced with this default, so it does not depend on the active defaults version.- Default:
DEFAULT
-
eventPublication
EventPublication eventPublicationControls whether an applied update produces an event when the returned model is unchanged.Independent models default to
IF_MODIFIED, so a no-op apply does not create a model-stream or globally published event. UseALWAYSwhen an unchanged apply intentionally represents a domain event. This setting is evaluated beforepublicationStrategy().- Default:
IF_MODIFIED
-
publicationStrategy
EventPublicationStrategy publicationStrategyControls whether applied events are stored, published, or both.PUBLISH_ONLYmay mutate a document-loaded model. For an event-sourced model it may only publish an unchanged result, because otherwise the next reconstruction could not reproduce the committed state.- Default:
DEFAULT
-
document
DocumentProjection documentAdvanced configuration for the Model's direct current document.Include
ModelPersistence.DOCUMENTinpersistence()to enable this projection. Searchable documents use a public collection that defaults to the Model's simple class name. Reference-only documents use type-isolated private storage while remaining available to Model loads, aliases, relationships and Graph composition. Timestamps default to the applied event timestamp when no paths are configured.- Default:
@io.fluxzero.sdk.modeling.DocumentProjection
-
materializeGraph
boolean materializeGraphWhether Fluxzero should asynchronously materialize the complete model graph as a separate search document.Fluxzero retains the root's current document in its public direct collection when it has a searchable document projection and otherwise in the same type-isolated private storage used by other Graph-only Models. Only the separately named graph collection is allowed to lag; its high-watermark is exposed through the model repository. The collection defaults to the resolved public direct-model collection plus
-graphswhen present, or to<simple model name>-graphsotherwise.- Default:
false
-
graphProjection
GraphProjection graphProjectionAdvanced configuration for the materialized whole-graph search document.This configuration does not enable materialization by itself; set
materializeGraph()totrue.- Default:
@io.fluxzero.sdk.modeling.GraphProjection
-