@DomainObject
Domain semantics for domain objects (entities and view models; for services see DomainService ).
If - for the currently logged on user - none of the domain object’s members are effectively visible, (or if there are no members to begin with), the object instance is considered hidden. Hence a NOT-AUTHORIZED page will be displayed instead.
API
@interface DomainObject {
String[] aliased() default {}; (1)
Class<?> autoCompleteRepository() default Object.class; (2)
String autoCompleteMethod() default "autoComplete"; (3)
Bounding bounding() default Bounding.NOT_SPECIFIED; (4)
Editing editing() default Editing.NOT_SPECIFIED; (5)
String editingDisabledReason() default ""; (6)
Publishing entityChangePublishing() default Publishing.NOT_SPECIFIED; (7)
Introspection introspection() default Introspection.NOT_SPECIFIED; (8)
String mixinMethod() default "$$"; (9)
Nature nature() default Nature.NOT_SPECIFIED; (10)
Class<? extends ObjectCreatedEvent<?>> createdLifecycleEvent() default ObjectCreatedEvent.Default.class; (11)
Class<? extends ObjectPersistingEvent<?>> persistingLifecycleEvent() default ObjectPersistingEvent.Default.class; (12)
Class<? extends ObjectPersistedEvent<?>> persistedLifecycleEvent() default ObjectPersistedEvent.Default.class; (13)
Class<? extends ObjectLoadedEvent<?>> loadedLifecycleEvent() default ObjectLoadedEvent.Default.class; (14)
Class<? extends ObjectUpdatingEvent<?>> updatingLifecycleEvent() default ObjectUpdatingEvent.Default.class; (15)
Class<? extends ObjectUpdatedEvent<?>> updatedLifecycleEvent() default ObjectUpdatedEvent.Default.class; (16)
Class<? extends ObjectRemovingEvent<?>> removingLifecycleEvent() default ObjectRemovingEvent.Default.class; (17)
Class<? extends ActionDomainEvent<?>> actionDomainEvent() default ActionDomainEvent.Default.class; (18)
Class<? extends PropertyDomainEvent<?, ?>> propertyDomainEvent() default PropertyDomainEvent.Default.class; (19)
Class<? extends CollectionDomainEvent<?, ?>> collectionDomainEvent() default CollectionDomainEvent.Default.class; (20)
}
1 | aliased
Alternative logical type name(s) for the annotated type. |
2 | autoCompleteRepository
The class of the domain service that provides an |
3 | autoCompleteMethod
The method to use in order to perform the auto-complete search (defaults to "autoComplete"). |
4 | bounding
Indicates that the class has a bounded, or finite, set of instances. |
5 | editing
Whether the properties of this domain object can be edited, or collections of this object be added to/removed from. |
6 | editingDisabledReason
If #editing() is set to Editing#DISABLED , then the reason to provide to the user as to why the object’s properties cannot be edited/collections modified. |
7 | entityChangePublishing
Whether entity changes (persistent property updates) should be published to org.apache.causeway.applib.services.publishing.spi.EntityPropertyChangeSubscriber s and whether entity changes, captured as org.apache.causeway.applib.services.publishing.spi.EntityChanges , should be published to org.apache.causeway.applib.services.publishing.spi.EntityChangesSubscriber s. |
8 | introspection
Controls on a per class basis, how meta-model class introspection should process members, supporting methods and callback methods. |
9 | mixinMethod
Applicable only if #nature() is Nature#MIXIN , indicates the name of the method within the mixin class to be inferred as the action of that mixin. |
10 | nature
The nature of this domain object. |
11 | createdLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectCreatedEvent . |
12 | persistingLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectPersistingEvent . |
13 | persistedLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectPersistedEvent . |
14 | loadedLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectLoadedEvent . |
15 | updatingLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectUpdatingEvent . |
16 | updatedLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectUpdatedEvent . |
17 | removingLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectRemovingEvent . |
18 | actionDomainEvent
Indicates that an invocation of any action of the domain object (that do not themselves specify their own |
19 | propertyDomainEvent
Indicates that changes to any property of the domain object (that do not themselves specify their own |
20 | collectionDomainEvent
Indicates that changes to any collection of the domain object (that do not themselves specify their own |
Members
autoCompleteRepository
The class of the domain service that provides an autoComplete(String)
method.
It is sufficient to specify an interface rather than a concrete type.
autoCompleteMethod
The method to use in order to perform the auto-complete search (defaults to "autoComplete").
The method is required to accept a single string parameter, and must return a list of the domain type.
bounding
Indicates that the class has a bounded, or finite, set of instances.
Takes precedence over auto-complete.
Note: this replaces bounded=true|false prior to v2.x
editing
Whether the properties of this domain object can be edited, or collections of this object be added to/removed from.
Note that non-editable objects can nevertheless have actions invoked upon them.
editingDisabledReason
If #editing() is set to Editing#DISABLED , then the reason to provide to the user as to why the object’s properties cannot be edited/collections modified.
If left empty (default), no reason is given.
entityChangePublishing
Whether entity changes (persistent property updates) should be published to org.apache.causeway.applib.services.publishing.spi.EntityPropertyChangeSubscriber s and whether entity changes, captured as org.apache.causeway.applib.services.publishing.spi.EntityChanges , should be published to org.apache.causeway.applib.services.publishing.spi.EntityChangesSubscriber s.
introspection
Controls on a per class basis, how meta-model class introspection should process members, supporting methods and callback methods.
mixinMethod
Applicable only if #nature() is Nature#MIXIN , indicates the name of the method within the mixin class to be inferred as the action of that mixin.
Supporting methods are then derived from that method name. For example, if the mixin method name is "act", then the disable supporting method will be "disableAct".
Typical examples are "act", "prop", "coll", "exec", "execute", "invoke", "apply" and so on. The default name is $$
.
*NOTE* : it's more typical to instead use xref:refguide:applib:index/annotation/Action.adoc[Action] , xref:refguide:applib:index/annotation/Property.adoc[Property] or xref:refguide:applib:index/annotation/Collection.adoc[Collection] as the class-level annotation, indicating that the domain object is a mixin. The mixin method name for these is, respectively, "act", "prop" and "coll".
nature
The nature of this domain object.
Most common are natures of Nature#ENTITY and Nature#VIEW_MODEL . For mixins, rather than use a nature of Nature#MIXIN , it’s more typical to instead use Action , Property or Collection as the class-level annotation, indicating that the domain object is a mixin. The #mixinMethod() mixin method name for these is, respectively, "act", "prop" and "coll".
The Nature#BEAN nature is for internally use, and should not normally be specified explicitly.
createdLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectCreatedEvent .
This subclass must provide a no-arg constructor; the fields are set reflectively.
persistingLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectPersistingEvent .
This subclass must provide a no-arg constructor; the fields are set reflectively.
persistedLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectPersistedEvent .
This subclass must provide a no-arg constructor; the fields are set reflectively.
loadedLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectLoadedEvent .
This subclass must provide a no-arg constructor; the fields are set reflectively.
updatingLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectUpdatingEvent .
This subclass must provide a no-arg constructor; the fields are set reflectively.
updatedLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectUpdatedEvent .
This subclass must provide a no-arg constructor; the fields are set reflectively.
removingLifecycleEvent
Indicates that the loading of the domain object should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) ObjectRemovingEvent .
This subclass must provide a no-arg constructor; the fields are set reflectively.
actionDomainEvent
Indicates that an invocation of any action of the domain object (that do not themselves specify their own @Action(domainEvent=…)
should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using the specified custom (subclass of) ActionDomainEvent .
For example:
@DomainObject(actionDomainEvent=SomeObject.GenericActionDomainEvent.class) public class SomeObject{ public static class GenericActionDomainEvent extends ActionDomainEvent<Object> { ... } public void changeStartDate(final Date startDate) { ...} ... }
This will result in all actions as a more specific type to use) to emit this event.
This subclass must provide a no-arg constructor; the fields are set reflectively. It must also use Object
as its generic type. This is to allow mixins to also emit the same event.
propertyDomainEvent
Indicates that changes to any property of the domain object (that do not themselves specify their own @Property(domainEvent=…)
should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using the specified custom (subclass of) PropertyDomainEvent .
For example:
@DomainObject(propertyDomainEvent=SomeObject.GenericPropertyDomainEvent.class) public class SomeObject{ public LocalDate getStartDate() { ...} }
This subclass must provide a no-arg constructor; the fields are set reflectively. It must also use Object
as its generic type. This is to allow mixins to also emit the same event.
collectionDomainEvent
Indicates that changes to any collection of the domain object (that do not themselves specify their own @Collection(domainEvent=…)
should be posted to the org.apache.causeway.applib.services.eventbus.EventBusService event bus using a custom (subclass of) CollectionDomainEvent .
For example:
@DomainObject(collectionDomainEvent=Order.GenericCollectionDomainEvent.class) public class Order { public SortedSet<OrderLine> getLineItems() { ...} }
This subclass must provide a no-arg constructor; the fields are set reflectively. It must also use Object
as its generic type. This is to allow mixins to also emit the same event.
Example
For example:
@DomainObject(
auditing=Auditing.ENABLED,
autoCompleteRepository=CustomerRepository.class
editing=Editing.ENABLED,
updatedLifecycleEvent=Customer.UpdatedEvent.class
)
public class Customer {
...
}
View Models
The @DomainObject(nature=VIEW_MODEL)
annotation, applied to a class, indicates that the class is a view model.
View models are not persisted to the database, instead their state is encoded within their identity (ultimately represented in the URL).
For example:
@DomainObject(nature=VIEW_MODEL)
public class CustomerViewModel {
public CustomerViewModel() {}
public CustomerViewModel(String firstName, int lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
...
}
Although there are several ways to instantiate a view model, we recommend that they are instantiated using an N-arg constructor that initializes all relevant state. The ServiceRegistry can then be used to inject dependencies into the view model. For example:
Customer cust = ...
CustomerViewModel vm = factoryService.viewModel(
new CustomerViewModel(cust.getFirstName(), cust.getLastName()));
The view model’s memento will be derived from the value of the view model object’s properties. Any properties annotated with @Programmatic will be excluded from the memento. Properties that are merely hidden are included in the memento.
View models when defined using @DomainObject(nature=VIEW_MODEL)
have some limitations:
-
view models cannot hold collections other view models (simple properties are supported, though)
-
collections (of either view models or entities) are ignored.
-
not every data type is supported,
However, these limitations do not apply to JAXB view models. If you are using view models heavily, you may wish to restrict yourself to just the JAXB flavour.
Object aliases
The logicalTypeName() element is used to provide a unique alias for the object’s class name.
This value is used internally to generate a string representation of an objects identity (the Oid
).
This can appear in several contexts, including:
-
as the value of
Bookmark#getObjectType()
and in thetoString()
value ofBookmark
(see BookmarkService) -
in the serialization of
OidDto
in the command and interaction schemas -
in the URLs of the RestfulObjects viewer
-
in the URLs of the Web UI (Wicket viewer)
-
in XML snapshots generated by the XmlSnapshotService
Precedence
The rules of precedence are:
-
@DomainObject#logicalTypeName
-
ORM-specific:
-
JDO: @PersistenceCapable, if at least the
schema
attribute is defined.If both
schema
andtable
are defined, then the value is “schema.table”. If onlyschema
is defined, then the value is “schema.className”. -
(JPA)
@Table#schema()
.
-
-
Fully qualified class name of the entity.
This might be obvious, but to make explicit: we recommend that you always specify an object type for your domain objects. Otherwise, if you refactor your code (change class name or move package), then any externally held references to the OID of the object will break. At best this will require a data migration in the database; at worst it could cause external clients, eg if accessing data through the Restful Objects viewer, to break. |
If the object type is not unique across all domain classes then the framework will fail-fast and fail to boot. An error message will be printed in the log to help you determine which classes have duplicate object tyoes.
Nature
The nature() element is used to characterize the domain object as either an entity (part of the domain layer) or as a view model (part of the application layer). If the domain object should be thought of as an entity, it also captures how the persistence of that entity is managed.
For example:
@DomainObject(nature=Nature.VIEW_MODEL)
public class PieChartAnalysis {
...
}
Specifically, the nature must be one of:
-
NOT_SPECIFIED,
(the default); specifies no paricular semantics for the domain class.
-
ENTITY
indicates that the domain object is an entity whose persistence is managed internally by Apache Causeway, using the JDO/DataNucleus objectstore.
-
MIXIN
indicates that the domain object is part of the domain layer, and is contributing behaviour to objects of some other type as a mixin (also known as a trait).
For further discussion on using mixins, see mixins in the user guide.
-
VIEW_MODEL
indicates that the domain object is conceptually part of the application layer, and exists to surfaces behaviour and/or state that is aggregate of one or more domain entities.
-
Variant: External Entity
Or indicates that the domain object is a wrapper/proxy/stub (choose your term) to an entity that is managed by some related external system. For example, the domain object may hold just the URI to a RESTful resource of some third party REST service, or the id of some system accessible over SOAP.
The identity of an external entity is determined solely by the state of entity’s properties. The framework will automatically recreate the domain object each time it is interacted with.
-
Variant: In-Memory Entity
Or indicates that the domain object is a wrapper/proxy/stub to a "synthetic" entity, for example one that is constructed from some sort of internal memory data structure.
The identity of an in-memory entity is determined solely by the state of entity’s properties. The framework will automatically recreate the domain object each time it is interacted with.
-
Those natures that indicate the domain object is an entity (of some sort or another) mean then that the domain object is considered to be part of the domain model layer.
For Because this particular implementation was originally added to Apache Causeway in support of view models, the term was also used for the logically different external entities and in-memory entities. The benefit of |
On the other hand, view models defined in this way do have some limitations; see View Models for further discussion. These limitations do not apply to JAXB view models. If you are using view models heavily, you may wish to restrict yourself to just the JAXB flavour. |
Editing
The editing() element determines whether a domain object’s properties and collections are not editable (are read-only).
The default is AS_CONFIGURED
, meaning that the causeway.applib.annotation.domain-object.editing
configuration property is used to determine the whether the object is modifiable:
-
true
the object’s properties and collections are modifiable.
-
false
the object’s properties and collections are read-only, ie not modifiable.
If there is no configuration property in application.properties
then object are assumed to be modifiable.
In other words, editing can be disabled globally by setting the causeway.applib.adoc#causeway.applib.annotation.domain-object.editing configuration property:
We recommend this setting; it will help drive out the underlying business operations (processes and procedures) that require objects to change; these can then be captured as business actions. |
This default can be overridden on an object-by-object basis; if editing()
is set to ENABLED
then the object’s properties and collections are editable irrespective of the configured value; if set to DISABLED
then the object’s properties and collections are not editable irrespective of the configured value.
For example:
@DomainObject(
editing=Editing.DISABLED,
editingDisabledReason="Reference data, so cannot be modified"
)
public class Country {
...
}
Domain Event Defaults
Whenever an member of a domain object is interacted with then a domain event will be fired, for each of the various phases (hidden, disabled, validated, executing, executed).
For each of the three types of members (action, property and collection), the DomainObject provides an element that defines a common event domain class to be fired whenever interacting with members of that type. This default event type can be overridden with a more specific event type if necessary.
Type |
@DomainObject default |
Overridden with |
Actions |
||
Properties |
||
Collections |
Action example
For an action:
@DomainObject(
actionDomainEvent=ToDoItem.ActionDomainEventDefault.class
)
public class ToDoItem {
public static class ActionDomainEventDefault extends
org.apache.causeway.applib.events.domain.ActionDomainEvent<Object> { }
// ...
public void updateDescription(final String description) {
this.description = description;
}
}
The specified class will also be used as the event for any mixin action contributed to the class.
Note though one small difference between the events emitted by a "regular" action and a contributed action is that the source of the event (as in event#getSource()
will be different.
In the former case it will be the domain object instance, in the latter it will be the mixin object instantiated automatically by the framework.
However, the domain object is available using event#getMixedIn()
.
Even easier, event#getSubject()
will always return the domain object (it returns the #getMixedIn()
if present, otherwise the #getSource()
.
Action domain events have generic types, with the (first and only) generic type indicating the event’s source’s type. Because an event specified at the class level might have either the domain object or a mixin for the domain object as its source, they should therefore use simply |
Property example
For example:
@DomainObject(
propertyDomainEvent=ToDoItem.PropertyDomainEventDefault.class
)
public class ToDoItem {
public static class PropertyDomainEventDefault
extends org.apache.causeway.applib.events.domain.PropertyDomainEvent<Object> { }
...
@Getter @Setter
private String description;
}
If there is a mixin for the domain object, then this will also honour the domain event. For example:
@Property(
propertyDomainEvent=ToDoItem_priority.PropertyDomainEventDefault.class
)
public class ToDoItem_priority {
private final ToDoItem todoItem;
// constructor omitted
public static class PropertyDomainEventDefault
extends org.apache.causeway.applib.events.domain.PropertyDomainEvent<Object> { }
public Integer prop() { /* ... */ }
}
Interactions with the property contributed by this mixin will emit the domain event of the subject (ToDoItem
).
One small difference between the events emitted by a "regular" property and a contributed property is that the source of the event (as in event#getSource()
will be different.
In the former case it will be the domain object instance, in the latter it will be the mixin object instantiated automatically by the framework.
However, the domain object is available using event#getMixedIn()
.
Even easier, event#getSubject()
will always return the domain object (it returns the #getMixedIn()
if present, otherwise the #getSource()
.
Property domain events have generic types, with the first generic type indicating the event’s source’s type, and the second generic type indicating the property return type. Because an event specified at the class level might have either the domain object or a mixin for the domain object as its source, they should therefore use simply They should also have |
Collection example
For example:
import lombok.Getter;
import lombok.Setter;
@DomainObject(
collectionDomainEvent=ToDoItem.CollectionDomainEventDefault.class
)
public class ToDoItem {
public static class CollectionDomainEventDefault extends
org.apache.causeway.applib.events.domain.CollectionDomainEvent<Object> { }
...
@Getter @Setter
private Set<Category> categories = new TreeSet<>();
}
If there is a mixin for the domain object, then this will also honour the domain event. For example:
public class ToDoItem {
@Collection
@CollectionLayout(defaultView = "table")
public class related {
public List<ToDoItem> coll() {
// ...
}
}
}
Interactions with the collection contributed by this mixin will emit the domain event of the subject (ToDoItem
).
One small difference between the events emitted by a "regular" collection and a contributed action is that the source of the event (as in event#getSource()
will be different.
In the former case it will be the domain object instance, in the latter it will be the mixin object instantiated automatically by the framework.
However, the domain object is available using event#getMixedIn()
.
Even easier, event#getSubject()
will always return the domain object (it returns the #getMixedIn()
if present, otherwise the #getSource()
.
Collection domain events have generic types, with the first generic type indicating the event’s source’s type, and the second generic type indicating the element type. Because an event specified at the class level might have either the domain object or a mixin for the domain object as its source, they should therefore use simply They should also have |
Lifecycle Events
Whenever the framework interacts with a domain entity, lifecycle events are fired. These are:
-
instantiated
through FactoryService. An event is also fired for view models
-
persisting
an entity is about to be persisted (
INSERT
ed) into the database -
persisted
an entity has just been persisted to the database
-
loaded
an already persistent entity has just been loaded from the database
-
updating
an already persistent entity is about to be saved (
UPDATE
d) to the database -
updated
an already persistent entity has just been saved (
UPDATE
d) to the database -
removing
an already persistent entity is about to be removed (
DELETE
d) from the database
The framework has a built-in event class (for each lifecycle hint) that is raised by default; for example a ObjectUpdatingEvent.Default
is raised when an object is about to be updated.
Subscribers subscribe through the EventBusService and can use the event to obtain a reference to the object just created.
This basic model can be influenced in a couple of ways:
-
first, it is also possible to suppress any events from being emitted using a configuration property (a different config property exists each lifecycle hook).
-
second, an element on DomainObject annotation (for each lifecycle hook) allows a different event subtype to be emitted instead
This allows subscribers to more targeted as to the events that they subscribe to.
The framework also provides convenience "Doop" and a "Noop" event classes, that provoke these behaviours:
-
if the appropriate DomainObject' element is set to the "Doop" subclass, then this will event will be fired
this is a convenient way of ensuring an event is fired even if the hook has been disabled globally, but without the hassle of defining a custom event type
-
if set to the "Noop" subclass, then an event will not be fired.
The table below summarises all the lifecycle hooks:
Lifecycle event |
Config property |
@DomainObject override |
Framework event types |
created |
|
||
loaded |
|
||
persisting |
|
||
persisted |
|
||
updating |
|
||
updated |
|
||
removing |
|
Subscribers
Subscribers can be either coarse-grained (if they subscribe to the top-level event type):
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
@Service
public class SomeSubscriber {
@EventListener(ObjectCreatedEvent.class)
public void on(ObjectCreatedEvent ev) {
if(ev.getSource() instanceof ToDoItem) {
...
}
}
}
or can be fine-grained (by subscribing to specific event subtypes):
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
@Service
public class SomeSubscriber {
@EventListener(ToDoItem.ObjectCreatedEvent.class)
public void on(ToDoItem.ObjectCreatedEvent ev) {
...
}
}
Examples
created example
For example:
@DomainObjectLayout(
createdLifecycleEvent=ToDoItem.CreatedEvent.class
)
public class ToDoItem {
public static class CreatedEvent
extends org.apache.causeway.applib.events.lifecycle.ObjectCreatedEvent<ToDoItem> { }
...
}
It’s possible to instantiate objects without firing this lifecycle; just instantiate using its regular constructor, and then use the |
persisting example
For example:
@DomainObjectLayout(
persistingLifecycleEvent=ToDoItem.PersistingEvent.class
)
public class ToDoItem {
public static class PersistingEvent extends
org.apache.causeway.applib.events.lifecycle.ObjectPersistingEvent<ToDoItem> { }
// ...
}
persisted example
For example:
@DomainObjectLayout(
persistedLifecycleEvent=ToDoItem.PersistedEvent.class
)
public class ToDoItem {
public static class PersistedEvent extends
org.apache.causeway.applib.events.lifecycle.ObjectPersistedEvent<ToDoItem> { }
// ...
}
loaded example
For example:
@DomainObjectLayout(
loadedLifecycleEvent=ToDoItem.LoadedEvent.class
)
public class ToDoItem {
public static class LoadedEvent extends
org.apache.causeway.applib.events.lifecycle.ObjectLoadedEvent<ToDoItem> { }
...
}
updating example
For example:
@DomainObjectLayout(
updatingLifecycleEvent=ToDoItem.UpdatingEvent.class
)
public class ToDoItem {
public static class UpdatingEvent extends
org.apache.causeway.applib.events.lifecycle.ObjectUpdatingEvent<ToDoItem> { }
// ...
}
Auditing
The entityChangePublishing() element indicates that if the object is modified, then each of its changed properties should be submitted to the registered EntityPropertyChangeSubscriber(s).
The default value for the element is AS_CONFIGURED
, meaning that the causeway.applib.annotation.domain-object.entity-change-publishing configuration property is used to determine the whether the action is audited:
-
all
all changed properties of objects are audited
-
none
no changed properties of objects are audited
If there is no configuration property in application.properties
then auditing is automatically enabled for domain objects.
This default can be overridden on an object-by-object basis; if entityChangePublishing()
is set to ENABLED
then changed properties of instances of the domain class are audited irrespective of the configured value; if set to DISABLED
then the changed properties of instances are not audited, again irrespective of the configured value.
For example:
@DomainObject(
entityChangePublishing=Publishing.ENABLED (1)
)
public class Customer {
...
}
1 | because set to enabled, will be audited irrespective of the configured value. |
Reference Data
Some domain classes are immutable to the user, and moreover have only a fixed number of instances. Often these are "reference" ("standing") data, or lookup data/pick lists. Typical examples could include categories, countries, states, and tax or interest rate tables.
Where the number of instances is relatively small, ie bounded, then the bounding() element can be used as a hint. For such domain objects the framework will automatically allow instances to be selected; Web UI (Wicket viewer) displays these as a drop-down list.
For example:
@DomainObject(
bounding=Bounding.BOUNDED,
editing=Editing.DISABLED (1)
)
public class Currency {
...
}
1 | This attribute is commonly combined with editing=DISABLED to enforce the fact that reference data is immutable |
There is nothing to prevent you from using this attribute for regular mutable entities, and indeed this is sometimes worth doing during early prototyping. However, if there is no realistic upper bound to the number of instances of an entity that might be created, generally you should use autoComplete…() supporting method or the @DomainObject#autoCompleteRepository attribute instead. |
Auto-complete
The autoCompleteRepository() element nominates a single method on a domain service as the fallback means for looking up instances of the domain object using a simple string.
For example, this might search for a customer by their name or number. Or it could search for a country based on its ISO-3 code or user-friendly name.
If you require additional control - for example restricting the returned results based on the object being interacted with - then use the autoComplete…() supporting method instead. |
For example:
@DomainObject(
autoCompleteRepository=CustomerRepository.class
)
public class Customer {
....
}
where:
@DomainService
public class CustomerRepository {
List<Customer> autoComplete(String search); (1)
...
}
1 | is assumed to be called "autoComplete", and accepts a single string |
(As noted above) the method invoked on the repository is assumed to be called "autoComplete". The optional autoCompleteMethod() element allows the name of this method to be overridden.
For example:
@DomainObject(
autoCompleteRepository=Customers.class,
autoCompleteAction="findByName"
)
public class Customer {
....
}
where in this case findByName
might be an existing action already defined:
@DomainService
public class Customers {
@Action(semantics=SemanticsOf.SAFE)
public List<Customer> findByName(
@MinLength(3) (1)
@ParameterLayout(named="name")
String name);
...
}
1 | end-user must enter minimum number of characters to trigger the query |
The autocomplete "action" can also be a regular method, annotated using @Programmatic. That is, it does not need to be part of the metamodel: |
+
@DomainService
public class Customers {
@Programmatic
public List<Customer> findByName(
@MinLength(3)
String name);
...
}
Mixin method
The mixinMethod() element specifies the name of the method to be treated as a "reserved" method name, meaning that the mixin’s name should instead be inferred from the mixin’s type.
For example:
@DomainObject
public class Customer {
@DomainObject(
nature=Nature.MIXIN, (1)
mixinMethod="execute" (2)
)
public class placeOrder { (3)
public Customer execute(Product p, int quantity) { (4)
// ...
}
public String disableExecute() {
// ...
}
public String validate0Execute() {
// ...
}
}
)
1 | This is a mixin. Alternatively, could have used @Action. |
2 | This mixin is using a non-standard method. |
3 | a trick - this is not a static class, so the java compiler will create the 1-arg constructor automatically |
4 | Same name as the specified mixinMethod.
Alternatively, when using @Action then method’s name defaults to act . |
This allows all mixins to follow a similar convention, with the name of the mixin inferred entirely from its type ("placeOrder").
When invoked programmatically, the code reads:
someCustomer.new placeOrder().execute(someProduct, 3);
See also
-
EntityPropertyChangeSubscriber SPI
(only applies to entity objects)
-
(only applies to entity objects)