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:09 UTC

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

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;
+    }
+
+
 }