You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2018/11/23 08:36:06 UTC

[isis] branch master updated (61ba00e -> 901b63e)

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git.


    from 61ba00e  ISIS-1974: moves further metamodel validation to be evaluated later
     new b9adf68  ISIS-2043: adds domainEvents for @DomainObject
     new cab9e46  ISIS-2043: adds ActionDomainEvent#getSubject() as a convenience
     new c568147  ISIS-2043: adds support for @DomainObject(xxxDomainEvent=...) for mixins
     new 901b63e  ISIS-2043: updates docs for @DomainObject#xxxDomainEvent

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../guides/rgant/_rgant-Action_domainEvent.adoc    |  79 +++++++-----
 .../rgant/_rgant-Collection_domainEvent.adoc       |  44 ++++---
 .../_rgant-DomainObjectLayout_bookmarking.adoc     |   6 +-
 .../_rgant-DomainObjectLayout_cssClassFa.adoc      |   8 +-
 .../_rgant-DomainObjectLayout_describedAs.adoc     |   4 +-
 .../_rgant-DomainObjectLayout_iconUiEvent.adoc     |   6 +-
 .../rgant/_rgant-DomainObjectLayout_named.adoc     |   3 +-
 .../rgant/_rgant-DomainObjectLayout_paged.adoc     |   6 +-
 .../rgant/_rgant-DomainObjectLayout_plural.adoc    |   3 +-
 .../_rgant-DomainObjectLayout_titleUiEvent.adoc    |  34 +++--
 .../_rgant-DomainObject_actionDomainEvent.adoc     |  45 +++++++
 .../_rgant-DomainObject_collectionDomainEvent.adoc |  62 ++++++++++
 ...rgant-DomainObject_persistedLifecycleEvent.adoc |  18 ++-
 ...gant-DomainObject_persistingLifecycleEvent.adoc |  15 +--
 .../_rgant-DomainObject_propertyDomainEvent.adoc   |  62 ++++++++++
 .../rgant/_rgant-DomainObject_publishing.adoc      |  13 +-
 ..._rgant-DomainObject_removingLifecycleEvent.adoc |  24 ++--
 .../_rgant-DomainObject_updatedLifecycleEvent.adoc |  25 ++--
 ..._rgant-DomainObject_updatingLifecycleEvent.adoc |  30 ++---
 .../guides/rgant/_rgant-Property_domainEvent.adoc  |  45 +++----
 ..._ugvw_hints-and-tips_highlight-current-row.adoc |   4 +-
 .../org/apache/isis/applib/annotation/Action.java  |   6 -
 .../apache/isis/applib/annotation/Collection.java  |   6 -
 .../isis/applib/annotation/DomainObject.java       |  81 ++++++++++++
 .../apache/isis/applib/annotation/Property.java    |   6 -
 .../services/eventbus/AbstractDomainEvent.java     |  35 ++++++
 .../services/eventbus/ActionDomainEvent.java       |  16 ---
 .../core/metamodel/facets/DomainEventHelper.java   |  13 ++
 .../facets/SingleClassValueFacetAbstract.java      |   2 +-
 .../action/ActionAnnotationFacetFactory.java       |  22 +++-
 .../invocation/ActionDomainEventFacetAbstract.java |  28 +++--
 .../CollectionAnnotationFacetFactory.java          |  34 ++++-
 ...ectionAddToFacetForDomainEventFromAbstract.java |   7 +-
 .../modify/CollectionDomainEventFacetAbstract.java |  42 ++++---
 ...nRemoveFromFacetForDomainEventFromAbstract.java |   5 +-
 .../DomainObjectAnnotationFacetFactory.java        |  54 ++++++++
 ...ventDefaultFacetForDomainObjectAnnotation.java} |  35 +++---
 ...ventDefaultFacetForDomainObjectAnnotation.java} |  34 ++---
 ...ventDefaultFacetForDomainObjectAnnotation.java} |  34 ++---
 .../property/PropertyAnnotationFacetFactory.java   |  69 +++++++++--
 .../modify/PropertyDomainEventFacetAbstract.java   |  80 +++++++-----
 .../modify/PropertyDomainEventFacetDefault.java    |   8 +-
 ...acetForPostsPropertyChangedEventAnnotation.java |   8 +-
 ...pertyDomainEventFacetForPropertyAnnotation.java |   8 +-
 ...EventFacetForPropertyInteractionAnnotation.java |   8 +-
 ...tySetterOrClearFacetForDomainEventAbstract.java |  10 +-
 .../param/DeriveFacetsPostProcessor.java           | 137 +++++++++++++++++++++
 .../simple/subscribers/SimpleObjectListener.java   |   4 +
 48 files changed, 969 insertions(+), 359 deletions(-)
 create mode 100644 adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_actionDomainEvent.adoc
 create mode 100644 adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_collectionDomainEvent.adoc
 create mode 100644 adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_propertyDomainEvent.adoc
 copy core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/{callbacks/RemovingLifecycleEventFacetForDomainObjectAnnotation.java => domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java} (58%)
 copy core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/{callbacks/RemovingLifecycleEventFacetForDomainObjectAnnotation.java => domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java} (57%)
 copy core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/{callbacks/RemovingLifecycleEventFacetForDomainObjectAnnotation.java => domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java} (58%)
 create mode 100644 example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/subscribers/SimpleObjectListener.java


[isis] 02/04: ISIS-2043: adds ActionDomainEvent#getSubject() as a convenience

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit cab9e464aa0b9661ada58ab3a0f9b2512afc63d0
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Nov 21 22:42:12 2018 +0100

    ISIS-2043: adds ActionDomainEvent#getSubject() as a convenience
---
 .../isis/applib/services/eventbus/ActionDomainEvent.java | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java
index 1ff72cf..1d97fc2 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java
@@ -210,6 +210,22 @@ public abstract class ActionDomainEvent<S> extends AbstractInteractionEvent<S> {
     }
     // endregion
 
+
+
+    // region > subject
+
+    /**
+     * The subject of the event, which will be either the {@link #getSource() source} for a regular action, or the
+     * {@link #getMixedIn() mixed-in} domain object for a mixin.
+     */
+    public Object getSubject() {
+        final Object mixedIn = getMixedIn();
+        return mixedIn != null ? mixedIn : getSource();
+    }
+
+
+    //endregion
+
     //region > arguments
     private List<Object> arguments;
     /**


[isis] 04/04: ISIS-2043: updates docs for @DomainObject#xxxDomainEvent

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 901b63e2236bd93ed3af8a4b5e188dddecd27dd5
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Nov 22 19:11:07 2018 +0100

    ISIS-2043: updates docs for @DomainObject#xxxDomainEvent
---
 .../guides/rgant/_rgant-Action_domainEvent.adoc    | 79 ++++++++++++++--------
 .../rgant/_rgant-Collection_domainEvent.adoc       | 44 ++++++------
 .../_rgant-DomainObjectLayout_bookmarking.adoc     |  6 +-
 .../_rgant-DomainObjectLayout_cssClassFa.adoc      |  8 +--
 .../_rgant-DomainObjectLayout_describedAs.adoc     |  4 +-
 .../_rgant-DomainObjectLayout_iconUiEvent.adoc     |  6 +-
 .../rgant/_rgant-DomainObjectLayout_named.adoc     |  3 +-
 .../rgant/_rgant-DomainObjectLayout_paged.adoc     |  6 +-
 .../rgant/_rgant-DomainObjectLayout_plural.adoc    |  3 +-
 .../_rgant-DomainObjectLayout_titleUiEvent.adoc    | 34 ++++------
 .../_rgant-DomainObject_actionDomainEvent.adoc     | 45 ++++++++++++
 .../_rgant-DomainObject_collectionDomainEvent.adoc | 62 +++++++++++++++++
 ...rgant-DomainObject_persistedLifecycleEvent.adoc | 18 ++---
 ...gant-DomainObject_persistingLifecycleEvent.adoc | 15 ++--
 .../_rgant-DomainObject_propertyDomainEvent.adoc   | 62 +++++++++++++++++
 .../rgant/_rgant-DomainObject_publishing.adoc      | 13 ++--
 ..._rgant-DomainObject_removingLifecycleEvent.adoc | 24 +++----
 .../_rgant-DomainObject_updatedLifecycleEvent.adoc | 25 +++----
 ..._rgant-DomainObject_updatingLifecycleEvent.adoc | 30 ++++----
 .../guides/rgant/_rgant-Property_domainEvent.adoc  | 45 ++++++------
 ..._ugvw_hints-and-tips_highlight-current-row.adoc |  4 +-
 21 files changed, 347 insertions(+), 189 deletions(-)

diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_domainEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_domainEvent.adoc
index 504a118..ed63229 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_domainEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_domainEvent.adoc
@@ -5,7 +5,8 @@
 :_imagesdir: images/
 
 
-Whenever a domain object (or list of domain objects) is to be rendered, the framework fires off multiple domain events for every property, collection and action of the domain object.  In the cases of the domain object's actions, the events that are fired are:
+Whenever a domain object (or list of domain objects) is to be rendered, the framework fires off multiple domain events for every property, collection and action of the domain object.
+In the cases of the domain object's actions, the events that are fired are:
 
 * hide phase: to check that the action is visible (has not been hidden)
 * disable phase: to check that the action is usable (has not been disabled)
@@ -15,7 +16,8 @@ Whenever a domain object (or list of domain objects) is to be rendered, the fram
 
 Subscribers subscribe through the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] using either link:https://github.com/google/guava[Guava] or link:http://www.axonframework.org/[Axon Framework] annotations and can influence each of these phases.
 
-By default the event raised is `ActionDomainEvent.Default`. For example:
+By default the event raised is `ActionDomainEvent.Default`.
+For example:
 
 [source,java]
 ----
@@ -28,9 +30,9 @@ public class ToDoItem {
 
 
 The `domainEvent()` attribute allows a custom subclass to be emitted allowing more precise subscriptions (to those
-subclasses) to be defined instead.  This attribute is also supported for
-xref:../rgant/rgant.adoc#_rgant-Collection_domainEvent[collections] and
-xref:../rgant/rgant.adoc#_rgant-Property_domainEvent[properties].
+subclasses) to be defined instead.
+This attribute is also supported for
+xref:../rgant/rgant.adoc#_rgant-Collection_domainEvent[collections] and xref:../rgant/rgant.adoc#_rgant-Property_domainEvent[properties].
 
 For example:
 
@@ -54,10 +56,6 @@ This substantially reduces the boilerplate required in subclasses because no exp
 
 
 
-
-
-
-
 == Subscribers
 
 Subscribers (which must be domain services) subscribe using either the link:https://github.com/google/guava[Guava] API or (if the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] has been appropriately configured) using the link:http://www.axonframework.org/[Axon Framework] API.  The examples below use the Guava API.
@@ -68,7 +66,8 @@ Subscribers can be either coarse-grained (if they subscribe to the top-level eve
 ----
 @DomainService(nature=NatureOfService.DOMAIN)
 public class SomeSubscriber extends AbstractSubscriber {
-    @com.google.common.eventbus.Subscribe
+    @org.axonframework.eventhandling.annotation.EventHandler // if using axon
+    @com.google.common.eventbus.Subscribe                    // if using guava
     public void on(ActionDomainEvent ev) {
         ...
     }
@@ -81,17 +80,14 @@ or can be fine-grained (by subscribing to specific event subtypes):
 ----
 @DomainService(nature=NatureOfService.DOMAIN)
 public class SomeSubscriber extends AbstractSubscriber {
-    @com.google.common.eventbus.Subscribe
+    @org.axonframework.eventhandling.annotation.EventHandler // if using axon
+    @com.google.common.eventbus.Subscribe                    // if using guava
     public void on(ToDoItem.CompletedEvent ev) {
         ...
     }
 }
 ----
 
-[TIP]
-====
-If the AxonFramework is being used, replace `@com.google.common.eventbus.Subscribe` with `@org.axonframework.eventhandling.annotation.EventHandler`.
-====
 
 
 The subscriber's method is called (up to) 5 times:
@@ -99,15 +95,16 @@ The subscriber's method is called (up to) 5 times:
 * whether to veto visibility (hide)
 * whether to veto usability (disable)
 * whether to veto execution (validate)
-* steps to perform prior to the action being invoked.
-* steps to perform after the action has been invoked.
+* steps to perform prior to the action being invoked
+* steps to perform after the action has been invoked
 
-The subscriber can distinguish these by calling `ev.getEventPhase()`. Thus the general form is:
+The subscriber can distinguish these by calling `ev.getEventPhase()`.
+Thus the general form is:
 
 [source,java]
 ----
-@Programmatic
-@com.google.common.eventbus.Subscribe
+@org.axonframework.eventhandling.annotation.EventHandler // if using axon
+@com.google.common.eventbus.Subscribe                    // if using guava
 public void on(ActionDomainEvent ev) {
     switch(ev.getEventPhase()) {
         case HIDE:
@@ -135,28 +132,50 @@ It is also possible to abort the transaction during the executing or executed ph
 
 == Default, Doop and Noop events
 
-If the `domainEvent` attribute is not explicitly specified (is left as its default value, `ActionDomainEvent.Default`),
-then the framework will, by default, post an event.
+If the `domainEvent` attribute is not explicitly specified (is left as its default value, `ActionDomainEvent.Default`), then the framework will, by default, post an event.
 
-If this is not required, then the `isis.reflector.facet.actionAnnotation.domainEvent.postForDefault`
-configuration property can be set to "false"; this will disable posting.
+If this is not required, then the `isis.reflector.facet.actionAnnotation.domainEvent.postForDefault` configuration property can be set to "false"; this will disable posting.
 
 On the other hand, if the `domainEvent` has been explicitly specified to some subclass, then an event will be posted.
-The framework provides `ActionDomainEvent.Doop` as such a subclass, so setting the `domainEvent` attribute to this class
-will ensure that the event to be posted, irrespective of the configuration property setting.
+The framework provides `ActionDomainEvent.Doop` as such a subclass, so setting the `domainEvent` attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.
+
+And, conversely, the framework also provides `ActionDomainEvent.Noop`; if `domainEvent` attribute is set to this class, then no event will be posted.
+
+
+
+=== Class-level default
 
-And, conversely, the framework also provides `ActionDomainEvent.Noop`; if `domainEvent` attribute is set to this class,
-then no event will be posted.
+Sometimes a subscriber is interested in all of the actions of a given class, though not any individual action.
+A common use case is to hide or disable all actions for some particular object for some particular user group.
 
+For this use, the default action domain event can be annotated using `@DomainObject`:
 
+[source,java]
+----
+@DomainObject(
+    actionDomainEvent=ToDoItem.ActionDomainEventDefault.class
+)
+public class ToDoItem {
+    public static class ActionDomainEventDefault
+        extends org.apache.isis.applib.services.eventbus.ActionDomainEvent<Object> { }
+    ...
 
+    public void updateDescription(final String description) {
+        this.description = description;
+    }
 
+}
+----
 
 
 == Raising events programmatically
 
-Normally events are only raised for interactions through the UI. However, events can be raised programmatically either by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly, or by emulating the UI by
-wrapping the target object using the xref:../rgsvc/rgsvc.adoc#_rgsvc_application-layer-api_WrapperFactory[`WrapperFactory`] domain service.
+Normally events are only raised for interactions through the UI.
+However, events can be raised programmatically either by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly, or by emulating the UI by wrapping the target object using the xref:../rgsvc/rgsvc.adoc#_rgsvc_application-layer-api_WrapperFactory[`WrapperFactory`] domain service.
+
+
+
+
 
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Collection_domainEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Collection_domainEvent.adoc
index 3ac38e7..043652c 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Collection_domainEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Collection_domainEvent.adoc
@@ -5,7 +5,8 @@
 :_imagesdir: images/
 
 
-Whenever a domain object (or list of domain objects) is to be rendered, the framework fires off multiple domain events for every property, collection and action of the domain object.  In the cases of the domain object's collections, the events that are fired are:
+Whenever a domain object (or list of domain objects) is to be rendered, the framework fires off multiple domain events for every property, collection and action of the domain object.
+In the cases of the domain object's collections, the events that are fired are:
 
 * hide phase: to check that the collection is visible (has not been hidden)
 * disable phase: to check that the collection is usable (has not been disabled)
@@ -18,7 +19,8 @@ Subscribers subscribe through the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-ap
 
 [WARNING]
 ====
-The xref:../ugvw/ugvw.adoc#[Wicket viewer] does _not_ currently support the modification of collections; they are rendered read-only.  However, domain events are still relevant to determine if such collections should be hidden.
+The xref:../ugvw/ugvw.adoc#[Wicket viewer] does _not_ currently support the modification of collections; they are rendered read-only.
+However, domain events are still relevant to determine if such collections should be hidden.
 
 The workaround is to create add/remove actions and use xref:../rgant/rgant.adoc#_rgant-MemberOrder[UI hints] to render them close to the collection.
 ====
@@ -34,8 +36,8 @@ public class ToDoItem {
 }
 ----
 
-The `domainEvent()` attribute allows a custom subclass to be emitted allowing more precise subscriptions (to those
-subclasses) to be defined instead.  This attribute is also supported for
+The `domainEvent()` attribute allows a custom subclass to be emitted allowing more precise subscriptions (to those subclasses) to be defined instead.
+This attribute is also supported for
 xref:../rgant/rgant.adoc#_rgant-Action_domainEvent[actions] and
 xref:../rgant/rgant.adoc#_rgant-Property_domainEvent[properties].
 
@@ -73,7 +75,8 @@ This substantially reduces the boilerplate in the subclasses because no explicit
 
 == Subscribers
 
-Subscribers (which must be domain services) subscribe using either the link:https://github.com/google/guava[Guava] API or (if the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] has been appropriately configured) using the link:http://www.axonframework.org/[Axon Framework] API.  The examples below use the Guava API.
+Subscribers (which must be domain services) subscribe using either the link:https://github.com/google/guava[Guava] API or (if the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] has been appropriately configured) using the link:http://www.axonframework.org/[Axon Framework] API.
+The examples below use the Guava API.
 
 Subscribers can be either coarse-grained (if they subscribe to the top-level event type):
 
@@ -94,17 +97,14 @@ or can be fine-grained (by subscribing to specific event subtypes):
 ----
 @DomainService(nature=NatureOfService.DOMAIN)
 public class SomeSubscriber extends AbstractSubscriber {
-    @com.google.common.eventbus.Subscribe
+    @org.axonframework.eventhandling.annotation.EventHandler // if using axon
+    @com.google.common.eventbus.Subscribe                    // if using guava
     public void on(ToDoItem.DependenciesChangedEvent ev) {
         ...
     }
 }
 ----
 
-[TIP]
-====
-If the AxonFramework is being used, replace `@com.google.common.eventbus.Subscribe` with `@org.axonframework.eventhandling.annotation.EventHandler`.
-====
 
 The subscriber's method is called (up to) 5 times:
 
@@ -112,9 +112,10 @@ The subscriber's method is called (up to) 5 times:
 * whether to veto usability (disable)
 * whether to veto execution (validate) the element being added to/removed from the collection
 * steps to perform prior to the collection being added to/removed from
-* steps to perform after the collection has been added to/removed from.
+* steps to perform after the collection has been added to/removed from
 
-The subscriber can distinguish these by calling `ev.getEventPhase()`. Thus the general form is:
+The subscriber can distinguish these by calling `ev.getEventPhase()`.
+Thus the general form is:
 
 [source,java]
 ----
@@ -140,25 +141,22 @@ public void on(CollectionDomainEvent ev) {
 }
 ----
 
-It is also possible to abort the transaction during the executing or executed phases by throwing an exception. If the exception is a subtype of `RecoverableException` then the exception will be rendered as a user-friendly warning (eg Growl/toast) rather than an error.
+It is also possible to abort the transaction during the executing or executed phases by throwing an exception.
+If the exception is a subtype of `RecoverableException` then the exception will be rendered as a user-friendly warning (eg Growl/toast) rather than an error.
 
 
 
 
 == Default, Doop and Noop events
 
-If the `domainEvent` attribute is not explicitly specified (is left as its default value, `CollectionDomainEvent.Default`),
-then the framework will, by default, post an event.
+If the `domainEvent` attribute is not explicitly specified (is left as its default value, `CollectionDomainEvent.Default`), then the framework will, by default, post an event.
 
-If this is not required, then the `isis.reflector.facet.collectionAnnotation.domainEvent.postForDefault`
-configuration collection can be set to "false"; this will disable posting.
+If this is not required, then the `isis.reflector.facet.collectionAnnotation.domainEvent.postForDefault` configuration collection can be set to "false"; this will disable posting.
 
 On the other hand, if the `domainEvent` has been explicitly specified to some subclass, then an event will be posted.
-The framework provides `CollectionDomainEvent.Doop` as such a subclass, so setting the `domainEvent` attribute to this class
-will ensure that the event to be posted, irrespective of the configuration collection setting.
+The framework provides `CollectionDomainEvent.Doop` as such a subclass, so setting the `domainEvent` attribute to this class will ensure that the event to be posted, irrespective of the configuration collection setting.
 
-And, conversely, the framework also provides `CollectionDomainEvent.Noop`; if `domainEvent` attribute is set to this class,
-then no event will be posted.
+And, conversely, the framework also provides `CollectionDomainEvent.Noop`; if `domainEvent` attribute is set to this class, then no event will be posted.
 
 
 
@@ -166,8 +164,8 @@ then no event will be posted.
 
 == Raising events programmatically
 
-Normally events are only raised for interactions through the UI. However, events can be raised programmatically either by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly, or by emulating the UI by
-wrapping the target object using the xref:../rgsvc/rgsvc.adoc#_rgsvc_application-layer-api_WrapperFactory[`WrapperFactory`] domain service.
+Normally events are only raised for interactions through the UI.
+However, events can be raised programmatically either by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly, or by emulating the UI by wrapping the target object using the xref:../rgsvc/rgsvc.adoc#_rgsvc_application-layer-api_WrapperFactory[`WrapperFactory`] domain service.
 
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_bookmarking.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_bookmarking.adoc
index 98cb467..281cbdc 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_bookmarking.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_bookmarking.adoc
@@ -6,7 +6,8 @@
 
 
 
-The `bookmarking()` attribute indicates that an entity is automatically bookmarked. This attribute is also supported for  xref:../rgant/rgant.adoc#_rgant-Action_bookmarking[domain objects].
+The `bookmarking()` attribute indicates that an entity is automatically bookmarked.
+This attribute is also supported for  xref:../rgant/rgant.adoc#_rgant-Action_bookmarking[domain objects].
 
 (In the Wicket viewer), a link to a bookmarked object is shown in the bookmarks panel:
 
@@ -30,7 +31,8 @@ public class ToDoItem ... {
 indicates that the `ToDoItem` class is bookmarkable:
 
 
-It is also possible to nest bookmarkable entities. For example, this screenshot is taken from http://github.com/estatio/estatio[Estatio]:
+It is also possible to nest bookmarkable entities.
+For example, this screenshot is taken from http://github.com/estatio/estatio[Estatio]:
 
 image::{_imagesdir}reference-annotations/DomainObjectLayout/bookmarking-nested.png[width="720px",link="{_imagesdir}reference-annotations/DomainObjectLayout/bookmarking-nested.png"]
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassFa.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassFa.adoc
index 4b25012..9045c53 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassFa.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassFa.adoc
@@ -24,7 +24,9 @@ For example:
 public class ToDoItem { ... }
 ----
 
-There can be multiple "fa-" classes, eg to mirror or rotate the icon. There is no need to include the mandatory `fa` "marker" CSS class; it will be automatically added to the list.  The `fa-` prefix can also be omitted from the class names; it will be prepended to each if required.
+There can be multiple "fa-" classes, eg to mirror or rotate the icon.
+There is no need to include the mandatory `fa` "marker" CSS class; it will be automatically added to the list.
+The `fa-` prefix can also be omitted from the class names; it will be prepended to each if required.
 
 
 The related `cssClassFaPosition()` attribute is currently unused for domain objects; the icon is always rendered to the left.
@@ -32,9 +34,7 @@ The related `cssClassFaPosition()` attribute is currently unused for domain obje
 
 [TIP]
 ====
-The similar xref:../rgant/rgant.adoc#_rgant-DomainObjectLayout_cssClass[`@DomainObjectLayout#cssClass()`] annotation attribute is also used as a hint
-to apply CSS, but for wrapping the representation of an object or object
-member so that it can be styled in an application-specific way.
+The similar xref:../rgant/rgant.adoc#_rgant-DomainObjectLayout_cssClass[`@DomainObjectLayout#cssClass()`] annotation attribute is also used as a hint to apply CSS, but for wrapping the representation of an object or object member so that it can be styled in an application-specific way.
 ====
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_describedAs.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_describedAs.adoc
index ac6b00a..25d7fbb 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_describedAs.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_describedAs.adoc
@@ -5,7 +5,9 @@
 :_imagesdir: images/
 
 
-The `describedAs()` attribute is used to provide a short description of the domain object to the user.  In the xref:../ugvw/ugvw.adoc#[Wicket viewer] it is displayed as a 'tool tip'.  The attribute can also be specified for xref:../rgant/rgant.adoc#_rgant-CollectionLayout_describedAs[collections],  xref:../rgant/rgant.adoc#_rgant-PropertyLayout_describedAs[properties], xref:../rgant/rgant.adoc#_rgant-ActionLayout_describedAs[actions], xref:../rgant/rgant.adoc#_rgant-ParameterLayout_descr [...]
+The `describedAs()` attribute is used to provide a short description of the domain object to the user.
+In the xref:../ugvw/ugvw.adoc#[Wicket viewer] it is displayed as a 'tool tip'.
+The attribute can also be specified for xref:../rgant/rgant.adoc#_rgant-CollectionLayout_describedAs[collections],  xref:../rgant/rgant.adoc#_rgant-PropertyLayout_describedAs[properties], xref:../rgant/rgant.adoc#_rgant-ActionLayout_describedAs[actions], xref:../rgant/rgant.adoc#_rgant-ParameterLayout_describedAs[parameters] and xref:../rgant/rgant.adoc#_rgant-ViewModelLayout_describedAs[view models].
 
 For example:
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_iconUiEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_iconUiEvent.adoc
index 15a4eac..854d822 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_iconUiEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_iconUiEvent.adoc
@@ -97,11 +97,9 @@ If the `iconUiEvent` attribute is not explicitly specified (is left as its defau
 If this is not required, then the `isis.reflector.facet.domainObjectLayoutAnnotation.iconUiEvent.postForDefault` configuration property can be set to "false"; this will disable posting.
 
 On the other hand, if the `iconUiEvent` has been explicitly specified to some subclass, then an event will be posted.
-The framework provides `IconUiEvent.Doop` as such a subclass, so setting the `iconUiEvent` attribute to this class
-will ensure that the event to be posted, irrespective of the configuration property setting.
+The framework provides `IconUiEvent.Doop` as such a subclass, so setting the `iconUiEvent` attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.
 
-And, conversely, the framework also provides `IconUiEvent.Noop`; if `iconUiEvent` attribute is set to this class,
-then no event will be posted.
+And, conversely, the framework also provides `IconUiEvent.Noop`; if `iconUiEvent` attribute is set to this class, then no event will be posted.
 
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_named.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_named.adoc
index c5e702e..07524e6 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_named.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_named.adoc
@@ -5,7 +5,8 @@
 :_imagesdir: images/
 
 
-The `named()` attribute explicitly specifies the domain object's name, overriding the name that would normally be inferred from the Java source code.  The attribute can also be specified for xref:../rgant/rgant.adoc#_rgant-ActionLayout_named[actions], xref:../rgant/rgant.adoc#_rgant-CollectionLayout_named[collections], xref:../rgant/rgant.adoc#_rgant-PropertyLayout_named[properties], xref:../rgant/rgant.adoc#_rgant-ParameterLayout_named[parameters], xref:../rgant/rgant.adoc#_rgant-ViewMo [...]
+The `named()` attribute explicitly specifies the domain object's name, overriding the name that would normally be inferred from the Java source code.
+The attribute can also be specified for xref:../rgant/rgant.adoc#_rgant-ActionLayout_named[actions], xref:../rgant/rgant.adoc#_rgant-CollectionLayout_named[collections], xref:../rgant/rgant.adoc#_rgant-PropertyLayout_named[properties], xref:../rgant/rgant.adoc#_rgant-ParameterLayout_named[parameters], xref:../rgant/rgant.adoc#_rgant-ViewModelLayout_named[view models] and xref:../rgant/rgant.adoc#_rgant-DomainServiceLayout_named[domain services].
 
 
 [TIP]
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_paged.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_paged.adoc
index fb206ee..e9632ba 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_paged.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_paged.adoc
@@ -6,12 +6,14 @@
 
 
 
-The `paged()` attribute specifies the number of rows to display in a standalone collection, as returned from an action invocation. This attribute can also be applied to xref:../rgant/rgant.adoc#_rgant-CollectionLayout_paged[collections] and xref:../rgant/rgant.adoc#_rgant-ViewModelLayout_paged[view models].
+The `paged()` attribute specifies the number of rows to display in a standalone collection, as returned from an action invocation.
+This attribute can also be applied to xref:../rgant/rgant.adoc#_rgant-CollectionLayout_paged[collections] and xref:../rgant/rgant.adoc#_rgant-ViewModelLayout_paged[view models].
 
 
 [WARNING]
 ====
-The xref:../ugvro/ugvro.adoc#[RestfulObjects viewer] currently does not support paging.   The xref:../ugvw/ugvw.adoc#[Wicket viewer] _does_ support paging, but note that the paging is performed client-side rather than server-side.
+The xref:../ugvro/ugvro.adoc#[RestfulObjects viewer] currently does not support paging.
+The xref:../ugvw/ugvw.adoc#[Wicket viewer] _does_ support paging, but note that the paging is performed client-side rather than server-side.
 
 We therefore recommend that large collections should instead be modelled as actions (to allow filtering to be applied to limit the number of rows).
 ====
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_plural.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_plural.adoc
index 60c38fc..c1f979c 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_plural.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_plural.adoc
@@ -10,7 +10,8 @@ When Apache Isis displays a standalone collection of several objects, it will la
 
 By default the plural name will be derived from the end of the singular name, with support for some basic English language defaults (eg using "ies" for names ending with a "y").
 
-The `plural()` attribute allows the plural form of the class name to be specified explicitly.  This attribute is also supported for xref:../rgant/rgant.adoc#_rgant-ViewModelLayout_plural[view models].
+The `plural()` attribute allows the plural form of the class name to be specified explicitly.
+This attribute is also supported for xref:../rgant/rgant.adoc#_rgant-ViewModelLayout_plural[view models].
 
 For example:
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_titleUiEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_titleUiEvent.adoc
index bc405f4..123f72b 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_titleUiEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_titleUiEvent.adoc
@@ -6,22 +6,21 @@
 
 
 Whenever a domain object is to be rendered, the framework fires off a title UI event to obtain a title for the object.
-This is as an alternative to implementing xref:../rgcms/rgcms.adoc#_rgcms_methods_reserved_title[`title()`] reserved method, or
-using the xref:../rgant/rgant.adoc#_rgant-Title[`@Title`] annotation, within the class itself.  (If either
-`title()` or `@Title` are present, then they will take precedence).
+This is as an alternative to implementing xref:../rgcms/rgcms.adoc#_rgcms_methods_reserved_title[`title()`] reserved method, or using the xref:../rgant/rgant.adoc#_rgant-Title[`@Title`] annotation, within the class itself.
+(If either `title()` or `@Title` are present, then they will take precedence).
 
 Subscribers subscribe through the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] and can
-use obtain a reference to the domain object from the event.  From this they can, if they wish, specify a title for
-the domain object using the event's API.
+use obtain a reference to the domain object from the event.
+From this they can, if they wish, specify a title for the domain object using the event's API.
 
 [NOTE]
 ====
-The feature was originally introduced so that xref:../rgant/rgant.adoc#_rgant-XmlRootElement[`@XmlRootElement`]-annotated
-xref:../ugfun/ugfun.adoc#_ugfun_building-blocks_types-of-domain-objects_view-models[view model]s could be kept as minimal as possible, just defining the data.
+The feature was originally introduced so that xref:../rgant/rgant.adoc#_rgant-XmlRootElement[`@XmlRootElement`]-annotated xref:../ugfun/ugfun.adoc#_ugfun_building-blocks_types-of-domain-objects_view-models[view model]s could be kept as minimal as possible, just defining the data.
 UI events allow subscribers to provide UI hints, while xref:../ugfun/ugfun.adoc#_ugfun_building-blocks_types-of-domain-objects_mixins[mixin]s can be used to provide the behaviour.
 ====
 
-By default the event raised is `TitleUiEvent.Default`. For example:
+By default the event raised is `TitleUiEvent.Default`.
+For example:
 
 [source,java]
 ----
@@ -31,8 +30,8 @@ public class ToDoItemDto {
 }
 ----
 
-The purpose of the `titleUiEvent()` attribute is to allows a custom subclass to be emitted instead.  A similar
-attribute is available for icon names and CSS classes.
+The purpose of the `titleUiEvent()` attribute is to allows a custom subclass to be emitted instead.
+A similar attribute is available for icon names and CSS classes.
 
 For example:
 
@@ -86,26 +85,21 @@ public class SomeSubscriber extends AbstractSubscriber {
 }
 ----
 
-The subscriber should then use either `TitleUiEvent#setTranslatableTitle(...)` or `TitleUiEvent#setTitle(...)` to
-actually specify the title to be used.
+The subscriber should then use either `TitleUiEvent#setTranslatableTitle(...)` or `TitleUiEvent#setTitle(...)` to actually specify the title to be used.
 
 
 
 
 == Default, Doop and Noop events
 
-If the `titleUiEvent` attribute is not explicitly specified (is left as its default value, `TitleUiEvent.Default`),
-then the framework will, by default, post an event.
+If the `titleUiEvent` attribute is not explicitly specified (is left as its default value, `TitleUiEvent.Default`), then the framework will, by default, post an event.
 
-If this is not required, then the `isis.reflector.facet.domainObjectLayoutAnnotation.titleUiEvent.postForDefault`
-configuration property can be set to "false"; this will disable posting.
+If this is not required, then the `isis.reflector.facet.domainObjectLayoutAnnotation.titleUiEvent.postForDefault` configuration property can be set to "false"; this will disable posting.
 
 On the other hand, if the `titleUiEvent` has been explicitly specified to some subclass, then an event will be posted.
-The framework provides `TitleUiEvent.Doop` as such a subclass, so setting the `titleUiEvent` attribute to this class
-will ensure that the event to be posted, irrespective of the configuration property setting.
+The framework provides `TitleUiEvent.Doop` as such a subclass, so setting the `titleUiEvent` attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.
 
-And, conversely, the framework also provides `TitleUiEvent.Noop`; if `titleUiEvent` attribute is set to this class,
-thn no event will be posted.
+And, conversely, the framework also provides `TitleUiEvent.Noop`; if `titleUiEvent` attribute is set to this class, thn no event will be posted.
 
 
 == Raising events programmatically
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_actionDomainEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_actionDomainEvent.adoc
new file mode 100644
index 0000000..68f16e2
--- /dev/null
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_actionDomainEvent.adoc
@@ -0,0 +1,45 @@
+[[_rgant-DomainObject_actionDomainEvent]]
+= `actionDomainEvent()`
+: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 ag [...]
+:_basedir: ../../
+:_imagesdir: images/
+
+
+Whenever an action 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).
+Using xref:rgant.adoc#_rgant_Action_domainEvent[`@Action#domainEvent()`], the actual domain event class fired can be customised.
+the `@DomainObject#actionDomainEvent()` attribute allows a common event domain class to be fired for all of the actions of the domain object.
+
+For example:
+
+[source,java]
+----
+@DomainObject(
+    actionDomainEvent=ToDoItem.ActionDomainEventDefault.class
+)
+public class ToDoItem {
+    public static class ActionDomainEventDefault
+        extends org.apache.isis.applib.services.eventbus.ActionDomainEvent<Object> { }
+    ...
+
+    public void updateDescription(final String description) {
+        this.description = description;
+    }
+
+}
+----
+
+
+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" 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()`.
+
+[NOTE]
+====
+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 `Object` as their generic type.
+====
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_collectionDomainEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_collectionDomainEvent.adoc
new file mode 100644
index 0000000..22b39bf
--- /dev/null
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_collectionDomainEvent.adoc
@@ -0,0 +1,62 @@
+[[_rgant-DomainObject_collectionDomainEvent]]
+= `collectionDomainEvent()`
+: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 ag [...]
+:_basedir: ../../
+:_imagesdir: images/
+
+
+Whenever a collection 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).
+Using xref:rgant.adoc#_rgant_Collection_domainEvent[`@Collection#domainEvent()`], the actual domain event class fired can be customised.
+the `@DomainObject#collectionDomainEvent()` attribute allows a common event domain class to be fired for all of the collections of the domain object.
+
+For example:
+
+[source,java]
+----
+@DomainObject(
+    collectionDomainEvent=ToDoItem.CollectionDomainEventDefault.class
+)
+public class ToDoItem {
+    public static class CollectionDomainEventDefault
+        extends org.apache.isis.applib.services.eventbus.CollectionDomainEvent<Object> { }
+    ...
+
+    @Getter @Setter
+    private Set<Category> categories = Sets.newTreeSet();
+}
+----
+
+If there is a mixin for the domain object, then this will also honour the domain event.
+For example:
+
+[source,java]
+----
+@Mixin(method="coll")
+public class ToDoItem_related {
+    private final ToDoItem todoItem;
+    // constructor omitted
+
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(contributed = Contributed.AS_ASSOCIATION)
+    @Collection
+    @CollectionLayout(defaultView = "table")
+    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()`.
+
+[NOTE]
+====
+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 `Object` as their first generic type.
+
+They should _also_ have `Object` for their second generic type, because the element type of the various collections of the domain object will or could be different.
+====
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_persistedLifecycleEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_persistedLifecycleEvent.adoc
index 5046ee8..ebe5afe 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_persistedLifecycleEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_persistedLifecycleEvent.adoc
@@ -5,8 +5,7 @@
 :_imagesdir: images/
 
 
-Whenever a (just created, still transient) domain object has been saved (INSERTed in)to the database, a "persisted" lifecycle
-event is fired.
+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 xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] and can use the event to obtain a reference to the domain object.
 The subscriber could then, for example, maintain an external datastore.
@@ -16,7 +15,8 @@ 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:
+By default the event raised is `ObjectPersistedEvent.Default`.
+For example:
 
 [source,java]
 ----
@@ -89,18 +89,14 @@ public class SomeSubscriber extends AbstractSubscriber {
 
 == 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 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.
+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.
+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.
+And, conversely, the framework also provides `ObjectPersistedEvent.Noop`; if `persistedLifecycleEvent` attribute is set to this class, then no event will be posted.
 
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_persistingLifecycleEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_persistingLifecycleEvent.adoc
index 928d55b..708e0ce 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_persistingLifecycleEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_persistingLifecycleEvent.adoc
@@ -17,7 +17,8 @@ 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 xref:../rgcms/rgcms.adoc#_rgcms_classes_roles_Timestampable[`Timestampable`] role interface.
 ====
 
-By default the event raised is `ObjectPersistingEvent.Default`. For example:
+By default the event raised is `ObjectPersistingEvent.Default`.
+For example:
 
 [source,java]
 ----
@@ -88,18 +89,14 @@ public class SomeSubscriber extends AbstractSubscriber {
 
 == 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 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.
+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.
+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.
+And, conversely, the framework also provides `ObjectPersistingEvent.Noop`; if `persistingLifecycleEvent` attribute is set to this class, then no event will be posted.
 
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_propertyDomainEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_propertyDomainEvent.adoc
new file mode 100644
index 0000000..4784dfb
--- /dev/null
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_propertyDomainEvent.adoc
@@ -0,0 +1,62 @@
+[[_rgant-DomainObject_propertyDomainEvent]]
+= `propertyDomainEvent()`
+: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 ag [...]
+:_basedir: ../../
+:_imagesdir: images/
+
+
+Whenever a property 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).
+Using xref:rgant.adoc#_rgant_Property_domainEvent[`@Property#domainEvent()`], the actual domain event class fired can be customised.
+the `@DomainObject#propertyDomainEvent()` attribute allows a common event domain class to be fired for all of the properties of the domain object.
+
+For example:
+
+[source,java]
+----
+@DomainObject(
+    propertyDomainEvent=ToDoItem.PropertyDomainEventDefault.class
+)
+public class ToDoItem {
+    public static class PropertyDomainEventDefault
+        extends org.apache.isis.applib.services.eventbus.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:
+
+[source,java]
+----
+@Mixin(method="act")
+public class ToDoItem_priority {
+    private final ToDoItem todoItem;
+    // constructor omitted
+
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(contributed = Contributed.AS_ASSOCIATION)
+    @Property
+    public Integer act() { ... }
+}
+----
+
+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()`.
+
+[NOTE]
+====
+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 `Object` as their first generic type.
+
+They should _also_ have `Object` for their second generic type, because the return type of the various properties of the domain object will or could be different.
+====
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_publishing.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_publishing.adoc
index 7f6833f..80f1054 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_publishing.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_publishing.adoc
@@ -43,17 +43,14 @@ public class InterestRate {
 
 == `publishingPayloadFactory()`
 
-The (optional) related `publishingPayloadFactory()` specifies the class to use to create the (payload of the) event to
-be published by the publishing factory.
+The (optional) related `publishingPayloadFactory()` specifies the class to use to create the (payload of the) event to be published by the publishing factory.
 
-Rather than simply broadcast that the object was changed, the payload factory allows a "fatter" payload to be
-instantiated that can eagerly push commonly-required information to all subscribers. For at least some subscribers
-this should avoid the necessity to query back for additional information.
+Rather than simply broadcast that the object was changed, the payload factory allows a "fatter" payload to be instantiated that can eagerly push commonly-required information to all subscribers.
+For at least some subscribers this should avoid the necessity to query back for additional information.
 
 
 [WARNING]
 ====
-Be aware that this attribute is only honoured by the (deprecated)
-xref:../rgsvc/rgsvc.adoc#_rgsvc_persistence-layer-spi_PublishingService[`PublishingService`], so should itself be considered as deprecated.  It
-is ignored by the replacement xref:../rgsvc/rgsvc.adoc#_rgsvc_persistence-layer-spi_PublisherService[`PublisherService`],
+Be aware that this attribute is only honoured by the (deprecated) xref:../rgsvc/rgsvc.adoc#_rgsvc_persistence-layer-spi_PublishingService[`PublishingService`], so should itself be considered as deprecated.
+It is ignored by the replacement xref:../rgsvc/rgsvc.adoc#_rgsvc_persistence-layer-spi_PublisherService[`PublisherService`],
 ====
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_removingLifecycleEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_removingLifecycleEvent.adoc
index e39e162..9f0aa43 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_removingLifecycleEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_removingLifecycleEvent.adoc
@@ -13,12 +13,12 @@ One possible application is to maintain a full-text search database using link:h
 
 [NOTE]
 ====
-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
-xref:../rgcms/rgcms.adoc#_rgcms_classes_roles_Timestampable[`Timestampable`] role interface.
+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 xref:../rgcms/rgcms.adoc#_rgcms_classes_roles_Timestampable[`Timestampable`] role interface.
 ====
 
-By default the event raised is `ObjectRemovingEvent.Default`. For example:
+By default the event raised is `ObjectRemovingEvent.Default`.
+For example:
 
 [source,java]
 ----
@@ -28,8 +28,8 @@ 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.
+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:
 
@@ -91,18 +91,14 @@ public class SomeSubscriber extends AbstractSubscriber {
 
 == 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 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.
+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.
+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.
+And, conversely, the framework also provides `ObjectRemovingEvent.Noop`; if `removingLifecycleEvent` attribute is set to this class, then no event will be posted.
 
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_updatedLifecycleEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_updatedLifecycleEvent.adoc
index 0271fe4..86b8c84 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_updatedLifecycleEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_updatedLifecycleEvent.adoc
@@ -5,18 +5,17 @@
 :_imagesdir: images/
 
 
-Whenever a (persistent) domain object has been modified and has been updated in the database, an "updated" lifecycle
-event is fired.
+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 xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] and can
-use the event to obtain a reference to the domain object.
+Subscribers subscribe through the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] and can use the event to obtain a reference to the domain object.
 
 [WARNING]
 ====
 The object should _not_ be modified during the updated callback.
 ====
 
-By default the event raised is `ObjectUpdatedEvent.Default`. For example:
+By default the event raised is `ObjectUpdatedEvent.Default`.
+For example:
 
 [source,java]
 ----
@@ -26,8 +25,8 @@ 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.
+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:
 
@@ -90,18 +89,14 @@ public class SomeSubscriber extends AbstractSubscriber {
 
 == 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 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.
+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.
+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.
+And, conversely, the framework also provides `ObjectUpdatedEvent.Noop`; if `updatedLifecycleEvent` attribute is set to this class, then no event will be posted.
 
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_updatingLifecycleEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_updatingLifecycleEvent.adoc
index d2b74dc..f4a46bd 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_updatingLifecycleEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_updatingLifecycleEvent.adoc
@@ -5,22 +5,21 @@
 :_imagesdir: images/
 
 
-Whenever a (persistent) domain object has been modified and is about to be updated to the database, an "updating"
-lifecycle event is fired.
+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 xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`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 link:https://lucene.apache.org/[Apache Lucene] or similar.
+Subscribers subscribe through the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`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 link:https://lucene.apache.org/[Apache Lucene] or similar.
 
 [NOTE]
 ====
-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
+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
 xref:../rgcms/rgcms.adoc#_rgcms_classes_roles_Timestampable[`Timestampable`] role interface.
 ====
 
-By default the event raised is `ObjectUpdatingEvent.Default`. For example:
+By default the event raised is `ObjectUpdatingEvent.Default`.
+For example:
 
 [source,java]
 ----
@@ -30,8 +29,8 @@ 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.
+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:
 
@@ -94,15 +93,12 @@ public class SomeSubscriber extends AbstractSubscriber {
 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.
+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.
+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.
+And, conversely, the framework also provides `ObjectUpdatingEvent.Noop`; if `updatingLifecycleEvent` attribute is set to this class, then no event will be posted.
 
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property_domainEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property_domainEvent.adoc
index ac53113..85e528c 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property_domainEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property_domainEvent.adoc
@@ -5,7 +5,8 @@
 :_imagesdir: images/
 
 
-Whenever a domain object (or list of domain objects) is to be rendered, the framework fires off multiple domain events for every property, collection and action of the domain object.  In the cases of the domain object's properties, the events that are fired are:
+Whenever a domain object (or list of domain objects) is to be rendered, the framework fires off multiple domain events for every property, collection and action of the domain object.
+In the cases of the domain object's properties, the events that are fired are:
 
 * hide phase: to check that the property is visible (has not been hidden)
 * disable phase: to check that the property is usable (has not been disabled)
@@ -15,7 +16,8 @@ Whenever a domain object (or list of domain objects) is to be rendered, the fram
 
 Subscribers subscribe through the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] using either link:https://github.com/google/guava[Guava] or link:http://www.axonframework.org/[Axon Framework] annotations and can influence each of these phases.
 
-By default the event raised is `PropertyDomainEvent.Default`. For example:
+By default the event raised is `PropertyDomainEvent.Default`.
+For example:
 
 [source,java]
 ----
@@ -26,10 +28,8 @@ public class ToDoItem {
 }
 ----
 
-The `domainEvent()` attribute allows a custom subclass to be emitted allowing more precise subscriptions (to those
-subclasses) to be defined instead.  This attribute is also supported for
- xref:../rgant/rgant.adoc#_rgant-Action_domainEvent[actions] and
- xref:../rgant/rgant.adoc#_rgant-Property_domainEvent[properties].
+The `domainEvent()` attribute allows a custom subclass to be emitted allowing more precise subscriptions (to those subclasses) to be defined instead.
+This attribute is also supported for xref:../rgant/rgant.adoc#_rgant-Action_domainEvent[actions] and xref:../rgant/rgant.adoc#_rgant-Property_domainEvent[properties].
 
 
 For example:
@@ -56,7 +56,8 @@ This substantially reduces the boilerplate in the subclasses because no explicit
 
 == Subscribers
 
-Subscribers (which must be domain services) subscribe using either the link:https://github.com/google/guava[Guava] API or (if the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] has been appropriately configured) using the link:http://www.axonframework.org/[Axon Framework] API.  The examples below use the Guava API.
+Subscribers (which must be domain services) subscribe using either the link:https://github.com/google/guava[Guava] API or (if the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] has been appropriately configured) using the link:http://www.axonframework.org/[Axon Framework] API.
+The examples below use the Guava API.
 
 Subscribers can be either coarse-grained (if they subscribe to the top-level event type):
 
@@ -77,17 +78,13 @@ or can be fine-grained (by subscribing to specific event subtypes):
 ----
 @DomainService(nature=NatureOfService.DOMAIN)
 public class SomeSubscriber extends AbstractSubscriber {
-    @com.google.common.eventbus.Subscribe
-    public void on(ToDoItem.DueByChangedEvent ev) {
+    @org.axonframework.eventhandling.annotation.EventHandler // if using axon
+    @com.google.common.eventbus.Subscribe                     public void on(ToDoItem.DueByChangedEvent ev) {
         ...
     }
 }
 ----
 
-[TIP]
-====
-If the AxonFramework is being used, replace `@com.google.common.eventbus.Subscribe` with `@org.axonframework.eventhandling.annotation.EventHandler`.
-====
 
 The subscriber's method is called (up to) 5 times:
 
@@ -97,7 +94,8 @@ The subscriber's method is called (up to) 5 times:
 * steps to perform prior to the property being modified
 * steps to perform after the property has been modified.
 
-The subscriber can distinguish these by calling `ev.getEventPhase()`. Thus the general form is:
+The subscriber can distinguish these by calling `ev.getEventPhase()`.
+Thus the general form is:
 
 [source,java]
 ----
@@ -123,25 +121,22 @@ public void on(PropertyDomainEvent ev) {
 }
 ----
 
-It is also possible to abort the transaction during the executing or executed phases by throwing an exception. If the exception is a subtype of `RecoverableException` then the exception will be rendered as a user-friendly warning (eg Growl/toast) rather than an error.
+It is also possible to abort the transaction during the executing or executed phases by throwing an exception.
+If the exception is a subtype of `RecoverableException` then the exception will be rendered as a user-friendly warning (eg Growl/toast) rather than an error.
 
 
 
 
 == Default, Doop and Noop events
 
-If the `domainEvent` attribute is not explicitly specified (is left as its default value, `PropertyDomainEvent.Default`),
-then the framework will, by default, post an event.
+If the `domainEvent` attribute is not explicitly specified (is left as its default value, `PropertyDomainEvent.Default`), then the framework will, by default, post an event.
 
-If this is not required, then the `isis.reflector.facet.propertyAnnotation.domainEvent.postForDefault`
-configuration property can be set to "false"; this will disable posting.
+If this is not required, then the `isis.reflector.facet.propertyAnnotation.domainEvent.postForDefault` configuration property can be set to "false"; this will disable posting.
 
 On the other hand, if the `domainEvent` has been explicitly specified to some subclass, then an event will be posted.
-The framework provides `PropertyDomainEvent.Doop` as such a subclass, so setting the `domainEvent` attribute to this class
-will ensure that the event to be posted, irrespective of the configuration property setting.
+The framework provides `PropertyDomainEvent.Doop` as such a subclass, so setting the `domainEvent` attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.
 
-And, conversely, the framework also provides `PropertyDomainEvent.Noop`; if `domainEvent` attribute is set to this class,
-then no event will be posted.
+And, conversely, the framework also provides `PropertyDomainEvent.Noop`; if `domainEvent` attribute is set to this class, then no event will be posted.
 
 
 
@@ -149,6 +144,6 @@ then no event will be posted.
 
 == Raising events programmatically
 
-Normally events are only raised for interactions through the UI. However, events can be raised programmatically by
-wrapping the target object using the xref:../rgsvc/rgsvc.adoc#_rgsvc_application-layer-api_WrapperFactory[`WrapperFactory`] service.
+Normally events are only raised for interactions through the UI.
+However, events can be raised programmatically by wrapping the target object using the xref:../rgsvc/rgsvc.adoc#_rgsvc_application-layer-api_WrapperFactory[`WrapperFactory`] service.
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/ugvw/_ugvw_hints-and-tips_highlight-current-row.adoc b/adocs/documentation/src/main/asciidoc/guides/ugvw/_ugvw_hints-and-tips_highlight-current-row.adoc
index 2853ad5..467ad24 100644
--- a/adocs/documentation/src/main/asciidoc/guides/ugvw/_ugvw_hints-and-tips_highlight-current-row.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/ugvw/_ugvw_hints-and-tips_highlight-current-row.adoc
@@ -36,8 +36,8 @@ Next, we define the domain service to act as the subscriber:
 public class HomePageViewModel ... {
     @DomainService(nature = NatureOfService.DOMAIN)
     public static class CssHighlighter extends AbstractSubscriber {
-        @EventHandler
-        @Subscribe
+        @org.axonframework.eventhandling.annotation.EventHandler // if using axon
+        @com.google.common.eventbus.Subscribe                    // if using guava
         public void on(DemoObject.CssClassUiEvent ev) {
             if(getContext() == null) { return; }
             if(ev.getSource() == getContext().getSelected()) {      // <1>


[isis] 01/04: ISIS-2043: adds domainEvents for @DomainObject

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit b9adf68d3cb78424c6c081a95d79911d5692d07c
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Nov 21 22:35:23 2018 +0100

    ISIS-2043: adds domainEvents for @DomainObject
---
 .../org/apache/isis/applib/annotation/Action.java  |  6 --
 .../apache/isis/applib/annotation/Collection.java  |  6 --
 .../isis/applib/annotation/DomainObject.java       | 81 ++++++++++++++++++++++
 .../apache/isis/applib/annotation/Property.java    |  6 --
 .../action/ActionAnnotationFacetFactory.java       | 22 ++++--
 .../CollectionAnnotationFacetFactory.java          | 27 ++++++--
 .../DomainObjectAnnotationFacetFactory.java        | 54 +++++++++++++++
 ...EventDefaultFacetForDomainObjectAnnotation.java | 54 +++++++++++++++
 ...EventDefaultFacetForDomainObjectAnnotation.java | 53 ++++++++++++++
 ...EventDefaultFacetForDomainObjectAnnotation.java | 53 ++++++++++++++
 .../property/PropertyAnnotationFacetFactory.java   | 65 ++++++++++++++---
 .../simple/subscribers/SimpleObjectListener.java   |  4 ++
 12 files changed, 395 insertions(+), 36 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
index d1178eb..4ac31f7 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
@@ -63,12 +63,6 @@ public @interface Action {
      * <p>
      * This subclass must provide a no-arg constructor; the fields are set reflectively.
      * </p>
-     *
-     * <p>
-     * Only domain services should be registered as subscribers; only domain services are guaranteed to be instantiated and
-     * resident in memory.  The typical implementation of a domain service subscriber is to identify the impacted entities,
-     * load them using a repository, and then to delegate to the event to them.
-     * </p>
      */
     Class<? extends ActionDomainEvent<?>> domainEvent() default ActionDomainEvent.Default.class;
 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Collection.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Collection.java
index 50b8f95..444881d 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Collection.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Collection.java
@@ -54,12 +54,6 @@ public @interface Collection {
      * </pre>
      *
      * <p>
-     * Only domain services should be registered as subscribers; only domain services are guaranteed to be instantiated and
-     * resident in memory.  The typical implementation of a domain service subscriber is to identify the impacted entities,
-     * load them using a repository, and then to delegate to the event to them.
-     * </p>
-     *
-     * <p>
      * This subclass must provide a no-arg constructor; the fields are set reflectively.
      * </p>
      */
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java
index 97a9512..abce74b 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java
@@ -24,6 +24,8 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.isis.applib.services.eventbus.ActionDomainEvent;
+import org.apache.isis.applib.services.eventbus.CollectionDomainEvent;
 import org.apache.isis.applib.services.eventbus.ObjectCreatedEvent;
 import org.apache.isis.applib.services.eventbus.ObjectLoadedEvent;
 import org.apache.isis.applib.services.eventbus.ObjectPersistedEvent;
@@ -31,6 +33,7 @@ import org.apache.isis.applib.services.eventbus.ObjectPersistingEvent;
 import org.apache.isis.applib.services.eventbus.ObjectRemovingEvent;
 import org.apache.isis.applib.services.eventbus.ObjectUpdatedEvent;
 import org.apache.isis.applib.services.eventbus.ObjectUpdatingEvent;
+import org.apache.isis.applib.services.eventbus.PropertyDomainEvent;
 import org.apache.isis.applib.services.publish.PublisherService;
 
 /**
@@ -246,4 +249,82 @@ public @interface DomainObject {
      * </p>
      */
     Class<? extends ObjectRemovingEvent<?>> removingLifecycleEvent() default ObjectRemovingEvent.Default.class;
+
+
+    /**
+     * Indicates that an invocation of <i>any</i> action of the domain object (that do not themselves specify their own
+     * <tt>&#64;Action(domainEvent=...)</tt> should be posted to the
+     * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using the specified custom
+     * (subclass of) {@link org.apache.isis.applib.services.eventbus.ActionDomainEvent}.
+     *
+     * <p>For example:
+     * </p>
+     *
+     * <pre>
+     * &#64;DomainObject(actionDomainEvent=SomeObject.GenericActionDomainEvent.class)
+     * public class SomeObject{
+     *     public static class GenericActionDomainEvent extends ActionDomainEvent&lt;Object&gt; { ... }
+     *
+     *     public void changeStartDate(final Date startDate) { ...}
+     *     ...
+     * }
+     * </pre>
+     *
+     * <p>
+     *     This will result in all actions as a more specific type to use) to emit this event.
+     * </p>
+     * <p>
+     * This subclass must provide a no-arg constructor; the fields are set reflectively.
+     * It must also use <tt>Object</tt> as its generic type.  This is to allow mixins to also emit the same event.
+     * </p>
+     */
+    Class<? extends ActionDomainEvent<?>> actionDomainEvent() default ActionDomainEvent.Default.class;
+
+    /**
+     * Indicates that changes to <i>any</i> property of the domain object (that do not themselves specify their own
+     * <tt>&#64;Property(domainEvent=...)</tt> should be posted to the
+     * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using the specified custom
+     * (subclass of) {@link org.apache.isis.applib.services.eventbus.PropertyDomainEvent}.
+     *
+     * <p>For example:
+     * </p>
+     *
+     * <pre>
+     * &#64;DomainObject(propertyDomainEvent=SomeObject.GenericPropertyDomainEvent.class)
+     * public class SomeObject{
+     *
+     *    public LocalDate getStartDate() { ...}
+     * }
+     * </pre>
+     *
+     * <p>
+     * This subclass must provide a no-arg constructor; the fields are set reflectively.
+     * It must also use <tt>Object</tt> as its generic type.  This is to allow mixins to also emit the same event.
+     * </p>
+     */
+    Class<? extends PropertyDomainEvent<?,?>> propertyDomainEvent() default PropertyDomainEvent.Default.class;
+
+    /**
+     * Indicates that changes to <i>any</i> collection of the domain object (that do not themselves specify their own
+     * <tt>&#64;Collection(domainEvent=...)</tt>  should be posted to the
+     * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using a custom (subclass of)
+     * {@link org.apache.isis.applib.services.eventbus.CollectionDomainEvent}.
+     *
+     * <p>For example:
+     * </p>
+     * <pre>
+     * &#64;DomainObject(collectionDomainEvent=Order.GenericCollectionDomainEvent.class)
+     * public class Order {
+     *
+     *   public SortedSet&lt;OrderLine&gt; getLineItems() { ...}
+     * }
+     * </pre>
+     *
+     * <p>
+     * This subclass must provide a no-arg constructor; the fields are set reflectively.
+     * It must also use <tt>Object</tt> as its generic type.  This is to allow mixins to also emit the same event.
+     * </p>
+     */
+    Class<? extends CollectionDomainEvent<?,?>> collectionDomainEvent() default CollectionDomainEvent.Default.class;
+
 }
\ No newline at end of file
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
index ed91f9e..7d248d5 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
@@ -61,12 +61,6 @@ public @interface Property {
      * </pre>
      *
      * <p>
-     * Only domain services should be registered as subscribers; only domain services are guaranteed to be instantiated
-     * and resident in memory.  The typical implementation of a domain service subscriber is to identify the impacted
-     * entities, load them using a repository, and then to delegate to the event to them.
-     * </p>
-     *
-     * <p>
      * This subclass must provide a no-arg constructor; the fields are set reflectively.
      * </p>
      */
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
index b26586d..255f7c0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
@@ -85,6 +85,7 @@ import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFa
 import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
 import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
 import org.apache.isis.core.metamodel.facets.members.order.annotprop.MemberOrderFacetForActionAnnotation;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.CollectionUtils;
@@ -172,19 +173,19 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract
 
             // search for @ActionInteraction(value=...)
             if(actionInteraction != null) {
-                actionDomainEventType = actionInteraction.value();
+                actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, actionInteraction.value());
                 actionDomainEventFacet = new ActionDomainEventFacetForActionInteractionAnnotation(
                         actionDomainEventType, servicesInjector, getSpecificationLoader(), holder);
             } else
             // search for @Action(domainEvent=...)
-            if(action != null && action.domainEvent() != null) {
-                actionDomainEventType = action.domainEvent();
+            if(action != null) {
+                actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, action.domainEvent());
                 actionDomainEventFacet = new ActionDomainEventFacetForActionAnnotation(
                         actionDomainEventType, servicesInjector, getSpecificationLoader(), holder);
             } else
             // else use default event type
             {
-                actionDomainEventType = ActionDomainEvent.Default.class;
+                actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, ActionDomainEvent.Default.class);
                 actionDomainEventFacet = new ActionDomainEventFacetDefault(
                         actionDomainEventType, servicesInjector, getSpecificationLoader(), holder);
             }
@@ -238,6 +239,19 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract
         }
     }
 
+    private static Class<? extends ActionDomainEvent<?>> defaultFromDomainObjectIfRequired(
+            final ObjectSpecification typeSpec,
+            final Class<? extends ActionDomainEvent<?>> actionDomainEventType) {
+        if (actionDomainEventType == ActionDomainEvent.Default.class) {
+            final ActionDomainEventDefaultFacetForDomainObjectAnnotation typeFromDomainObject =
+                    typeSpec.getFacet(ActionDomainEventDefaultFacetForDomainObjectAnnotation.class);
+            if (typeFromDomainObject != null) {
+                return typeFromDomainObject.getEventType();
+            }
+        }
+        return actionDomainEventType;
+    }
+
     void processHidden(final ProcessMethodContext processMethodContext) {
         final Method method = processMethodContext.getMethod();
         final FacetHolder holder = processMethodContext.getFacetHolder();
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
index 55e1fc1..1832bda 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
@@ -70,9 +70,11 @@ import org.apache.isis.core.metamodel.facets.collections.collection.typeof.TypeO
 import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToFacet;
 import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet;
 import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.CollectionDomainEventDefaultFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.facets.propcoll.notpersisted.NotPersistedFacet;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.CollectionUtils;
 import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite;
 import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForDeprecatedAnnotation;
@@ -105,6 +107,9 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple
     void processModify(final ProcessMethodContext processMethodContext) {
 
         final Method method = processMethodContext.getMethod();
+
+        final Class<?> cls = processMethodContext.getCls();
+        final ObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(cls);
         final FacetHolder holder = processMethodContext.getFacetHolder();
 
         final PropertyOrCollectionAccessorFacet getterFacet = holder.getFacet(PropertyOrCollectionAccessorFacet.class);
@@ -145,21 +150,21 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple
 
         // search for @CollectionInteraction(value=...)
         if(collectionInteraction != null) {
-            collectionDomainEventType = collectionInteraction.value();
+            collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, collectionInteraction.value());
             collectionDomainEventFacet = collectionInteractionValidator.flagIfPresent(
                     new CollectionDomainEventFacetForCollectionInteractionAnnotation(
                         collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder), processMethodContext);
         } else
         // search for @Collection(domainEvent=...)
-        if(collection != null && collection.domainEvent() != null) {
-            collectionDomainEventType = collection.domainEvent();
+        if(collection != null) {
+            collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, collection.domainEvent());
             collectionDomainEventFacet = new CollectionDomainEventFacetForCollectionAnnotation(
                     collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder);
 
         } else
         // else use default event type
         {
-            collectionDomainEventType = CollectionDomainEvent.Default.class;
+            collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, CollectionDomainEvent.Default.class);
             collectionDomainEventFacet = new CollectionDomainEventFacetDefault(
                     collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder);
         }
@@ -237,6 +242,20 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple
 
     }
 
+    private static Class<? extends CollectionDomainEvent<?,?>> defaultFromDomainObjectIfRequired(
+            final ObjectSpecification typeSpec,
+            final Class<? extends CollectionDomainEvent<?,?>> collectionDomainEventType) {
+        if (collectionDomainEventType == CollectionDomainEvent.Default.class) {
+            final CollectionDomainEventDefaultFacetForDomainObjectAnnotation typeFromDomainObject =
+                    typeSpec.getFacet(CollectionDomainEventDefaultFacetForDomainObjectAnnotation.class);
+            if (typeFromDomainObject != null) {
+                return typeFromDomainObject.getEventType();
+            }
+        }
+        return collectionDomainEventType;
+    }
+
+
     void processHidden(final ProcessMethodContext processMethodContext) {
         final Method method = processMethodContext.getMethod();
         final FacetHolder holder = processMethodContext.getFacetHolder();
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
index 2a35bba..35808b6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
@@ -34,6 +34,8 @@ import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.ObjectType;
 import org.apache.isis.applib.annotation.PublishedObject;
 import org.apache.isis.applib.services.HasTransactionId;
+import org.apache.isis.applib.services.eventbus.ActionDomainEvent;
+import org.apache.isis.applib.services.eventbus.CollectionDomainEvent;
 import org.apache.isis.applib.services.eventbus.ObjectCreatedEvent;
 import org.apache.isis.applib.services.eventbus.ObjectLoadedEvent;
 import org.apache.isis.applib.services.eventbus.ObjectPersistedEvent;
@@ -41,6 +43,7 @@ import org.apache.isis.applib.services.eventbus.ObjectPersistingEvent;
 import org.apache.isis.applib.services.eventbus.ObjectRemovingEvent;
 import org.apache.isis.applib.services.eventbus.ObjectUpdatedEvent;
 import org.apache.isis.applib.services.eventbus.ObjectUpdatingEvent;
+import org.apache.isis.applib.services.eventbus.PropertyDomainEvent;
 import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.commons.lang.Nullable;
 import org.apache.isis.core.metamodel.facetapi.Facet;
@@ -68,6 +71,9 @@ import org.apache.isis.core.metamodel.facets.object.domainobject.autocomplete.Au
 import org.apache.isis.core.metamodel.facets.object.domainobject.autocomplete.AutoCompleteFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.object.domainobject.choices.ChoicesFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.object.domainobject.choices.ChoicesFacetFromBoundedAnnotation;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.CollectionDomainEventDefaultFacetForDomainObjectAnnotation;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.PropertyDomainEventDefaultFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.object.domainobject.editing.ImmutableFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.object.domainobject.objectspecid.ObjectSpecIdFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.object.domainobject.objectspecid.ObjectSpecIdFacetForJdoPersistenceCapableAnnotation;
@@ -124,6 +130,7 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract
         processObjectType(processClassContext);
         processNature(processClassContext);
         processLifecycleEvents(processClassContext);
+        processDomainEvents(processClassContext);
 
     }
 
@@ -391,7 +398,21 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract
         processLifecycleEventRemoving(domainObject, holder);
         processLifecycleEventUpdated(domainObject, holder);
         processLifecycleEventUpdating(domainObject, holder);
+    }
+
 
+    private void processDomainEvents(final ProcessClassContext processClassContext) {
+
+        final Class<?> cls = processClassContext.getCls();
+        final DomainObject domainObject = Annotations.getAnnotation(cls, DomainObject.class);
+        if(domainObject == null) {
+            return;
+        }
+        final FacetHolder holder = processClassContext.getFacetHolder();
+
+        processDomainEventAction(domainObject, holder);
+        processDomainEventProperty(domainObject, holder);
+        processDomainEventCollection(domainObject, holder);
     }
 
     private void processLifecycleEventCreated(final DomainObject domainObject, final FacetHolder holder) {
@@ -513,6 +534,39 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract
         }
     }
 
+    private void processDomainEventAction(final DomainObject domainObject, final FacetHolder holder) {
+        final Class<? extends ActionDomainEvent<?>> domainEvent = domainObject.actionDomainEvent();
+
+        if(domainEvent != ActionDomainEvent.Default.class) {
+            final ActionDomainEventDefaultFacetForDomainObjectAnnotation facet =
+                    new ActionDomainEventDefaultFacetForDomainObjectAnnotation(
+                        holder, domainEvent, getSpecificationLoader());
+            FacetUtil.addFacet(facet);
+        }
+    }
+
+    private void processDomainEventProperty(final DomainObject domainObject, final FacetHolder holder) {
+        final Class<? extends PropertyDomainEvent<?,?>> domainEvent = domainObject.propertyDomainEvent();
+
+        if(domainEvent != PropertyDomainEvent.Default.class) {
+            final PropertyDomainEventDefaultFacetForDomainObjectAnnotation facet =
+                    new PropertyDomainEventDefaultFacetForDomainObjectAnnotation(
+                        holder, domainEvent, getSpecificationLoader());
+            FacetUtil.addFacet(facet);
+        }
+    }
+
+    private void processDomainEventCollection(final DomainObject domainObject, final FacetHolder holder) {
+        final Class<? extends CollectionDomainEvent<?,?>> domainEvent = domainObject.collectionDomainEvent();
+
+        if(domainEvent != CollectionDomainEvent.Default.class) {
+            final CollectionDomainEventDefaultFacetForDomainObjectAnnotation facet =
+                    new CollectionDomainEventDefaultFacetForDomainObjectAnnotation(
+                        holder, domainEvent, getSpecificationLoader());
+            FacetUtil.addFacet(facet);
+        }
+    }
+
     // //////////////////////////////////////
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java
new file mode 100644
index 0000000..2bb8aef
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java
@@ -0,0 +1,54 @@
+/*
+ *  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.
+ */
+
+package org.apache.isis.core.metamodel.facets.object.domainobject.domainevents;
+
+import org.apache.isis.applib.services.eventbus.ActionDomainEvent;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract;
+import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+
+/**
+ * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use
+ * for any actions as a fallback/default.
+ */
+public class ActionDomainEventDefaultFacetForDomainObjectAnnotation
+                    extends SingleClassValueFacetAbstract  {
+
+
+    private final Class<? extends ActionDomainEvent<?>> eventType;
+    public Class<? extends ActionDomainEvent<?>> getEventType() {
+        return eventType;
+    }
+
+    static Class<? extends Facet> type() {
+        return ActionDomainEventDefaultFacetForDomainObjectAnnotation.class;
+    }
+
+    public ActionDomainEventDefaultFacetForDomainObjectAnnotation(
+            final FacetHolder holder,
+            final Class<? extends ActionDomainEvent<?>> value,
+            final SpecificationLoader specificationLoader) {
+        super(type(), holder, value, specificationLoader);
+        this.eventType = value;
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java
new file mode 100644
index 0000000..627b09d
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java
@@ -0,0 +1,53 @@
+/*
+ *  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.
+ */
+
+package org.apache.isis.core.metamodel.facets.object.domainobject.domainevents;
+
+import org.apache.isis.applib.services.eventbus.CollectionDomainEvent;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract;
+import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+
+/**
+ * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use
+ * for any actions as a fallback/default.
+ */
+public class CollectionDomainEventDefaultFacetForDomainObjectAnnotation
+                    extends SingleClassValueFacetAbstract  {
+
+    private final Class<? extends CollectionDomainEvent<?, ?>> eventType;
+    public Class<? extends CollectionDomainEvent<?, ?>> getEventType() {
+        return eventType;
+    }
+
+    static Class<? extends Facet> type() {
+        return CollectionDomainEventDefaultFacetForDomainObjectAnnotation.class;
+    }
+
+    public CollectionDomainEventDefaultFacetForDomainObjectAnnotation(
+            final FacetHolder holder,
+            final Class<? extends CollectionDomainEvent<?,?>> value,
+            final SpecificationLoader specificationLoader) {
+        super(type(), holder, value, specificationLoader);
+        this.eventType = value;
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java
new file mode 100644
index 0000000..4688bbd
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java
@@ -0,0 +1,53 @@
+/*
+ *  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.
+ */
+
+package org.apache.isis.core.metamodel.facets.object.domainobject.domainevents;
+
+import org.apache.isis.applib.services.eventbus.PropertyDomainEvent;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract;
+import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+
+/**
+ * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use
+ * for any actions as a fallback/default.
+ */
+public class PropertyDomainEventDefaultFacetForDomainObjectAnnotation
+                    extends SingleClassValueFacetAbstract  {
+
+    private final Class<? extends PropertyDomainEvent<?, ?>> eventType;
+    public Class<? extends PropertyDomainEvent<?, ?>> getEventType() {
+        return eventType;
+    }
+
+    static Class<? extends Facet> type() {
+        return PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class;
+    }
+
+    public PropertyDomainEventDefaultFacetForDomainObjectAnnotation(
+            final FacetHolder holder,
+            final Class<? extends PropertyDomainEvent<?,?>> value,
+            final SpecificationLoader specificationLoader) {
+        super(type(), holder, value, specificationLoader);
+        this.eventType = value;
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
index 7f919f7..47db77a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
@@ -19,18 +19,37 @@
 
 package org.apache.isis.core.metamodel.facets.properties.property;
 
-import org.apache.isis.applib.annotation.*;
+import java.lang.reflect.Method;
+
+import javax.annotation.Nullable;
+
+import org.apache.isis.applib.annotation.Disabled;
+import org.apache.isis.applib.annotation.Hidden;
+import org.apache.isis.applib.annotation.Mandatory;
+import org.apache.isis.applib.annotation.MaxLength;
+import org.apache.isis.applib.annotation.MustSatisfy;
+import org.apache.isis.applib.annotation.NotPersisted;
+import org.apache.isis.applib.annotation.Optional;
+import org.apache.isis.applib.annotation.PostsPropertyChangedEvent;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyInteraction;
+import org.apache.isis.applib.annotation.RegEx;
 import org.apache.isis.applib.services.HasTransactionId;
 import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
 import org.apache.isis.applib.services.eventbus.PropertyDomainEvent;
 import org.apache.isis.core.commons.config.IsisConfiguration;
-import org.apache.isis.core.metamodel.facetapi.*;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelValidatorRefiner;
 import org.apache.isis.core.metamodel.facets.Annotations;
 import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
 import org.apache.isis.core.metamodel.facets.actions.command.CommandFacet;
 import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
 import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.PropertyDomainEventDefaultFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.objectvalue.fileaccept.FileAcceptFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
@@ -50,7 +69,18 @@ import org.apache.isis.core.metamodel.facets.properties.property.mandatory.Manda
 import org.apache.isis.core.metamodel.facets.properties.property.mandatory.MandatoryFacetInvertedByOptionalAnnotationOnProperty;
 import org.apache.isis.core.metamodel.facets.properties.property.maxlength.MaxLengthFacetForMaxLengthAnnotationOnProperty;
 import org.apache.isis.core.metamodel.facets.properties.property.maxlength.MaxLengthFacetForPropertyAnnotation;
-import org.apache.isis.core.metamodel.facets.properties.property.modify.*;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyClearFacetForDomainEventFromDefault;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyClearFacetForDomainEventFromPropertyAnnotation;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyClearFacetForDomainEventFromPropertyInteractionAnnotation;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyClearFacetForPostsPropertyChangedEventAnnotation;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetAbstract;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetDefault;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetForPropertyAnnotation;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetForPropertyInteractionAnnotation;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterFacetForDomainEventFromDefault;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterFacetForDomainEventFromPropertyAnnotation;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterFacetForDomainEventFromPropertyInteractionAnnotation;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterFacetForPostsPropertyChangedEventAnnotation;
 import org.apache.isis.core.metamodel.facets.properties.property.mustsatisfy.MustSatisfySpecificationFacetForMustSatisfyAnnotationOnProperty;
 import org.apache.isis.core.metamodel.facets.properties.property.mustsatisfy.MustSatisfySpecificationFacetForPropertyAnnotation;
 import org.apache.isis.core.metamodel.facets.properties.property.notpersisted.NotPersistedFacetForNotPersistedAnnotationOnProperty;
@@ -62,14 +92,12 @@ import org.apache.isis.core.metamodel.facets.properties.publish.PublishedPropert
 import org.apache.isis.core.metamodel.facets.properties.update.clear.PropertyClearFacet;
 import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacet;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite;
 import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForConflictingOptionality;
 import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForDeprecatedAnnotation;
 import org.apache.isis.core.metamodel.util.EventUtil;
 
-import javax.annotation.Nullable;
-import java.lang.reflect.Method;
-
 public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract implements MetaModelValidatorRefiner {
 
     private final MetaModelValidatorForDeprecatedAnnotation postsPropertyChangedEventValidator = new MetaModelValidatorForDeprecatedAnnotation(PostsPropertyChangedEvent.class);
@@ -108,6 +136,9 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme
     void processModify(final ProcessMethodContext processMethodContext) {
 
         final Method method = processMethodContext.getMethod();
+
+        final Class<?> cls = processMethodContext.getCls();
+        final ObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(cls);
         final FacetedMethod holder = processMethodContext.getFacetHolder();
 
         final PropertyOrCollectionAccessorFacet getterFacet = holder.getFacet(PropertyOrCollectionAccessorFacet.class);
@@ -136,21 +167,21 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme
 
         // search for @PropertyInteraction(value=...)
         if(propertyInteraction != null) {
-            propertyDomainEventType = propertyInteraction.value();
+            propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, propertyInteraction.value());
             propertyDomainEventFacet = propertyInteractionValidator.flagIfPresent(
                     new PropertyDomainEventFacetForPropertyInteractionAnnotation(
                         propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder), processMethodContext);
         } else
         // search for @Property(domainEvent=...)
-        if(property != null && property.domainEvent() != null) {
-            propertyDomainEventType = property.domainEvent();
+        if(property != null) {
+            propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, property.domainEvent());
             propertyDomainEventFacet = new PropertyDomainEventFacetForPropertyAnnotation(
                     propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder);
 
         } else
         // else use default event type
         {
-            propertyDomainEventType = PropertyDomainEvent.Default.class;
+            propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, PropertyDomainEvent.Default.class);
             propertyDomainEventFacet = new PropertyDomainEventFacetDefault(
                     propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder);
         }
@@ -228,6 +259,20 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme
         }
     }
 
+    private static Class<? extends PropertyDomainEvent<?,?>> defaultFromDomainObjectIfRequired(
+            final ObjectSpecification typeSpec,
+            final Class<? extends PropertyDomainEvent<?,?>> propertyDomainEventType) {
+        if (propertyDomainEventType == PropertyDomainEvent.Default.class) {
+            final PropertyDomainEventDefaultFacetForDomainObjectAnnotation typeFromDomainObject =
+                    typeSpec.getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class);
+            if (typeFromDomainObject != null) {
+                return typeFromDomainObject.getEventType();
+            }
+        }
+        return propertyDomainEventType;
+    }
+
+
 
     void processHidden(final ProcessMethodContext processMethodContext) {
         final Method method = processMethodContext.getMethod();
diff --git a/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/subscribers/SimpleObjectListener.java b/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/subscribers/SimpleObjectListener.java
new file mode 100644
index 0000000..c377c55
--- /dev/null
+++ b/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/subscribers/SimpleObjectListener.java
@@ -0,0 +1,4 @@
+package domainapp.modules.simple.subscribers;
+
+public class SimpleObjectListener {
+}


[isis] 03/04: ISIS-2043: adds support for @DomainObject(xxxDomainEvent=...) for mixins

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit c568147d62ed6f5519e61051cdc26c6ce4ff83ad
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Nov 22 13:37:08 2018 +0100

    ISIS-2043: adds support for @DomainObject(xxxDomainEvent=...) for mixins
    
    whereby will honour the domain event specified if not otherwise annotated.
    
    In fact, this contains a few other fixes:
    
    * previously, although mixin actions did emit domain events for all phases (hide/disable/validate/executing/executed), mixin properties and mixin collections emitted none.  Now this is fixed so that the emit domain events for the first two phases (hide/disable).  It doesn't make sense to emit them for the remainder because mixin props/colls are always derived (are read-only queries under the covers).
    
    * also, getMixedIn() now promoted from ActionDomainEvent up to AbstractDomainEvent
    
    * also, added similar getSubject() (a simple wrapper around getSource() and getMixedIn()) also in AbstractDomainEvent.
---
 .../services/eventbus/AbstractDomainEvent.java     |  35 ++++++
 .../services/eventbus/ActionDomainEvent.java       |  32 -----
 .../core/metamodel/facets/DomainEventHelper.java   |  13 ++
 .../facets/SingleClassValueFacetAbstract.java      |   2 +-
 .../invocation/ActionDomainEventFacetAbstract.java |  28 +++--
 .../CollectionAnnotationFacetFactory.java          |   9 +-
 ...ectionAddToFacetForDomainEventFromAbstract.java |   7 +-
 .../modify/CollectionDomainEventFacetAbstract.java |  42 ++++---
 ...nRemoveFromFacetForDomainEventFromAbstract.java |   5 +-
 .../property/PropertyAnnotationFacetFactory.java   |   6 +-
 .../modify/PropertyDomainEventFacetAbstract.java   |  80 +++++++-----
 .../modify/PropertyDomainEventFacetDefault.java    |   8 +-
 ...acetForPostsPropertyChangedEventAnnotation.java |   8 +-
 ...pertyDomainEventFacetForPropertyAnnotation.java |   8 +-
 ...EventFacetForPropertyInteractionAnnotation.java |   8 +-
 ...tySetterOrClearFacetForDomainEventAbstract.java |  10 +-
 .../param/DeriveFacetsPostProcessor.java           | 137 +++++++++++++++++++++
 17 files changed, 334 insertions(+), 104 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/AbstractDomainEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/AbstractDomainEvent.java
index bba4e98..4e906ed 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/AbstractDomainEvent.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/AbstractDomainEvent.java
@@ -52,6 +52,41 @@ public abstract class AbstractDomainEvent<S> extends java.util.EventObject {
         return source != null ? source : new Object();
     }
 
+
+    // region > mixedIn
+    private Object mixedIn;
+
+    /**
+     * Populated only for mixins; holds the underlying domain object that the mixin contributes to.
+     */
+    public Object getMixedIn() {
+        return mixedIn;
+    }
+    /**
+     * Not API - set by the framework.
+     */
+    public void setMixedIn(final Object mixedIn) {
+        this.mixedIn = mixedIn;
+    }
+    // endregion
+
+
+
+    // region > subject
+
+    /**
+     * The subject of the event, which will be either the {@link #getSource() source} for a regular action, or the
+     * {@link #getMixedIn() mixed-in} domain object for a mixin.
+     */
+    public Object getSubject() {
+        final Object mixedIn = getMixedIn();
+        return mixedIn != null ? mixedIn : getSource();
+    }
+
+
+    //endregion
+
+
     //region > Phase
 
     public enum Phase {
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java
index 1d97fc2..385a43e 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java
@@ -193,38 +193,6 @@ public abstract class ActionDomainEvent<S> extends AbstractInteractionEvent<S> {
     //endregion
 
 
-    // region > mixedIn
-    private Object mixedIn;
-
-    /**
-     * Populated only for mixins; holds the underlying domain object that the mixin contributes to.
-     */
-    public Object getMixedIn() {
-        return mixedIn;
-    }
-    /**
-     * Not API - set by the framework.
-     */
-    public void setMixedIn(final Object mixedIn) {
-        this.mixedIn = mixedIn;
-    }
-    // endregion
-
-
-
-    // region > subject
-
-    /**
-     * The subject of the event, which will be either the {@link #getSource() source} for a regular action, or the
-     * {@link #getMixedIn() mixed-in} domain object for a mixin.
-     */
-    public Object getSubject() {
-        final Object mixedIn = getMixedIn();
-        return mixedIn != null ? mixedIn : getSource();
-    }
-
-
-    //endregion
 
     //region > arguments
     private List<Object> arguments;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java
index 772844c..f881113 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java
@@ -192,6 +192,7 @@ public class DomainEventHelper {
             final PropertyDomainEvent<?, ?> existingEvent,
             final IdentifiedHolder identified,
             final ObjectAdapter targetAdapter,
+            final ObjectAdapter mixedInAdapter,
             final Object oldValue,
             final Object newValue) {
 
@@ -206,6 +207,12 @@ public class DomainEventHelper {
             } else {
                 // all other phases, create a new event
                 event = newPropertyDomainEvent(eventType, identifier, source, oldValue, newValue);
+
+                // copy over if have
+                if(mixedInAdapter != null ) {
+                    event.setMixedIn(mixedInAdapter.getObject());
+                }
+
             }
 
             event.setEventPhase(phase);
@@ -283,6 +290,7 @@ public class DomainEventHelper {
             final CollectionDomainEvent<?, ?> existingEvent,
             final IdentifiedHolder identified,
             final ObjectAdapter targetAdapter,
+            final ObjectAdapter mixedInAdapter,
             final CollectionDomainEvent.Of of,
             final Object reference) {
         try {
@@ -295,6 +303,11 @@ public class DomainEventHelper {
                 final Object source = ObjectAdapter.Util.unwrap(targetAdapter);
                 final Identifier identifier = identified.getIdentifier();
                 event = newCollectionDomainEvent(eventType, phase, identifier, source, of, reference);
+
+                // copy over if have
+                if(mixedInAdapter != null ) {
+                    event.setMixedIn(mixedInAdapter.getObject());
+                }
             }
 
             event.setEventPhase(phase);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleClassValueFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleClassValueFacetAbstract.java
index d057fcc..2c71129 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleClassValueFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/SingleClassValueFacetAbstract.java
@@ -62,7 +62,7 @@ public abstract class SingleClassValueFacetAbstract extends FacetAbstract implem
 
     @Override public void appendAttributesTo(final Map<String, Object> attributeMap) {
         super.appendAttributesTo(attributeMap);
-        attributeMap.put("value", value);
+        attributeMap.put("value", value());
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionDomainEventFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionDomainEventFacetAbstract.java
index 4399084..0763fb9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionDomainEventFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionDomainEventFacetAbstract.java
@@ -48,6 +48,7 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 public abstract class ActionDomainEventFacetAbstract
         extends SingleClassValueFacetAbstract implements ActionDomainEventFacet {
 
+    private Class<? extends ActionDomainEvent<?>> eventType;
     private final TranslationService translationService;
     private final String translationContext;
 
@@ -63,6 +64,7 @@ public abstract class ActionDomainEventFacetAbstract
             final ServicesInjector servicesInjector,
             final SpecificationLoader specificationLoader) {
         super(type(), holder, eventType, specificationLoader);
+        this.eventType = eventType;
 
         this.translationService = servicesInjector.lookupService(TranslationService.class);
         // sadness: same as in TranslationFactory
@@ -71,6 +73,23 @@ public abstract class ActionDomainEventFacetAbstract
         domainEventHelper = new DomainEventHelper(servicesInjector);
     }
 
+    @Override
+    public Class<?> value() {
+        return eventType;
+    }
+
+    protected Class eventType() {
+        return eventType;
+    }
+
+    public Class<? extends ActionDomainEvent<?>> getEventType() {
+        return eventType;
+    }
+    public void setEventType(final Class<? extends ActionDomainEvent<?>> eventType) {
+        this.eventType = eventType;
+    }
+
+
 
     @Override
     public String hides(final VisibilityContext<? extends VisibilityEvent> ic) {
@@ -147,13 +166,4 @@ public abstract class ActionDomainEventFacetAbstract
         return null;
     }
 
-    protected Class eventType() {
-        return value();
-    }
-
-    public Class<? extends ActionDomainEvent<?>> getEventType() {
-        //noinspection unchecked
-        return eventType();
-    }
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
index 1832bda..1372406 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
@@ -117,6 +117,11 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple
             return;
         }
 
+
+
+        // following only runs for regular collections, not for mixins.
+        // those are tackled in the post-processing, when more of the metamodel is available to us
+
         //
         // Set up CollectionDomainEventFacet, which will act as the hiding/disabling/validating advisor
         //
@@ -242,7 +247,7 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple
 
     }
 
-    private static Class<? extends CollectionDomainEvent<?,?>> defaultFromDomainObjectIfRequired(
+    public static Class<? extends CollectionDomainEvent<?,?>> defaultFromDomainObjectIfRequired(
             final ObjectSpecification typeSpec,
             final Class<? extends CollectionDomainEvent<?,?>> collectionDomainEventType) {
         if (collectionDomainEventType == CollectionDomainEvent.Default.class) {
@@ -382,6 +387,8 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple
     }
 
 
+
+
     // //////////////////////////////////////
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionAddToFacetForDomainEventFromAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionAddToFacetForDomainEventFromAbstract.java
index 1b67a62..fe3927b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionAddToFacetForDomainEventFromAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionAddToFacetForDomainEventFromAbstract.java
@@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.facets.collections.collection.modify;
 
 import java.util.Map;
 import java.util.Set;
+
 import org.apache.isis.applib.services.eventbus.AbstractDomainEvent;
 import org.apache.isis.applib.services.eventbus.CollectionDomainEvent;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
@@ -33,7 +34,6 @@ import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToF
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 
-
 public abstract class CollectionAddToFacetForDomainEventFromAbstract
     extends SingleValueFacetAbstract<Class<? extends CollectionDomainEvent<?,?>>>
     implements CollectionAddToFacet {
@@ -86,13 +86,14 @@ public abstract class CollectionAddToFacetForDomainEventFromAbstract
 
         // either doesn't contain object, or doesn't have set semantics, so
         // execute the add wrapped between the executing and executed events ...
+        final ObjectAdapter mixedInAdapter = null;
 
         // ... post the executing event
         final CollectionDomainEvent<?, ?> event =
                 domainEventHelper.postEventForCollection(
                         AbstractDomainEvent.Phase.EXECUTING,
                         eventType(), null,
-                        getIdentified(), targetAdapter,
+                        getIdentified(), targetAdapter, mixedInAdapter,
                         CollectionDomainEvent.Of.ADD_TO,
                         referencedObject);
 
@@ -103,7 +104,7 @@ public abstract class CollectionAddToFacetForDomainEventFromAbstract
         domainEventHelper.postEventForCollection(
                 AbstractDomainEvent.Phase.EXECUTED,
                 value(), verify(event),
-                getIdentified(), targetAdapter,
+                getIdentified(), targetAdapter, mixedInAdapter,
                 CollectionDomainEvent.Of.ADD_TO,
                 referencedObject);
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionDomainEventFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionDomainEventFacetAbstract.java
index 75c227a..6569e78 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionDomainEventFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionDomainEventFacetAbstract.java
@@ -50,6 +50,7 @@ public abstract class CollectionDomainEventFacetAbstract extends SingleClassValu
             final ServicesInjector servicesInjector,
             final SpecificationLoader specificationLoader) {
         super(CollectionDomainEventFacet.class, holder, eventType, specificationLoader);
+        this.eventType = eventType;
 
         this.translationService = servicesInjector.lookupService(TranslationService.class);
         // sadness: same as in TranslationFactory
@@ -58,6 +59,25 @@ public abstract class CollectionDomainEventFacetAbstract extends SingleClassValu
         domainEventHelper = new DomainEventHelper(servicesInjector);
     }
 
+    private Class<? extends CollectionDomainEvent<?, ?>> eventType;
+
+    private Class<?> eventType() {
+        return value();
+    }
+
+    @Override
+    public Class<?> value() {
+        return eventType;
+    }
+
+    public Class<? extends CollectionDomainEvent<?, ?>> getEventType() {
+        return eventType;
+    }
+
+    public void setEventType(final Class<? extends CollectionDomainEvent<?, ?>> eventType) {
+        this.eventType = eventType;
+    }
+
     @Override
     public String hides(final VisibilityContext<? extends VisibilityEvent> ic) {
 
@@ -65,7 +85,7 @@ public abstract class CollectionDomainEventFacetAbstract extends SingleClassValu
                 domainEventHelper.postEventForCollection(
                         AbstractDomainEvent.Phase.HIDE,
                         eventType(), null,
-                        getIdentified(), ic.getTarget(),
+                        getIdentified(), ic.getTarget(), ic.getMixedIn(),
                         CollectionDomainEvent.Of.ACCESS,
                         null);
         if (event != null && event.isHidden()) {
@@ -81,7 +101,7 @@ public abstract class CollectionDomainEventFacetAbstract extends SingleClassValu
                 domainEventHelper.postEventForCollection(
                         AbstractDomainEvent.Phase.DISABLE,
                         eventType(), null,
-                        getIdentified(), ic.getTarget(),
+                        getIdentified(), ic.getTarget(), ic.getMixedIn(),
                         CollectionDomainEvent.Of.ACCESS,
                         null);
         if (event != null && event.isDisabled()) {
@@ -97,6 +117,10 @@ public abstract class CollectionDomainEventFacetAbstract extends SingleClassValu
     @Override
     public String invalidates(final ValidityContext<? extends ValidityEvent> ic) {
 
+        // if this is a mixin, then this ain't true.
+        if(!(ic instanceof ProposedHolder)) {
+            return null;
+        }
         final ProposedHolder catc = (ProposedHolder) ic;
         final Object proposed = catc.getProposed().getObject();
 
@@ -109,7 +133,7 @@ public abstract class CollectionDomainEventFacetAbstract extends SingleClassValu
                 domainEventHelper.postEventForCollection(
                         AbstractDomainEvent.Phase.VALIDATE,
                         eventType(), null,
-                        getIdentified(), ic.getTarget(),
+                        getIdentified(), ic.getTarget(), ic.getMixedIn(),
                         of,
                         proposed);
         if (event != null && event.isInvalid()) {
@@ -123,16 +147,4 @@ public abstract class CollectionDomainEventFacetAbstract extends SingleClassValu
         return null;
     }
 
-    private Class<?> eventType() {
-        return value();
-    }
-
-    /**
-     * For testing.
-     */
-    public Class<? extends CollectionDomainEvent<?, ?>> getEventType() {
-        Class eventType = eventType();
-        return eventType;
-    }
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionRemoveFromFacetForDomainEventFromAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionRemoveFromFacetForDomainEventFromAbstract.java
index 1418f84..95ef179 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionRemoveFromFacetForDomainEventFromAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionRemoveFromFacetForDomainEventFromAbstract.java
@@ -86,13 +86,14 @@ public abstract class CollectionRemoveFromFacetForDomainEventFromAbstract
 
         // contains the element, so
         // execute the remove wrapped between the executing and executed events ...
+        final ObjectAdapter mixedInAdapter = null;
 
         // ... post the executing event
         final CollectionDomainEvent<?, ?> event =
                 domainEventHelper.postEventForCollection(
                         AbstractDomainEvent.Phase.EXECUTING,
                         eventType(), null,
-                        getIdentified(), targetAdapter,
+                        getIdentified(), targetAdapter, null,
                         CollectionDomainEvent.Of.REMOVE_FROM,
                         referencedObject);
 
@@ -103,7 +104,7 @@ public abstract class CollectionRemoveFromFacetForDomainEventFromAbstract
         domainEventHelper.postEventForCollection(
                 AbstractDomainEvent.Phase.EXECUTED,
                 value(), verify(event),
-                getIdentified(), targetAdapter,
+                getIdentified(), targetAdapter, mixedInAdapter,
                 CollectionDomainEvent.Of.REMOVE_FROM,
                 referencedObject);
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
index 47db77a..d7869dc 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
@@ -146,6 +146,10 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme
             return;
         }
 
+        // following only runs for regular properties, not for mixins.
+        // those are tackled in the post-processing, when more of the metamodel is available to us
+
+
         //
         // Set up PropertyDomainEventFacet, which will act as the hiding/disabling/validating advisor
         //
@@ -259,7 +263,7 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme
         }
     }
 
-    private static Class<? extends PropertyDomainEvent<?,?>> defaultFromDomainObjectIfRequired(
+    public static Class<? extends PropertyDomainEvent<?,?>> defaultFromDomainObjectIfRequired(
             final ObjectSpecification typeSpec,
             final Class<? extends PropertyDomainEvent<?,?>> propertyDomainEventType) {
         if (propertyDomainEventType == PropertyDomainEvent.Default.class) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetAbstract.java
index 4857840..4b00586 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetAbstract.java
@@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.facets.properties.property.modify;
 
 import java.util.Map;
 
+import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.events.UsabilityEvent;
 import org.apache.isis.applib.events.ValidityEvent;
 import org.apache.isis.applib.events.VisibilityEvent;
@@ -40,24 +41,29 @@ import org.apache.isis.core.metamodel.interactions.ValidityContext;
 import org.apache.isis.core.metamodel.interactions.VisibilityContext;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationMixedIn;
 
 public abstract class PropertyDomainEventFacetAbstract
         extends SingleClassValueFacetAbstract implements PropertyDomainEventFacet {
 
     private final DomainEventHelper domainEventHelper;
 
-    private final PropertyOrCollectionAccessorFacet getterFacet;
+    private final PropertyOrCollectionAccessorFacet getterFacetIfAny;
     private final TranslationService translationService;
     private final String translationContext;
 
+    /**
+     * @param getterFacetIfAny - will be null if this is for a mixin {@link OneToOneAssociationMixedIn}.
+     */
     public PropertyDomainEventFacetAbstract(
             final Class<? extends PropertyDomainEvent<?, ?>> eventType,
-            final PropertyOrCollectionAccessorFacet getterFacet,
+            final PropertyOrCollectionAccessorFacet getterFacetIfAny,
             final FacetHolder holder,
             final ServicesInjector servicesInjector,
             final SpecificationLoader specificationLoader) {
         super(PropertyDomainEventFacet.class, holder, eventType, specificationLoader);
-        this.getterFacet = getterFacet;
+        this.eventType = eventType;
+        this.getterFacetIfAny = getterFacetIfAny;
 
         this.translationService = servicesInjector.lookupService(TranslationService.class);
         // sadness: same as in TranslationFactory
@@ -66,6 +72,29 @@ public abstract class PropertyDomainEventFacetAbstract
         domainEventHelper = new DomainEventHelper(servicesInjector);
     }
 
+    private Class<? extends PropertyDomainEvent<?, ?>> eventType;
+
+    @Override
+    public Class<?> value() {
+        return eventType;
+    }
+
+    private Class<?> eventType() {
+        return eventType;
+    }
+
+    public Class<? extends PropertyDomainEvent<?, ?>> getEventType() {
+        return eventType;
+    }
+
+    /**
+     * Can be overwritten if this facet is on a mixin where the subject (mixedInType) is annotated with
+     * {@link DomainObject#propertyDomainEvent()}.
+     */
+    public void setEventType(final Class<? extends PropertyDomainEvent<?, ?>> eventType) {
+        this.eventType = eventType;
+    }
+
     @Override
     public String hides(VisibilityContext<? extends VisibilityEvent> ic) {
 
@@ -73,7 +102,7 @@ public abstract class PropertyDomainEventFacetAbstract
                 domainEventHelper.postEventForProperty(
                         AbstractDomainEvent.Phase.HIDE,
                         eventType(), null,
-                        getIdentified(), ic.getTarget(),
+                        getIdentified(), ic.getTarget(), ic.getMixedIn(),
                         null, null);
         if (event != null && event.isHidden()) {
             return "Hidden by subscriber";
@@ -88,7 +117,7 @@ public abstract class PropertyDomainEventFacetAbstract
                 domainEventHelper.postEventForProperty(
                         AbstractDomainEvent.Phase.DISABLE,
                         eventType(), null,
-                        getIdentified(), ic.getTarget(),
+                        getIdentified(), ic.getTarget(), ic.getMixedIn(),
                         null, null);
         if (event != null && event.isDisabled()) {
             final TranslatableString reasonTranslatable = event.getDisabledReasonTranslatable();
@@ -103,15 +132,25 @@ public abstract class PropertyDomainEventFacetAbstract
     @Override
     public String invalidates(ValidityContext<? extends ValidityEvent> ic) {
 
-        final Object oldValue = getterFacet.getProperty(ic.getTarget(),
-                ic.getInitiatedBy());
-        final Object proposedValue = proposedFrom(ic);
+        if(getterFacetIfAny == null) {
+            return null;
+        }
+
+        // if this is a mixin, then this ain't true.
+        if(!(ic instanceof ProposedHolder)) {
+            return null;
+        }
+        final ProposedHolder ph = (ProposedHolder) ic;
+
+        final Object oldValue = getterFacetIfAny.getProperty(ic.getTarget(), ic.getInitiatedBy());
+        final ObjectAdapter proposedAdapter = ph.getProposed();
+        final Object proposedValue = proposedAdapter != null ? proposedAdapter.getObject() : null;
 
         final PropertyDomainEvent<?, ?> event =
                 domainEventHelper.postEventForProperty(
                         AbstractDomainEvent.Phase.VALIDATE,
                         eventType(), null,
-                        getIdentified(), ic.getTarget(),
+                        getIdentified(), ic.getTarget(), ic.getMixedIn(),
                         oldValue, proposedValue);
         if (event != null && event.isInvalid()) {
             final TranslatableString reasonTranslatable = event.getInvalidityReasonTranslatable();
@@ -124,27 +163,10 @@ public abstract class PropertyDomainEventFacetAbstract
         return null;
     }
 
-    private static Object proposedFrom(ValidityContext<? extends ValidityEvent> ic) {
-        final ProposedHolder ph = (ProposedHolder) ic;
-        final ObjectAdapter proposedAdapter = ph.getProposed();
-        return proposedAdapter != null? proposedAdapter.getObject(): null;
-    }
-
-    private Class<?> eventType() {
-        return value();
-    }
-
-    /**
-     * For testing.
-     */
-    public Class<? extends PropertyDomainEvent<?, ?>> getEventType() {
-        Class eventType = eventType();
-        return eventType;
-    }
-
-    @Override public void appendAttributesTo(final Map<String, Object> attributeMap) {
+    @Override
+    public void appendAttributesTo(final Map<String, Object> attributeMap) {
         super.appendAttributesTo(attributeMap);
-        attributeMap.put("getterFacet", getterFacet);
+        attributeMap.put("getterFacet", getterFacetIfAny);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetDefault.java
index 43cb32d..f228dbd 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetDefault.java
@@ -24,14 +24,18 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationMixedIn;
 
 public class PropertyDomainEventFacetDefault extends PropertyDomainEventFacetAbstract {
 
+    /**
+     * @param getterFacetIfAny - will be null if this is for a mixin {@link OneToOneAssociationMixedIn}.
+     */
     public PropertyDomainEventFacetDefault(
             final Class<? extends PropertyDomainEvent<?, ?>> eventType,
-            final PropertyOrCollectionAccessorFacet getterFacet,
+            final PropertyOrCollectionAccessorFacet getterFacetIfAny,
             final ServicesInjector servicesInjector, final SpecificationLoader specificationLoader, final FacetHolder holder) {
-        super(eventType, getterFacet, holder, servicesInjector, specificationLoader);
+        super(eventType, getterFacetIfAny, holder, servicesInjector, specificationLoader);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPostsPropertyChangedEventAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPostsPropertyChangedEventAnnotation.java
index d848833..bae3f46 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPostsPropertyChangedEventAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPostsPropertyChangedEventAnnotation.java
@@ -24,6 +24,7 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationMixedIn;
 
 /**
  * @deprecated
@@ -31,11 +32,14 @@ import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 @Deprecated
 public class PropertyDomainEventFacetForPostsPropertyChangedEventAnnotation extends PropertyDomainEventFacetAbstract {
 
+    /**
+     * @param getterFacetIfAny - will be null if this is for a mixin {@link OneToOneAssociationMixedIn}.
+     */
     public PropertyDomainEventFacetForPostsPropertyChangedEventAnnotation(
             final Class<? extends PropertyDomainEvent<?, ?>> eventType,
-            final PropertyOrCollectionAccessorFacet getterFacet,
+            final PropertyOrCollectionAccessorFacet getterFacetIfAny,
             final ServicesInjector servicesInjector, final SpecificationLoader specificationLoader, final FacetHolder holder) {
-        super(eventType, getterFacet, holder, servicesInjector, specificationLoader);
+        super(eventType, getterFacetIfAny, holder, servicesInjector, specificationLoader);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyAnnotation.java
index a31c90a..b731d35 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyAnnotation.java
@@ -24,14 +24,18 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationMixedIn;
 
 public class PropertyDomainEventFacetForPropertyAnnotation extends PropertyDomainEventFacetAbstract {
 
+    /**
+     * @param getterFacetIfAny - will be null if this is for a mixin {@link OneToOneAssociationMixedIn}.
+     */
     public PropertyDomainEventFacetForPropertyAnnotation(
             final Class<? extends PropertyDomainEvent<?, ?>> eventType,
-            final PropertyOrCollectionAccessorFacet getterFacet,
+            final PropertyOrCollectionAccessorFacet getterFacetIfAny,
             final ServicesInjector servicesInjector, final SpecificationLoader specificationLoader, final FacetHolder holder) {
-        super(eventType, getterFacet, holder, servicesInjector, specificationLoader);
+        super(eventType, getterFacetIfAny, holder, servicesInjector, specificationLoader);
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyInteractionAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyInteractionAnnotation.java
index 1e49c31..26e812d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyInteractionAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyInteractionAnnotation.java
@@ -24,6 +24,7 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationMixedIn;
 
 /**
  * @deprecated
@@ -31,11 +32,14 @@ import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 @Deprecated
 public class PropertyDomainEventFacetForPropertyInteractionAnnotation extends PropertyDomainEventFacetAbstract {
 
+    /**
+     * @param getterFacetIfAny - will be null if this is for a mixin {@link OneToOneAssociationMixedIn}.
+     */
     public PropertyDomainEventFacetForPropertyInteractionAnnotation(
             final Class<? extends PropertyDomainEvent<?, ?>> eventType,
-            final PropertyOrCollectionAccessorFacet getterFacet,
+            final PropertyOrCollectionAccessorFacet getterFacetIfAny,
             final ServicesInjector servicesInjector, final SpecificationLoader specificationLoader, final FacetHolder holder) {
-        super(eventType, getterFacet, holder, servicesInjector, specificationLoader);
+        super(eventType, getterFacetIfAny, holder, servicesInjector, specificationLoader);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java
index fd44952..1fd5017 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java
@@ -139,6 +139,7 @@ public abstract class PropertySetterOrClearFacetForDomainEventAbstract
             final ObjectAdapter targetAdapter,
             final InteractionInitiatedBy interactionInitiatedBy) {
 
+        final ObjectAdapter mixedInAdapter = null;
         setOrClearProperty(Style.CLEAR,
                 owningProperty, targetAdapter, null, interactionInitiatedBy);
 
@@ -150,6 +151,7 @@ public abstract class PropertySetterOrClearFacetForDomainEventAbstract
             final ObjectAdapter newValueAdapter,
             final InteractionInitiatedBy interactionInitiatedBy) {
 
+        final ObjectAdapter mixedInAdapter = null;
         setOrClearProperty(Style.SET,
                 owningProperty, targetAdapter, newValueAdapter, interactionInitiatedBy);
 
@@ -162,11 +164,12 @@ public abstract class PropertySetterOrClearFacetForDomainEventAbstract
             final ObjectAdapter newValueAdapter,
             final InteractionInitiatedBy interactionInitiatedBy) {
 
+        final ObjectAdapter mixedInAdapter = null;
         getPersistenceSessionServiceInternal().executeWithinTransaction(
                 new TransactionalClosure(){
                     @Override
                     public void execute() {
-                        doSetOrClearProperty(style, owningProperty, targetAdapter, newValueAdapter, interactionInitiatedBy);
+                        doSetOrClearProperty(style, owningProperty, targetAdapter, mixedInAdapter, newValueAdapter, interactionInitiatedBy);
                     }
                 }
         );
@@ -179,6 +182,7 @@ public abstract class PropertySetterOrClearFacetForDomainEventAbstract
             final Style style,
             final OneToOneAssociation owningProperty,
             final ObjectAdapter targetAdapter,
+            final ObjectAdapter mixedInAdapter,
             final ObjectAdapter newValueAdapter,
             final InteractionInitiatedBy interactionInitiatedBy) {
 
@@ -250,7 +254,7 @@ public abstract class PropertySetterOrClearFacetForDomainEventAbstract
                                         domainEventHelper.postEventForProperty(
                                                 AbstractDomainEvent.Phase.EXECUTING,
                                                 eventType(), null,
-                                                getIdentified(), targetAdapter,
+                                                getIdentified(), targetAdapter, mixedInAdapter,
                                                 oldValue, newValue);
 
 
@@ -271,7 +275,7 @@ public abstract class PropertySetterOrClearFacetForDomainEventAbstract
                                     domainEventHelper.postEventForProperty(
                                             AbstractDomainEvent.Phase.EXECUTED,
                                             eventType(), verify(event),
-                                            getIdentified(), targetAdapter,
+                                            getIdentified(), targetAdapter, mixedInAdapter,
                                             oldValue, actualNewValue);
                                 }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java
index 0d70429..8c390b2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java
@@ -19,22 +19,35 @@
 
 package org.apache.isis.core.metamodel.postprocessors.param;
 
+import java.lang.reflect.Method;
 import java.util.List;
 
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
+import org.apache.isis.applib.annotation.Collection;
+import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.filter.Filters;
+import org.apache.isis.applib.services.eventbus.ActionDomainEvent;
+import org.apache.isis.applib.services.eventbus.CollectionDomainEvent;
+import org.apache.isis.applib.services.eventbus.PropertyDomainEvent;
 import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider;
 import org.apache.isis.core.metamodel.deployment.DeploymentCategory;
 import org.apache.isis.core.metamodel.deployment.DeploymentCategoryProvider;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facets.Annotations;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
 import org.apache.isis.core.metamodel.facets.TypedHolder;
+import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet;
+import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacetAbstract;
 import org.apache.isis.core.metamodel.facets.actions.defaults.ActionDefaultsFacet;
 import org.apache.isis.core.metamodel.facets.all.describedas.DescribedAsFacet;
+import org.apache.isis.core.metamodel.facets.collections.collection.CollectionAnnotationFacetFactory;
+import org.apache.isis.core.metamodel.facets.collections.collection.modify.CollectionDomainEventFacet;
+import org.apache.isis.core.metamodel.facets.collections.collection.modify.CollectionDomainEventFacetAbstract;
+import org.apache.isis.core.metamodel.facets.collections.collection.modify.CollectionDomainEventFacetForCollectionAnnotation;
 import org.apache.isis.core.metamodel.facets.collections.disabled.fromimmutable.DisabledFacetOnCollectionDerivedFromImmutable;
 import org.apache.isis.core.metamodel.facets.collections.disabled.fromimmutable.DisabledFacetOnCollectionDerivedFromImmutableFactory;
 import org.apache.isis.core.metamodel.facets.members.describedas.annotprop.DescribedAsFacetOnMemberDerivedFromType;
@@ -42,6 +55,9 @@ import org.apache.isis.core.metamodel.facets.members.describedas.annotprop.Descr
 import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
 import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacetAbstract;
 import org.apache.isis.core.metamodel.facets.object.defaults.DefaultedFacet;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.CollectionDomainEventDefaultFacetForDomainObjectAnnotation;
+import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.PropertyDomainEventDefaultFacetForDomainObjectAnnotation;
 import org.apache.isis.core.metamodel.facets.object.immutable.ImmutableFacet;
 import org.apache.isis.core.metamodel.facets.object.immutable.immutableannot.CopyImmutableFacetOntoMembersFactory;
 import org.apache.isis.core.metamodel.facets.object.recreatable.DisabledFacetOnCollectionDerivedFromRecreatableObject;
@@ -60,6 +76,7 @@ import org.apache.isis.core.metamodel.facets.param.describedas.annotderived.Desc
 import org.apache.isis.core.metamodel.facets.param.describedas.annotderived.DescribedAsFacetOnParameterDerivedFromType;
 import org.apache.isis.core.metamodel.facets.param.typicallen.fromtype.TypicalLengthFacetOnParameterDerivedFromType;
 import org.apache.isis.core.metamodel.facets.param.typicallen.fromtype.TypicalLengthFacetOnParameterDerivedFromTypeFacetFactory;
+import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.facets.properties.choices.PropertyChoicesFacet;
 import org.apache.isis.core.metamodel.facets.properties.choices.enums.PropertyChoicesFacetDerivedFromChoicesFacet;
 import org.apache.isis.core.metamodel.facets.properties.choices.enums.PropertyChoicesFacetDerivedFromChoicesFacetFactory;
@@ -68,6 +85,10 @@ import org.apache.isis.core.metamodel.facets.properties.defaults.fromtype.Proper
 import org.apache.isis.core.metamodel.facets.properties.defaults.fromtype.PropertyDefaultFacetDerivedFromTypeFactory;
 import org.apache.isis.core.metamodel.facets.properties.disabled.fromimmutable.DisabledFacetOnPropertyDerivedFromImmutable;
 import org.apache.isis.core.metamodel.facets.properties.disabled.fromimmutable.DisabledFacetOnPropertyDerivedFromImmutableFactory;
+import org.apache.isis.core.metamodel.facets.properties.property.PropertyAnnotationFacetFactory;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacet;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetAbstract;
+import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetForPropertyAnnotation;
 import org.apache.isis.core.metamodel.facets.properties.typicallen.fromtype.TypicalLengthFacetOnPropertyDerivedFromType;
 import org.apache.isis.core.metamodel.facets.properties.typicallen.fromtype.TypicalLengthFacetOnPropertyDerivedFromTypeFacetFactory;
 import org.apache.isis.core.metamodel.progmodel.ObjectSpecificationPostProcessor;
@@ -84,8 +105,11 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionMixedIn;
 import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionParameterAbstract;
 import org.apache.isis.core.metamodel.specloader.specimpl.ObjectMemberAbstract;
+import org.apache.isis.core.metamodel.specloader.specimpl.OneToManyAssociationMixedIn;
+import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationMixedIn;
 
 /**
  * Sets up all the {@link Facet}s for an action in a single shot.
@@ -97,6 +121,7 @@ public class DeriveFacetsPostProcessor implements ObjectSpecificationPostProcess
     private SpecificationLoader specificationLoader;
     private AuthenticationSessionProvider authenticationSessionProvider;
     private PersistenceSessionServiceInternal adapterManager;
+    private ServicesInjector servicesInjector;
 
     @Override
     public void postProcess(final ObjectSpecification objectSpecification) {
@@ -112,6 +137,7 @@ public class DeriveFacetsPostProcessor implements ObjectSpecificationPostProcess
         final List<OneToOneAssociation> properties = objectSpecification.getProperties(Contributed.INCLUDED);
 
         // for each action, ...
+
         for (final ObjectAction objectAction : objectActions) {
 
             // ... for each action parameter
@@ -131,6 +157,7 @@ public class DeriveFacetsPostProcessor implements ObjectSpecificationPostProcess
             // corresponds to CopyImmutableFacetOntoMembersFactory.  However, ImmutableFacet only ever disables for
             // properties and collections, so no point in copying over.
 
+            tweakActionDomainEventForMixin(objectSpecification, objectAction);
         }
 
         // for each property, ...
@@ -142,6 +169,7 @@ public class DeriveFacetsPostProcessor implements ObjectSpecificationPostProcess
             derivePropertyDisabledFromViewModel(property);
             derivePropertyOrCollectionImmutableFromSpec(property);
             derivePropertyDisabledFromImmutable(property);
+            tweakPropertyMixinDomainEvent(objectSpecification, property);
         }
 
 
@@ -198,6 +226,109 @@ public class DeriveFacetsPostProcessor implements ObjectSpecificationPostProcess
                     addCollectionParamChoicesFacetIfNoneAlready(collection, scalarParam);
                 }
             }
+
+            deriveCollectionDomainEventForMixins(objectSpecification, collection);
+        }
+    }
+
+    private void tweakActionDomainEventForMixin(
+            final ObjectSpecification objectSpecification,
+            final ObjectAction objectAction) {
+        if(objectAction instanceof ObjectActionMixedIn) {
+            // unlike collection and property mixins, there is no need to create the DomainEventFacet, it will
+            // have been created in the ActionAnnotationFacetFactory
+            final ActionDomainEventDefaultFacetForDomainObjectAnnotation actionDomainEventDefaultFacet =
+                    objectSpecification.getFacet(ActionDomainEventDefaultFacetForDomainObjectAnnotation.class);
+
+            if(actionDomainEventDefaultFacet != null) {
+                final ObjectActionMixedIn actionMixedIn = (ObjectActionMixedIn) objectAction;
+                final ActionDomainEventFacet actionFacet = actionMixedIn.getFacet(ActionDomainEventFacet.class);
+                if (actionFacet instanceof ActionDomainEventFacetAbstract) {
+                    final ActionDomainEventFacetAbstract facetAbstract = (ActionDomainEventFacetAbstract) actionFacet;
+                    if (facetAbstract.getEventType() == ActionDomainEvent.Default.class) {
+                        final ActionDomainEventFacetAbstract existing = (ActionDomainEventFacetAbstract) actionFacet;
+                        existing.setEventType(actionDomainEventDefaultFacet.getEventType());
+                    }
+                }
+            }
+        }
+    }
+
+    private void deriveCollectionDomainEventForMixins(
+            final ObjectSpecification objectSpecification,
+            final OneToManyAssociation collection) {
+
+        if(collection instanceof OneToManyAssociationMixedIn) {
+            final OneToManyAssociationMixedIn collectionMixin = (OneToManyAssociationMixedIn) collection;
+            final FacetedMethod facetedMethod = collectionMixin.getFacetedMethod();
+            final Method method = facetedMethod != null ? facetedMethod.getMethod() : null;
+
+            if(method != null) {
+                // this is basically a subset of the code that is in CollectionAnnotationFacetFactory,
+                // ignoring stuff which is deprecated for Isis v2
+                final Collection collectionAnnot = Annotations.getAnnotation(method, Collection.class);
+                if(collectionAnnot != null) {
+                    final Class<? extends CollectionDomainEvent<?, ?>> collectionDomainEventType =
+                            CollectionAnnotationFacetFactory.defaultFromDomainObjectIfRequired(
+                                    objectSpecification, collectionAnnot.domainEvent());
+                    final CollectionDomainEventFacetForCollectionAnnotation collectionDomainEventFacet = new CollectionDomainEventFacetForCollectionAnnotation(
+                            collectionDomainEventType, servicesInjector, specificationLoader, collection);
+                    FacetUtil.addFacet(collectionDomainEventFacet);
+                }
+
+                final CollectionDomainEventDefaultFacetForDomainObjectAnnotation collectionDomainEventDefaultFacet =
+                        objectSpecification.getFacet(CollectionDomainEventDefaultFacetForDomainObjectAnnotation.class);
+                if(collectionDomainEventDefaultFacet != null) {
+                    final CollectionDomainEventFacet collectionFacet = collection.getFacet(CollectionDomainEventFacet.class);
+                    if (collectionFacet instanceof CollectionDomainEventFacetAbstract) {
+                        final CollectionDomainEventFacetAbstract facetAbstract = (CollectionDomainEventFacetAbstract) collectionFacet;
+                        if (facetAbstract.getEventType() == CollectionDomainEvent.Default.class) {
+                            final CollectionDomainEventFacetAbstract existing = (CollectionDomainEventFacetAbstract) collectionFacet;
+                            existing.setEventType(collectionDomainEventDefaultFacet.getEventType());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void tweakPropertyMixinDomainEvent(
+            final ObjectSpecification objectSpecification,
+            final OneToOneAssociation property) {
+
+        if(property instanceof OneToOneAssociationMixedIn) {
+            final OneToOneAssociationMixedIn propertyMixin = (OneToOneAssociationMixedIn) property;
+            final FacetedMethod facetedMethod = propertyMixin.getFacetedMethod();
+            final Method method = facetedMethod != null ? facetedMethod.getMethod() : null;
+
+            if(method != null) {
+                // this is basically a subset of the code that is in CollectionAnnotationFacetFactory,
+                // ignoring stuff which is deprecated for Isis v2
+                final Property propertyAnnot = Annotations.getAnnotation(method, Property.class);
+                if(propertyAnnot != null) {
+                    final Class<? extends PropertyDomainEvent<?, ?>> propertyDomainEventType =
+                            PropertyAnnotationFacetFactory.defaultFromDomainObjectIfRequired(
+                                    objectSpecification, propertyAnnot.domainEvent());
+                    final PropertyOrCollectionAccessorFacet getterFacetIfAny = null;
+                    final PropertyDomainEventFacetForPropertyAnnotation propertyDomainEventFacet =
+                            new PropertyDomainEventFacetForPropertyAnnotation(
+                                propertyDomainEventType, getterFacetIfAny,
+                                servicesInjector, specificationLoader, property);
+                    FacetUtil.addFacet(propertyDomainEventFacet);
+                }
+            }
+            final PropertyDomainEventDefaultFacetForDomainObjectAnnotation propertyDomainEventDefaultFacet =
+                objectSpecification.getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class);
+            if(propertyDomainEventDefaultFacet != null) {
+                final PropertyDomainEventFacet propertyFacet = property.getFacet(PropertyDomainEventFacet.class);
+                if (propertyFacet instanceof PropertyDomainEventFacetAbstract) {
+                    final PropertyDomainEventFacetAbstract facetAbstract = (PropertyDomainEventFacetAbstract) propertyFacet;
+                    if (facetAbstract.getEventType() == PropertyDomainEvent.Default.class) {
+                        final PropertyDomainEventFacetAbstract existing = (PropertyDomainEventFacetAbstract) propertyFacet;
+                        existing.setEventType(propertyDomainEventDefaultFacet.getEventType());
+                    }
+                }
+            }
         }
     }
 
@@ -498,10 +629,16 @@ public class DeriveFacetsPostProcessor implements ObjectSpecificationPostProcess
 
     @Override
     public void setServicesInjector(final ServicesInjector servicesInjector) {
+        this.servicesInjector = servicesInjector;
         deploymentCategoryProvider = servicesInjector.getDeploymentCategoryProvider();
         specificationLoader = servicesInjector.getSpecificationLoader();
         authenticationSessionProvider = servicesInjector.getAuthenticationSessionProvider();
         adapterManager = servicesInjector.getPersistenceSessionServiceInternal();
     }
 
+    private ServicesInjector getServicesInjector() {
+        return servicesInjector;
+    }
+
+
 }