@DomainObject

The @DomainObject annotation applies to domain objects, collecting together all domain semantics within a single annotation.

Main API

@Inherited
@Target({
        ElementType.TYPE,
        ElementType.ANNOTATION_TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Component @Scope("prototype")
public @interface DomainObject {

    Auditing auditing()                             (1)
            default Auditing.NOT_SPECIFIED;

    Class<?> autoCompleteRepository()               (2)
            default Object.class;

    String autoCompleteAction()                     (3)
            default "autoComplete";

    Bounding bounding()                             (4)
            default Bounding.NOT_SPECIFIED;

    Editing editing()                               (5)
            default Editing.NOT_SPECIFIED;

    String editingDisabledReason()                  (6)
            default "Disabled";

    String mixinMethod()                            (7)
            default Mixin.DEFAULT_METHOD_NAME;

    Nature nature()                                 (8)
            default Nature.NOT_SPECIFIED;

    String objectType()                             (9)
            default "";

    Publishing publishing()                         (10)
            default Publishing.NOT_SPECIFIED;

    // ...
}
1 auditing()

Indicates whether each of the changed properties of an object should be submitted to the registered AuditerService(s)

2 autoCompleteRepository()

Nominate a method on a domain service to be used for looking up instances of the domain object

3 autoCompleteAction()

Override the method name to use on the auto-complete repository

4 bounding()

Whether the number of instances of this domain class is relatively small (a "bounded" set), such that instances could be selected from a drop-down list box or similar.

5 editing()

Whether the object’s properties and collections can be edited or not (ie whether the instance should be considered to be immutable)

6 editingDisabledReason()

The default reason why the object’s properties and collections cannot be edited.

7 mixinMethod()

How to recognize the "reserved" method name, meaning that the mixin’s own name will be inferred from the mixin type. Typical examples are "exec", "execute", "invoke", "apply" and so on. The default "reserved" method name is $$.

8 nature()

Whether the domain object logically is an entity (part of the domain layer) or is a view model (part of the application layer); or is a mixin. If an entity, indicates how its persistence is managed.

9 objectType()

Specify an alias for the domain class used to uniquely identify the object both within the Apache Isis runtime and externally

10 publishing()

Whether changes to the object should be published to the registered PublisherService.

Entity Lifecycle API

These attributes define the events to be fired throughout an the persistence lifecycle of a domain entity:

public @interface DomainObject {

    // ...

    Class<? extends ObjectCreatedEvent<?>>
            createdLifecycleEvent()                         (1)
            default ObjectCreatedEvent.Default.class;

    Class<? extends ObjectPersistingEvent<?>>
            persistingLifecycleEvent()                      (2)
            default ObjectPersistingEvent.Default.class;

    Class<? extends ObjectPersistedEvent<?>>
            persistedLifecycleEvent()                       (3)
            default ObjectPersistedEvent.Default.class;

    Class<? extends ObjectLoadedEvent<?>>
            loadedLifecycleEvent()                          (4)
            default ObjectLoadedEvent.Default.class;

    Class<? extends ObjectUpdatingEvent<?>>
            updatingLifecycleEvent()                        (5)
            default ObjectUpdatingEvent.Default.class;

    Class<? extends ObjectUpdatedEvent<?>>
            updatedLifecycleEvent()                         (6)
            default ObjectUpdatedEvent.Default.class;

    Class<? extends ObjectRemovingEvent<?>>
            removingLifecycleEvent()                        (7)
            default ObjectRemovingEvent.Default.class;

    // ...
}
1 createdLifecycleEvent()

The event type to be posted to the EventBusService whenever an instance is created

2 persistingLifecycleEvent()

The event type to be posted to whenever an instance is about to be persisted

3 persistedLifecycleEvent()

The event type to be posted whenever an instance has just been persisted

4 loadedLifecycleEvent()

The event type to be posted whenever an instance has just been loaded from the datastore

5 removingLifecycleEvent()

The event type to be posted whenever an instance is about to be deleted

6 updatedLifecycleEvent()

The event type to be posted whenever an instance has just been updated

7 updatingLifecycleEvent()

The event type to be posted whenever an instance is about to be updated

Domain Events API

These attributes define the events to be fired when a domain object’s members (its actions, properties and collections) are interacted with.

These attributes define the default event types; they can be overridden for each specific domain member.

public @interface DomainObject {

    // ...

    Class<? extends ActionDomainEvent<?>>
            actionDomainEvent()                             (1)
            default ActionDomainEvent.Default.class;

    Class<? extends PropertyDomainEvent<?,?>>
            propertyDomainEvent()                           (2)
            default PropertyDomainEvent.Default.class;

    Class<? extends CollectionDomainEvent<?,?>>
            collectionDomainEvent()                         (3)
            default CollectionDomainEvent.Default.class;

}
1 actionDomainEvent()

The event type to be posted to the EventBusService whenever an action of the domain object is interacted with (if not overridden using @Action#domainEvent()).

2 propertyDomainEvent()

The event type to be posted to the EventBusService whenever a property of the domain object is interacted with (if not overridden using @Property#domainEvent()).

3 collectionDomainEvent()

The event type to be posted to the EventBusService whenever a collection of the domain object is interacted with (if not overridden using @Collection#domainEvent()).

For example:

@DomainObject(
    auditing=Auditing.ENABLED,
    autoCompleteRepository=CustomerRepository.class
    editing=Editing.ENABLED,                            (1)
    updatedLifecycleEvent=Customer.UpdatedEvent.class

)
public class Customer {
    ...
}
1 default value, so could be omitted

auditing()

The auditing attribute indicates that if the object is modified, then each of its changed properties should be submitted to the registered AuditerService(s).

The default value for the attribute is AS_CONFIGURED, meaning that the isis.applib.annotation.domain-object.auditing 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 auditing() 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(
    auditing=Auditing.ENABLED  (1)
)
public class Customer {
    ...
}
1 because set to enabled, will be audited irrespective of the configured value.

autoCompleteRepository()

The autoCompleteRepository attribute 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

autoCompleteAction()

As noted above, by default the method invoked on the repository is assumed to be called "autoComplete". The optional autoCompleteAction attribute allows the method on the repository 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(nature = NatureOfService.VIEW)
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(nature = NatureOfService.VIEW)
public class Customers {
    @Programmatic
    public List<Customer> findByName(
        @MinLength(3)
        String name);
    ...
}

bounding

r

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 attribute can be used as a hint. For such domain objects the framework will automatically allow instances to be selected; 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.

createdLifecycleEvent()

Whenever a domain object is instantiated or otherwise becomes known to the framework, a "created" lifecycle event is fired. This is typically when the FactoryService's instantiate() method is called.

Subscribers subscribe through the EventBusService and can use the event to obtain a reference to the object just created. The subscriber could then, for example, update the object, eg looking up state from some external datastore.

It’s possible to instantiate objects without firing this lifecycle; just instantiate using its regular constructor, and then use the ServiceRegistry's injectServicesInto(…​) to manually inject any required domain services.

By default the event raised is ObjectCreatedEvent.Default. For example:

@DomainObject
public class ToDoItemDto {
    ...
}

The purpose of the createdLifecycleEvent attribute is to allows a custom subclass to be emitted instead. A similar attribute is available for other lifecycle events.

For example:

@DomainObjectLayout(
    createdLifecycleEvent=ToDoItem.CreatedEvent.class
)
public class ToDoItem {
    public static class CreatedEvent
        extends org.apache.isis.applib.events.lifecycle.ObjectCreatedEvent<ToDoItem> { }
    ...
}

The benefit is that subscribers can be more targeted as to the events that they subscribe to.

Subscribers

Subscribers (which must be domain services) subscribe to events posted through the EventBusService.

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) {
        ...
    }
}

Default, Doop and Noop events

If the createdLifecycleEvent attribute is not explicitly specified (is left as its default value, ObjectCreatedEvent.Default), then the framework will, by default, post an event.

If this is not required, then the isis.reflector.facet.domainObjectAnnotation.createdLifecycleEvent.postForDefault configuration property can be set to "false"; this will disable posting.

On the other hand, if the createdLifecycleEvent has been explicitly specified to some subclass, then an event will be posted. The framework provides ObjectCreatedEvent.Doop as such a subclass, so setting the createdLifecycleEvent attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.

And, conversely, the framework also provides ObjectCreatedEvent.Noop; if createdLifecycleEvent attribute is set to this class, then no event will be posted.

editing()

The editing attribute determines whether a domain object’s properties and collections are not editable (are read-only).

The default is AS_CONFIGURED, meaning that the isis.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 isis.applib.adoc#isis.applib.annotation.domain-object.editing configuration property:

isis.applib.annotation.domain-object.editing=false

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 {
    ...
}

loadedLifecycleEvent()

Whenever a persistent domain object is loaded from the database, a "loaded" lifecycle event is fired.

Subscribers subscribe through the EventBusService and can use the event to obtain a reference to the domain object just loaded. The subscriber could then, for example, update or default values on the object (eg to support on-the-fly migration scenarios).

By default the event raised is ObjectLoadedEvent.Default. For example:

@DomainObject
public class ToDoItemDto {
    ...
}

The purpose of the loadedLifecycleEvent attribute is to allows a custom subclass to be emitted instead. A similar attribute is available for other lifecycle events.

For example:

@DomainObjectLayout(
    loadedLifecycleEvent=ToDoItem.LoadedEvent.class
)
public class ToDoItem {
    public static class LoadedEvent extends
        org.apache.isis.applib.events.lifecycle.ObjectLoadedEvent<ToDoItem> { }
    ...
}

The benefit is that subscribers can be more targeted as to the events that they subscribe to.

Subscribers

Subscribers (which must be domain services) subscribe to events posted through the EventBusService.

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(ObjectLoadedEvent.class)
    public void on(ObjectLoadedEvent 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.ObjectLoadedEvent.class)
    public void on(ToDoItem.ObjectLoadedEvent ev) {
        ...
    }
}

Default, Doop and Noop events

If the loadedLifecycleEvent attribute is not explicitly specified (is left as its default value, ObjectLoadedEvent.Default), then the framework will, by default, post an event.

If this is not required, then the isis.reflector.facet.domainObjectAnnotation.loadedLifecycleEvent.postForDefault configuration property can be set to "false"; this will disable posting.

On the other hand, if the loadedLifecycleEvent has been explicitly specified to some subclass, then an event will be posted. The framework provides ObjectLoadedEvent.Doop as such a subclass, so setting the loadedLifecycleEvent attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.

And, conversely, the framework also provides ObjectLoadedEvent.Noop; if loadedLifecycleEvent attribute is set to this class, then no event will be posted.

mixinMethod()

The mixinMethod attribute 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 {

        public Customer execute(Product p, int quantity) {  (3)
            // ...
        }
        public String disableExecute() {
            // ...
        }
        public String validate0Execute() {
            // ...
        }
    }

)
1 This is a mixin.
Alternatively, could have used @Mixin.
2 This mixin is using a non-standard method.
Alternatively, could have used @Mixin#method().
3 Same name as the specified mixinMethod.

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);

nature()

The nature attribute 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.

  • JDO_ENTITY

    indicates that the domain object is an entity whose persistence is managed internally by Apache Isis, using the JDO/DataNucleus objectstore.

  • EXTERNAL_ENTITY

    indicates that the domain objecct 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.

  • INMEMORY_ENTITY

    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 inmemory 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.

  • 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).

    Equivalent to annotating with @Mixin. 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.

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.

Under the covers Apache Isis' support for VIEW_MODEL, EXTERNAL_ENTITY and INMEMORY_ENTITY domain objects is identical; the state of the object is encoded into its internal OID (represented ultimately as its URL), and is recreated directly from that URL.

Because this particular implementation was originally added to Apache Isis in support of view models, the term was also used for the logically different external entities and inmemory entities.

The benefit of nature() is that it allows the developer to properly characterize the layer (domain vs application) that an entity lives, thus avoiding confusion as "view model" (the implementation technique) and "view model" (the application layer concept).

On the other hand, view models defined in this way do have some limitations; see @ViewModel 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.

persistedLifecycleEvent()

Whenever a (just created, still transient) domain object has been saved (INSERTed in)to the database, a "persisted" lifecycle event is fired.

Subscribers subscribe through the EventBusService and can use the event to obtain a reference to the domain object. The subscriber could then, for example, maintain an external datastore.

The object should not be modified during the persisted callback.

By default the event raised is ObjectPersistedEvent.Default. For example:

@DomainObject
public class ToDoItemDto {
    ...
}

The purpose of the persistedLifecycleEvent attribute is to allows a custom subclass to be emitted instead. A similar attribute is available for other lifecycle events.

For example:

@DomainObjectLayout(
    persistedLifecycleEvent=ToDoItem.PersistedEvent.class
)
public class ToDoItem {

    public static class PersistedEvent extends
        org.apache.isis.applib.events.lifecycle.ObjectPersistedEvent<ToDoItem> { }

    // ...
}

The benefit is that subscribers can be more targeted as to the events that they subscribe to.

Subscribers

Subscribers (which must be domain services) subscribe to events posted through the EventBusService.

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(ObjectPersistedEvent.class)
    public void on(ObjectPersistedEvent 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.ObjectPersistedEvent.class)
    public void on(ToDoItem.ObjectPersistedEvent ev) {
        ...
    }
}

Default, Doop and Noop events

If the persistedLifecycleEvent attribute is not explicitly specified (is left as its default value, ObjectPersistedEvent.Default), then the framework will, by default, post an event.

If this is not required, then the isis.reflector.facet.domainObjectAnnotation.persistedLifecycleEvent.postForDefault configuration property can be set to "false"; this will disable posting.

On the other hand, if the persistedLifecycleEvent has been explicitly specified to some subclass, then an event will be posted. The framework provides ObjectPersistedEvent.Doop as such a subclass, so setting the persistedLifecycleEvent attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.

And, conversely, the framework also provides ObjectPersistedEvent.Noop; if persistedLifecycleEvent attribute is set to this class, then no event will be posted.

persistingLifecycleEvent()

Whenever a (just created, still transient) domain object is about to be saved (INSERTed in)to the database, a "persisting" lifecycle event is fired.

Subscribers subscribe through the EventBusService and can use the event to obtain a reference to the domain object. The subscriber could then, for example, update the object, or it could use it maintain an external datastore. One possible application is to maintain a full-text search database using Apache Lucene or similar.

Another use case is to maintain "last updated by"/"last updated at" properties. While you can roll your own, note that the framework provides built-in support for this use case through the Timestampable role interface.

By default the event raised is ObjectPersistingEvent.Default. For example:

@DomainObject
public class ToDoItemDto {
    ...
}

The purpose of the persistingLifecycleEvent attribute is to allows a custom subclass to be emitted instead. A similar attribute is available for other lifecycle events.

For example:

@DomainObjectLayout(
    persistingLifecycleEvent=ToDoItem.PersistingEvent.class
)
public class ToDoItem {

    public static class PersistingEvent extends
        org.apache.isis.applib.events.lifecycle.ObjectPersistingEvent<ToDoItem> { }

    // ...
}

The benefit is that subscribers can be more targeted as to the events that they subscribe to.

Subscribers

Subscribers (which must be domain services) subscribe to events posted through the EventBusService.

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(ObjectPersistingEvent.class)
    public void on(ObjectPersistingEvent 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.ObjectPersistingEvent.class)
    public void on(ToDoItem.ObjectPersistingEvent ev) {
        // ...
    }
}

Default, Doop and Noop events

If the persistingLifecycleEvent attribute is not explicitly specified (is left as its default value, ObjectPersistingEvent.Default), then the framework will, by default, post an event.

If this is not required, then the isis.reflector.facet.domainObjectAnnotation.persistingLifecycleEvent.postForDefault configuration property can be set to "false"; this will disable posting.

On the other hand, if the persistingLifecycleEvent has been explicitly specified to some subclass, then an event will be posted. The framework provides ObjectPersistingEvent.Doop as such a subclass, so setting the persistingLifecycleEvent attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.

And, conversely, the framework also provides ObjectPersistingEvent.Noop; if persistingLifecycleEvent attribute is set to this class, then no event will be posted.

objectType()

The objectType attribute 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:

Examples

For example:

@DomainObject(
    objectType="orders.Order"
)
public class Order {
    ...
}

Precedence

The rules of precedence are:

  1. @Discriminator

  2. @DomainObject#objectType

  3. @PersistenceCapable, if at least the schema attribute is defined.

    If both schema and table are defined, then the value is “schema.table”. If only schema is defined, then the value is “schema.className”.

  4. 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 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.

publishing()

The publishing attribute determines whether and how a modified object instance is published via the registered implementation of PublisherService.

A common use case is to notify external "downstream" systems of changes in the state of the Apache Isis application.

The default value for the attribute is AS_CONFIGURED, meaning that the isis.applib.annotation.domain-object.publishing configuration property is used to determine the whether the domain object’s changes are published:

  • all

    all changed objects are published

  • none

    no changed objects are published

If there is no configuration property in application.properties then publishing is automatically disabled for domain objects.

This default can be overridden on an object-by-object basis; if publishing() is set to ENABLED then changed instances of the domain class are published irrespective of the configured value; if set to DISABLED then the changed instances are not published, again irrespective of the configured value.

For example:

@DomainObject(
    publishing=Publishing.ENABLED   (1)
)
public class InterestRate {
    ...
}
1 because set to enabled, will be published irrespective of the configured value.

See also

This attribute is also supported for:

  • actions

    where it controls whether action invocations are published as events, and for

  • properties

    where it controls whether property edits are published as events.

removingLifecycleEvent()

r :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. :page-partial:

Whenever a (persistent) domain object is about to be removed (DELETEd) from the database, a "removing" lifecycle event is fired.

Subscribers subscribe through the EventBusService and can use the event to obtain a reference to the domain object. The subscriber could then, for example, could use it maintain an external datastore. One possible application is to maintain a full-text search database using Apache Lucene or similar.

Another use case is to maintain "last updated by"/"last updated at" properties. While you can roll your own, note that the framework provides built-in support for this use case through the Timestampable role interface.

By default the event raised is ObjectRemovingEvent.Default. For example:

@DomainObject
public class ToDoItemDto {
    ...
}

The purpose of the removingLifecycleEvent attribute is to allows a custom subclass to be emitted instead. A similar attribute is available for other lifecycle events.

For example:

@DomainObjectLayout(
    removingLifecycleEvent=ToDoItem.RemovingEvent.class
)
public class ToDoItem {

    public static class RemovingEvent extends
        org.apache.isis.applib.events.lifecycle.ObjectRemovingEvent<ToDoItem> { }

    // ...
}

The benefit is that subscribers can be more targeted as to the events that they subscribe to.

Subscribers

Subscribers (which must be domain services) subscribe to events posted through the EventBusService.

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(ObjectRemovingEvent.class)
    public void on(ObjectRemovingEvent 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.ObjectRemovingEvent.class)
    public void on(ToDoItem.ObjectRemovingEvent ev) {
        ...
    }
}

Default, Doop and Noop events

If the removingLifecycleEvent attribute is not explicitly specified (is left as its default value, ObjectRemovingEvent.Default), then the framework will, by default, post an event.

If this is not required, then the isis.reflector.facet.domainObjectAnnotation.removingLifecycleEvent.postForDefault configuration property can be set to "false"; this will disable posting.

On the other hand, if the removingLifecycleEvent has been explicitly specified to some subclass, then an event will be posted. The framework provides ObjectRemovingEvent.Doop as such a subclass, so setting the removingLifecycleEvent attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.

And, conversely, the framework also provides ObjectRemovingEvent.Noop; if removingLifecycleEvent attribute is set to this class, then no event will be posted.

updatingLifecycleEvent()

Whenever a (persistent) domain object has been modified and is about to be updated to the database, an "updating" lifecycle event is fired.

Subscribers subscribe through the EventBusService and can use the event to obtain a reference to the domain object. The subscriber could then, for example, update the object, or it could use it maintain an external datastore. One possible application is to maintain a full-text search database using Apache Lucene or similar.

Another use case is to maintain "last updated by"/"last updated at" properties. While you can roll your own, note that the framework provides built-in support for this use case through the Timestampable role interface.

By default the event raised is ObjectUpdatingEvent.Default. For example:

@DomainObject
public class ToDoItemDto {
    ...
}

The purpose of the updatingLifecycleEvent attribute is to allows a custom subclass to be emitted instead. A similar attribute is available for other lifecycle events.

For example:

@DomainObjectLayout(
    updatingLifecycleEvent=ToDoItem.UpdatingEvent.class
)
public class ToDoItem {

    public static class UpdatingEvent extends
        org.apache.isis.applib.events.lifecycle.ObjectUpdatingEvent<ToDoItem> { }

    // ...
}

The benefit is that subscribers can be more targeted as to the events that they subscribe to.

Subscribers

Subscribers (which must be domain services) subscribe to events posted through the EventBusService.

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(ObjectUpdatingEvent.class)
    public void on(ObjectUpdatingEvent 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.ObjectUpdatingEvent.class)
    public void on(ToDoItem.ObjectUpdatingEvent ev) {
        ...
    }
}

Default, Doop and Noop events

If the updatingLifecycleEvent attribute is not explicitly specified (is left as its default value, ObjectUpdatingEvent.Default), then the framework will, by default, post an event.

If this is not required, then the isis.reflector.facet.domainObjectAnnotation.updatingLifecycleEvent.postForDefault configuration property can be set to "false"; this will disable posting.

On the other hand, if the updatingLifecycleEvent has been explicitly specified to some subclass, then an event will be posted. The framework provides ObjectUpdatingEvent.Doop as such a subclass, so setting the updatingLifecycleEvent attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.

And, conversely, the framework also provides ObjectUpdatingEvent.Noop; if updatingLifecycleEvent attribute is set to this class, then no event will be posted.

updatedLifecycleEvent()

Whenever a (persistent) domain object has been modified and has been updated in the database, an "updated" lifecycle event is fired.

Subscribers subscribe through the EventBusService and can use the event to obtain a reference to the domain object.

The object should not be modified during the updated callback.

By default the event raised is ObjectUpdatedEvent.Default. For example:

@DomainObject
public class ToDoItemDto {
    ...
}

The purpose of the updatedLifecycleEvent attribute is to allows a custom subclass to be emitted instead. A similar attribute is available for other lifecycle events.

For example:

@DomainObjectLayout(
    updatedLifecycleEvent=ToDoItem.UpdatedEvent.class
)
public class ToDoItem {

    public static class UpdatedEvent extends
        org.apache.isis.applib.events.lifecycle.ObjectUpdatedEvent<ToDoItem> { }

    // ...
}

The benefit is that subscribers can be more targeted as to the events that they subscribe to.

Subscribers

Subscribers (which must be domain services) subscribe to events posted through the EventBusService.

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(ObjectUpdatedEvent.clas)
    public void on(ObjectUpdatedEvent 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.ObjectUpdatedEvent.class)
    public void on(ToDoItem.ObjectUpdatedEvent ev) {
        ...
    }
}

Default, Doop and Noop events

If the updatedLifecycleEvent attribute is not explicitly specified (is left as its default value, ObjectUpdatedEvent.Default), then the framework will, by default, post an event.

If this is not required, then the isis.reflector.facet.domainObjectAnnotation.updatedLifecycleEvent.postForDefault configuration property can be set to "false"; this will disable posting.

On the other hand, if the updatedLifecycleEvent has been explicitly specified to some subclass, then an event will be posted. The framework provides ObjectUpdatedEvent.Doop as such a subclass, so setting the updatedLifecycleEvent attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.

And, conversely, the framework also provides ObjectUpdatedEvent.Noop; if updatedLifecycleEvent attribute is set to this class, then no event will be posted.