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.
Model declarations are indexed at compilation by ModelTypeProcessor. Enable SDK annotation processing
(Kotlin: kapt) in each contract module and preserve ModelTypes.INDEX when packaging. This index discovers
classes without registering message handlers; the optional serialization @RegisterType is not required.
Contract JARs built before this index was introduced must be rebuilt for cold discovery. A shaded JAR must append
all contributing Model indexes rather than retain only one.
Identified abstract/interface contracts remain discoverable; identity-less abstract/interface inheritance templates
are not standalone Models and are omitted from the runtime catalog.- 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.Stable logical name of this Model in persisted Model metadata.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
-
name
String nameStable logical name of this Model in persisted Model metadata.The default is the simple name of the concrete Model class. This name deliberately does not contain the Java package, so moving or renaming a class remains possible by retaining its previous logical name explicitly. Applications sharing one Runtime namespace can prepend an application-scoped prefix with
fluxzero.model.namePrefix; the prefix is concatenated literally and should therefore include any desired separator; prefixbillingand nameInvoicebecomebillingInvoice.This is a durable identity. Changing it for an existing Model creates a different Model type and requires an application-managed data transition.
- Default:
""
-
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. Every direct document uses the configured collection, which defaults to the resolved logical Model name. A reference-only document is excluded from unrestricted typed Model search 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 resolved direct collection when it has a document projection, irrespective of that document's public search visibility. A root without a direct document uses the same type-isolated private storage as 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 direct-model collection plus
-graphswhen present, or to<logical 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
-