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 2021/09/06 15:46:19 UTC

[isis] branch ISIS-2867 created (now de1f193)

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

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


      at de1f193  ISIS-2866: ensures @MemberSupport everywhere; converts service actions to local mixins.

This branch includes the following new commits:

     new 56cfee9  ISIS-2867: adds arch tests 'unique logical type name'; 'logicalTypeName matches JDO discriminator'
     new de1f193  ISIS-2866: ensures @MemberSupport everywhere; converts service actions to local mixins.

The 2 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.


[isis] 02/02: ISIS-2866: ensures @MemberSupport everywhere; converts service actions to local mixins.

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

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

commit de1f19399550d9a1b550bb651d6e368860620680
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Sep 6 16:45:57 2021 +0100

    ISIS-2866: ensures @MemberSupport everywhere; converts service actions to local mixins.
---
 .../services/confview/ConfigurationMenu.java       |  19 +-
 .../applib/services/layout/LayoutServiceMenu.java  |  63 ++--
 .../services/metamodel/MetaModelServiceMenu.java   | 373 ++++++++++-----------
 .../isis/applib/services/user/ImpersonateMenu.java | 143 ++++----
 .../applib/services/user/ImpersonateStopMenu.java  |  24 +-
 .../isis/applib/services/userui/UserMenu.java      |  22 +-
 .../primary/mixins/Object_openOnSecondary.java     |  11 +-
 .../primary/ui/CommandReplayOnPrimaryService.java  | 194 ++++++-----
 .../ui/CommandReplayOnSecondaryService.java        |  51 +--
 ...OrphanedPermissionManager_relocateSelected.java |   5 +-
 .../dom/mixins/ApplicationPermission_allow.java    |   5 +-
 .../dom/mixins/ApplicationPermission_changing.java |   5 +-
 .../dom/mixins/ApplicationPermission_delete.java   |   3 +-
 .../dom/mixins/ApplicationPermission_feature.java  |  12 +-
 .../mixins/ApplicationPermission_updateRole.java   |  12 +-
 .../dom/mixins/ApplicationPermission_veto.java     |   6 +-
 .../dom/mixins/ApplicationPermission_viewing.java  |   5 +-
 .../permission/menu/ApplicationPermissionMenu.java |  39 ++-
 .../dom/mixins/ApplicationRole_addPermission.java  |  15 +-
 .../role/dom/mixins/ApplicationRole_addUser.java   |   6 +-
 .../role/dom/mixins/ApplicationRole_delete.java    |   6 +-
 .../mixins/ApplicationRole_removePermissions.java  |   3 +-
 .../dom/mixins/ApplicationRole_removeUsers.java    |   7 +-
 .../mixins/ApplicationRole_updateDescription.java  |   8 +-
 .../dom/mixins/ApplicationRole_updateName.java     |   8 +-
 .../applib/role/menu/ApplicationRoleMenu.java      |  70 ++--
 .../dom/mixins/ApplicationTenancy_addChild.java    |   3 +-
 .../dom/mixins/ApplicationTenancy_addUser.java     |   6 +-
 .../dom/mixins/ApplicationTenancy_delete.java      |   3 +-
 .../dom/mixins/ApplicationTenancy_removeChild.java |  12 +-
 .../dom/mixins/ApplicationTenancy_removeUser.java  |  12 +-
 .../dom/mixins/ApplicationTenancy_updateName.java  |   8 +-
 .../dom/mixins/ApplicationTenancy_users.java       |   3 +-
 .../tenancy/menu/ApplicationTenancyMenu.java       |  76 +++--
 .../applib/user/app/ApplicationUserManager.java    |   5 +-
 .../mixins/ApplicationUserManager_allUsers.java    |   3 +-
 .../ApplicationUserManager_newDelegateUser.java    |   6 +-
 .../ApplicationUserManager_newLocalUser.java       |   9 +-
 .../user/contributions/HasUsername_open.java       |  15 +-
 .../secman/applib/user/dom/ApplicationUser.java    |  14 +-
 .../user/dom/mixins/ApplicationUser_addRole.java   |   9 +-
 .../user/dom/mixins/ApplicationUser_delete.java    |   6 +-
 .../user/dom/mixins/ApplicationUser_duplicate.java |   7 +-
 .../user/dom/mixins/ApplicationUser_lock.java      |   6 +-
 .../dom/mixins/ApplicationUser_removeRoles.java    |   3 +-
 .../dom/mixins/ApplicationUser_resetPassword.java  |   9 +-
 .../user/dom/mixins/ApplicationUser_unlock.java    |   6 +-
 .../mixins/ApplicationUser_updateAccountType.java  |   7 +-
 .../dom/mixins/ApplicationUser_updateAtPath.java   |   6 +-
 .../mixins/ApplicationUser_updateEmailAddress.java |   9 +-
 .../mixins/ApplicationUser_updateFaxNumber.java    |  12 +-
 .../dom/mixins/ApplicationUser_updateName.java     |  27 +-
 .../dom/mixins/ApplicationUser_updatePassword.java |  16 +-
 .../mixins/ApplicationUser_updatePhoneNumber.java  |  12 +-
 .../dom/mixins/ApplicationUser_updateUsername.java |   8 +-
 ...ApplicationUser_effectiveMemberPermissions.java |   3 +-
 ...ationUser_filterEffectiveMemberPermissions.java |   6 +-
 .../dom/mixins/perms/UserPermissionViewModel.java  |   7 +-
 .../applib/user/menu/ApplicationUserMenu.java      |  38 ++-
 .../secman/applib/user/menu/MeService.java         |  49 +--
 .../jdo/metamodel/menu/JdoMetamodelMenu.java       |  19 +-
 .../service/swagger/SwaggerServiceMenu.java        |  83 +++--
 62 files changed, 781 insertions(+), 857 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/confview/ConfigurationMenu.java b/api/applib/src/main/java/org/apache/isis/applib/services/confview/ConfigurationMenu.java
index 20392f0..3e3a830 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/confview/ConfigurationMenu.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/confview/ConfigurationMenu.java
@@ -26,6 +26,7 @@ import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.annotation.SemanticsOf;
@@ -53,25 +54,25 @@ public class ConfigurationMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleApplib.NAMESPACE_CONF + ".ConfigurationMenu";
 
-    public static abstract class ActionDomainEvent
-            extends IsisModuleApplib.ActionDomainEvent<ConfigurationMenu> {}
+    public static abstract class ActionDomainEvent<T> extends IsisModuleApplib.ActionDomainEvent<T> {}
 
     final FactoryService factoryService;
 
 
-    public static class ConfigurationDomainEvent
-            extends ActionDomainEvent {}
-
     @Action(
-            domainEvent = ConfigurationDomainEvent.class,
+            domainEvent = configuration.ActionEvent.class,
             semantics = SemanticsOf.SAFE
     )
     @ActionLayout(
             cssClassFa = "fa-wrench",
             sequence = "500.900.1")
-    public ConfigurationViewmodel configuration(){
-        return factoryService.viewModel(new ConfigurationViewmodel());
-    }
+    public class configuration{
 
+        public class ActionEvent extends ActionDomainEvent<configuration> {}
+
+        @MemberSupport public ConfigurationViewmodel act(){
+            return factoryService.viewModel(new ConfigurationViewmodel());
+        }
+    }
 
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/layout/LayoutServiceMenu.java b/api/applib/src/main/java/org/apache/isis/applib/services/layout/LayoutServiceMenu.java
index ee56a91..078067e 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/layout/LayoutServiceMenu.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/layout/LayoutServiceMenu.java
@@ -20,7 +20,6 @@ package org.apache.isis.applib.services.layout;
 
 import javax.activation.MimeType;
 import javax.activation.MimeTypeParseException;
-import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.apache.isis.applib.IsisModuleApplib;
@@ -55,12 +54,13 @@ public class LayoutServiceMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleApplib.NAMESPACE + ".LayoutServiceMenu";
 
-    public static abstract class ActionDomainEvent
-    extends IsisModuleApplib.ActionDomainEvent<LayoutServiceMenu> {}
+    public static abstract class ActionDomainEvent<T> extends IsisModuleApplib.ActionDomainEvent<T> {}
 
+    private final LayoutService layoutService;
     private final MimeType mimeTypeApplicationZip;
 
-    public LayoutServiceMenu() {
+    public LayoutServiceMenu(final LayoutService layoutService) {
+        this.layoutService = layoutService;
         try {
             mimeTypeApplicationZip = new MimeType("application", "zip");
         } catch (final MimeTypeParseException ex) {
@@ -68,10 +68,9 @@ public class LayoutServiceMenu {
         }
     }
 
-    public static class DownloadLayoutsDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = DownloadLayoutsDomainEvent.class,
+            domainEvent = downloadLayouts.ActionEvent.class,
             semantics = SemanticsOf.NON_IDEMPOTENT, //disable client-side caching
             restrictTo = RestrictTo.PROTOTYPING
             )
@@ -79,25 +78,25 @@ public class LayoutServiceMenu {
             cssClassFa = "fa-download",
             named = "Download Object Layouts (ZIP)",
             sequence="500.400.1")
-    // ...
-    public Blob downloadLayouts(final Style style) {
+    public class downloadLayouts{
 
-        final String fileName = "layouts." + style.name().toLowerCase() + ".zip";
+        public class ActionEvent extends ActionDomainEvent<downloadLayouts> {}
 
-        final byte[] zipBytes = layoutService.toZip(style);
-        return new Blob(fileName, mimeTypeApplicationZip, zipBytes);
-        // ...
-    }
+        @MemberSupport public Blob act(final Style style) {
+
+            final String fileName = "layouts." + style.name().toLowerCase() + ".zip";
 
-    @MemberSupport
-    public Style default0DownloadLayouts() {
-        return Style.NORMALIZED;
+            final byte[] zipBytes = layoutService.toZip(style);
+            return new Blob(fileName, mimeTypeApplicationZip, zipBytes);
+        }
+
+        @MemberSupport public Style default0Act() { return Style.NORMALIZED; }
     }
 
-    public static class DownloadMenuBarsLayoutDomainEvent extends ActionDomainEvent {}
+
 
     @Action(
-            domainEvent = DownloadMenuBarsLayoutDomainEvent.class,
+            domainEvent = downloadMenuBarsLayout.ActionEvent.class,
             semantics = SemanticsOf.NON_IDEMPOTENT, //disable client-side caching
             restrictTo = RestrictTo.PROTOTYPING
             )
@@ -105,27 +104,21 @@ public class LayoutServiceMenu {
             cssClassFa = "fa-download",
             named = "Download Menu Bars Layout (XML)",
             sequence="500.400.2")
-    // ...
-    public Clob downloadMenuBarsLayout(
-            @ParameterLayout(named = "File name") final String fileName,
-            final MenuBarsService.Type type) {
+    public class downloadMenuBarsLayout{
 
-        final String xml = layoutService.toMenuBarsXml(type);
+        public class ActionEvent extends ActionDomainEvent<downloadMenuBarsLayout> {}
 
-        return new Clob(_Strings.asFileNameWithExtension(fileName,  ".xml"), "text/xml", xml);
-        // ...
-    }
+        @MemberSupport public Clob act(
+                @ParameterLayout(named = "File name") final String fileName,
+                final MenuBarsService.Type type) {
 
-    @MemberSupport
-    public String default0DownloadMenuBarsLayout() {
-        return "menubars.layout.xml";
-    }
+            final String xml = layoutService.toMenuBarsXml(type);
 
-    @MemberSupport
-    public MenuBarsService.Type default1DownloadMenuBarsLayout() {
-        return MenuBarsService.Type.DEFAULT;
-    }
+            return new Clob(_Strings.asFileNameWithExtension(fileName,  ".xml"), "text/xml", xml);
+        }
 
-    @Inject LayoutService layoutService;
+        @MemberSupport public String default0Act() { return "menubars.layout.xml"; }
+        @MemberSupport public MenuBarsService.Type default1Act() { return MenuBarsService.Type.DEFAULT; }
+    }
 
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelServiceMenu.java b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelServiceMenu.java
index bc5c289..20e9b30 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelServiceMenu.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelServiceMenu.java
@@ -69,14 +69,11 @@ public class MetaModelServiceMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleApplib.NAMESPACE + ".MetaModelServiceMenu";
 
-    public static abstract class ActionDomainEvent extends IsisModuleApplib.ActionDomainEvent<MetaModelServiceMenu> { }
+    public static abstract class ActionDomainEvent<T> extends IsisModuleApplib.ActionDomainEvent<T> { }
 
-    // -- CSV
-
-    public static class DownloadMetaModelEvent extends ActionDomainEvent { }
 
     @Action(
-            domainEvent = DownloadMetaModelEvent.class,
+            domainEvent = downloadMetaModelCsv.ActionEvent.class,
             semantics = SemanticsOf.NON_IDEMPOTENT, //disable client-side caching
             restrictTo = RestrictTo.PROTOTYPING
             )
@@ -84,30 +81,31 @@ public class MetaModelServiceMenu {
             cssClassFa = "fa-download",
             named = "Download Meta Model (CSV)",
             sequence="500.500.2")
-    public Blob downloadMetaModelCsv(
-            @ParameterLayout(named = ".csv file name")
-            final String csvFileName) {
-
-        final DomainModel domainModel =  metaModelService.getDomainModel();
-        final StringBuilder csv = _CsvExport.toCsv(domainModel);
-        
-        return Clob.of(csvFileName, CommonMimeType.CSV, csv)
-                .toBlob(UTF_8)
-                .zip();
-
-        // ...
-    }
+    public class downloadMetaModelCsv {
+
+        public class ActionEvent extends ActionDomainEvent<downloadMetaModelCsv> { }
+
+        @MemberSupport public Blob act(
+                @ParameterLayout(named = ".csv file name")
+                final String csvFileName) {
+
+            final DomainModel domainModel =  metaModelService.getDomainModel();
+            final StringBuilder csv = _CsvExport.toCsv(domainModel);
+
+            return Clob.of(csvFileName, CommonMimeType.CSV, csv)
+                    .toBlob(UTF_8)
+                    .zip();
+        }
+
+        @MemberSupport public String default0Act() {
+            return "metamodel.csv";
+        }
 
-    @MemberSupport
-    public String default0DownloadMetaModelCsv() {
-        return "metamodel.csv";
     }
 
-    // -- XML
 
-    public static class DownloadMetaModelXmlEvent extends ActionDomainEvent { }
     @Action(
-            domainEvent = DownloadMetaModelXmlEvent.class,
+            domainEvent = downloadMetaModelXml.ActionEvent.class,
             semantics = SemanticsOf.NON_IDEMPOTENT, //disable client-side caching
             restrictTo = RestrictTo.PROTOTYPING
             )
@@ -115,72 +113,62 @@ public class MetaModelServiceMenu {
             cssClassFa = "fa-download",
             named = "Download Meta Model (XML)",
             sequence="500.500.2")
-    public Blob downloadMetaModelXml(
-            @ParameterLayout(named = ".xml file name")
-            final String fileName,
-
-            @ParameterLayout(named = "Namespaces",
-            describedAs="Subset of the complete meta model, only including namespaces starting with given prefix")
-            final List<String> namespaces,
-
-            @ParameterLayout(named = "Ignore Interfaces")
-            @Parameter(optionality=Optionality.MANDATORY)
-            final boolean ignoreInterfaces
-            ) {
-
-        Config config =
-                new Config()
-                .withIgnoreNoop()
-                .withIgnoreAbstractClasses()
-                .withIgnoreInterfaces()
-                .withIgnoreBuiltInValueTypes();
-        for (final String namespace : namespaces) {
-            config = config.withNamespacePrefix(namespace);
-        }
-        if(ignoreInterfaces) {
-            config = config.withIgnoreInterfaces();
-        }
-
-        final MetamodelDto metamodelDto =  metaModelService.exportMetaModel(config);
-
-        final String xml = jaxbService.toXml(metamodelDto);
+    public class downloadMetaModelXml{
+
+        public class ActionEvent extends ActionDomainEvent<downloadMetaModelXml> { }
+
+        @MemberSupport public Blob act(
+                @ParameterLayout(named = ".xml file name")
+                final String fileName,
+
+                @ParameterLayout(named = "Namespaces",
+                        describedAs="Subset of the complete meta model, only including namespaces starting with given prefix")
+                final List<String> namespaces,
+
+                @ParameterLayout(named = "Ignore Interfaces")
+                @Parameter(optionality=Optionality.MANDATORY)
+                final boolean ignoreInterfaces
+        ) {
+
+            Config config =
+                    new Config()
+                            .withIgnoreNoop()
+                            .withIgnoreAbstractClasses()
+                            .withIgnoreInterfaces()
+                            .withIgnoreBuiltInValueTypes();
+            for (final String namespace : namespaces) {
+                config = config.withNamespacePrefix(namespace);
+            }
+            if(ignoreInterfaces) {
+                config = config.withIgnoreInterfaces();
+            }
 
-        return Clob.of(fileName, CommonMimeType.XML, xml)
-                .toBlob(UTF_8)
-                .zip();
+            final MetamodelDto metamodelDto =  metaModelService.exportMetaModel(config);
 
-        // ...
-    }
+            final String xml = jaxbService.toXml(metamodelDto);
 
-    @MemberSupport
-    public String validateDownloadMetaModelXml(
-            final String fileName, final List<String> namespacePrefixes, final boolean ignoreInterfaces) {
-        if(namespacePrefixes == null || namespacePrefixes.isEmpty()) {
-            return "At least one package must be selected";
+            return Clob.of(fileName, CommonMimeType.XML, xml)
+                    .toBlob(UTF_8)
+                    .zip();
         }
-        return null;
-    }
 
-    @MemberSupport
-    public String default0DownloadMetaModelXml() {
-        return "metamodel.xml";
-    }
+        @MemberSupport public String validateAct(
+                final String fileName, final List<String> namespacePrefixes, final boolean ignoreInterfaces) {
+            if(namespacePrefixes == null || namespacePrefixes.isEmpty()) {
+                return "At least one package must be selected";
+            }
+            return null;
+        }
 
-    @MemberSupport
-    public List<String> choices1DownloadMetaModelXml() {
-        return namespaceChoices();
-    }
+        @MemberSupport public String default0Act() { return "metamodel.xml"; }
+        @MemberSupport public List<String> choices1Act() { return namespaceChoices(); }
+        @MemberSupport public boolean default2Act() { return true; }
 
-    @MemberSupport
-    public boolean default2DownloadMetaModelXml() {
-        return true;
     }
 
-    // -- ASCII
 
-    public static class DownloadMetaModelAsciiEvent extends ActionDomainEvent { }
     @Action(
-            domainEvent = DownloadMetaModelAsciiEvent.class,
+            domainEvent = downloadMetaModelAscii.ActionEvent.class,
             semantics = SemanticsOf.NON_IDEMPOTENT, //disable client-side caching
             restrictTo = RestrictTo.PROTOTYPING
             )
@@ -188,72 +176,62 @@ public class MetaModelServiceMenu {
             cssClassFa = "fa-download",
             named = "Download Meta Model (Ascii)",
             sequence="500.500.2")
-    public Blob downloadMetaModelAscii(
-            @ParameterLayout(named = ".txt file name")
-            final String fileName,
-
-            @ParameterLayout(named = "Namespaces",
-            describedAs="Subset of the complete meta model, only including namespaces starting with given prefix")
-            final List<String> namespaces,
-
-            @ParameterLayout(named = "Ignore Interfaces")
-            @Parameter(optionality=Optionality.MANDATORY)
-            final boolean ignoreInterfaces
-            ) {
-
-        Config config =
-                new Config()
-                .withIgnoreNoop()
-                .withIgnoreAbstractClasses()
-                .withIgnoreInterfaces()
-                .withIgnoreBuiltInValueTypes();
-        for (final String namespace : namespaces) {
-            config = config.withNamespacePrefix(namespace);
-        }
-        if(ignoreInterfaces) {
-            config = config.withIgnoreInterfaces();
-        }
-
-        final MetamodelDto metamodelDto =  metaModelService.exportMetaModel(config);
+    public class downloadMetaModelAscii{
+
+        public class ActionEvent extends ActionDomainEvent<downloadMetaModelAscii> { }
+
+        @MemberSupport public Blob act(
+                @ParameterLayout(named = ".txt file name")
+                final String fileName,
+
+                @ParameterLayout(named = "Namespaces",
+                        describedAs="Subset of the complete meta model, only including namespaces starting with given prefix")
+                final List<String> namespaces,
+
+                @ParameterLayout(named = "Ignore Interfaces")
+                @Parameter(optionality=Optionality.MANDATORY)
+                final boolean ignoreInterfaces
+        ) {
+
+            Config config =
+                    new Config()
+                            .withIgnoreNoop()
+                            .withIgnoreAbstractClasses()
+                            .withIgnoreInterfaces()
+                            .withIgnoreBuiltInValueTypes();
+            for (final String namespace : namespaces) {
+                config = config.withNamespacePrefix(namespace);
+            }
+            if(ignoreInterfaces) {
+                config = config.withIgnoreInterfaces();
+            }
 
-        final StringBuilder ascii = _AsciiExport.toAscii(metamodelDto);
-        
-        return Clob.of(fileName, CommonMimeType.TXT, ascii)
-                .toBlob(UTF_8)
-                .zip();
+            final MetamodelDto metamodelDto =  metaModelService.exportMetaModel(config);
 
-        // ...
-    }
+            final StringBuilder ascii = _AsciiExport.toAscii(metamodelDto);
 
-    @MemberSupport
-    public String validateDownloadMetaModelAscii(
-            final String fileName, final List<String> namespacePrefixes, final boolean ignoreInterfaces) {
-        if(namespacePrefixes == null || namespacePrefixes.isEmpty()) {
-            return "At least one package must be selected";
+            return Clob.of(fileName, CommonMimeType.TXT, ascii)
+                    .toBlob(UTF_8)
+                    .zip();
         }
-        return null;
-    }
 
-    @MemberSupport
-    public String default0DownloadMetaModelAscii() {
-        return "metamodel.txt";
-    }
+        @MemberSupport public String validateAct(
+                final String fileName, final List<String> namespacePrefixes, final boolean ignoreInterfaces) {
+            if(namespacePrefixes == null || namespacePrefixes.isEmpty()) {
+                return "At least one package must be selected";
+            }
+            return null;
+        }
 
-    @MemberSupport
-    public List<String> choices1DownloadMetaModelAscii() {
-        return namespaceChoices();
-    }
+        @MemberSupport public String default0Act() { return "metamodel.txt"; }
+        @MemberSupport public List<String> choices1Act() { return namespaceChoices(); }
+        @MemberSupport public boolean default2Act() { return true; }
 
-    @MemberSupport
-    public boolean default2DownloadMetaModelAscii() {
-        return true;
     }
 
-    // -- DIFF
 
-    public static class DownloadMetaModelDiffEvent extends ActionDomainEvent { }
     @Action(
-            domainEvent = DownloadMetaModelDiffEvent.class,
+            domainEvent = downloadMetaModelDiff.ActionEvent.class,
             semantics = SemanticsOf.NON_IDEMPOTENT, //disable client-side caching
             restrictTo = RestrictTo.PROTOTYPING
             )
@@ -261,83 +239,76 @@ public class MetaModelServiceMenu {
             cssClassFa = "fa-download",
             named = "Generate Meta Model Diff",
             sequence="500.500.2")
-    public Blob downloadMetaModelDiff(
-            @ParameterLayout(named = ".txt file name")
-            final String fileName,
-
-            @ParameterLayout(named = "Namespaces",
-            describedAs="Subset of the complete meta model, only including namespaces starting with given prefix")
-            final List<String> namespaces,
-
-            @ParameterLayout(named = "Ignore Interfaces")
-            @Parameter(optionality=Optionality.MANDATORY)
-            final boolean ignoreInterfaces, 
-            
-            @ParameterLayout(named="Metamodel (Zipped XML)", 
-            describedAs="Metamodel from a previous export, to compare the current with.")
-            @Parameter(fileAccept=".zip", optionality = Optionality.MANDATORY)
-            Blob zippedMetamodelBlob
-            
-            ) throws IOException {
-
-        Config config =
-                new Config()
-                .withIgnoreNoop()
-                .withIgnoreAbstractClasses()
-                .withIgnoreInterfaces()
-                .withIgnoreBuiltInValueTypes();
-        for (final String namespace : namespaces) {
-            config = config.withNamespacePrefix(namespace);
-        }
-        if(ignoreInterfaces) {
-            config = config.withIgnoreInterfaces();
-        }
+    public class downloadMetaModelDiff {
 
-        final MetamodelDto leftMetamodelDto =  metaModelService.exportMetaModel(config);
+        public class ActionEvent extends ActionDomainEvent<downloadMetaModelDiff> { }
 
-        final String xml = zippedMetamodelBlob
-                .unZip(CommonMimeType.XML)
-                .toClob(UTF_8)
-                .getChars()
-                .toString();
-        
-        final MetamodelDto rightMetamodelDto =  jaxbService.fromXml(MetamodelDto.class, xml);
+        @MemberSupport public Blob act(
+                @ParameterLayout(named = ".txt file name")
+                final String fileName,
 
-        final StringBuilder diff = _DiffExport.toDiff(leftMetamodelDto, rightMetamodelDto);
+                @ParameterLayout(named = "Namespaces",
+                        describedAs="Subset of the complete meta model, only including namespaces starting with given prefix")
+                final List<String> namespaces,
 
-        return Clob.of(fileName, CommonMimeType.TXT, diff)
-                .toBlob(UTF_8)
-                .zip();
+                @ParameterLayout(named = "Ignore Interfaces")
+                @Parameter(optionality=Optionality.MANDATORY)
+                final boolean ignoreInterfaces,
 
-        // ...
-    }
+                @ParameterLayout(named="Metamodel (Zipped XML)",
+                        describedAs="Metamodel from a previous export, to compare the current with.")
+                @Parameter(fileAccept=".zip", optionality = Optionality.MANDATORY)
+                        Blob zippedMetamodelBlob
 
-    
-    @MemberSupport
-    public String validateDownloadMetaModelDiff(
-            final String fileName, 
-            final List<String> namespacePrefixes, 
-            final boolean ignoreInterfaces,
-            final Blob rightMetamodelBlob) {
-        if(namespacePrefixes == null || namespacePrefixes.isEmpty()) {
-            return "At least one package must be selected";
+        ) throws IOException {
+
+            Config config =
+                    new Config()
+                            .withIgnoreNoop()
+                            .withIgnoreAbstractClasses()
+                            .withIgnoreInterfaces()
+                            .withIgnoreBuiltInValueTypes();
+            for (final String namespace : namespaces) {
+                config = config.withNamespacePrefix(namespace);
+            }
+            if(ignoreInterfaces) {
+                config = config.withIgnoreInterfaces();
+            }
+
+            final MetamodelDto leftMetamodelDto =  metaModelService.exportMetaModel(config);
+
+            final String xml = zippedMetamodelBlob
+                    .unZip(CommonMimeType.XML)
+                    .toClob(UTF_8)
+                    .getChars()
+                    .toString();
+
+            final MetamodelDto rightMetamodelDto =  jaxbService.fromXml(MetamodelDto.class, xml);
+
+            final StringBuilder diff = _DiffExport.toDiff(leftMetamodelDto, rightMetamodelDto);
+
+            return Clob.of(fileName, CommonMimeType.TXT, diff)
+                    .toBlob(UTF_8)
+                    .zip();
+
+            // ...
         }
-        return null;
-    }
 
-    @MemberSupport
-    public String default0DownloadMetaModelDiff() {
-        return "metamodel-diff.txt";
-    }
+        @MemberSupport public String validateAct(
+                final String fileName,
+                final List<String> namespacePrefixes,
+                final boolean ignoreInterfaces,
+                final Blob rightMetamodelBlob) {
+            if(namespacePrefixes == null || namespacePrefixes.isEmpty()) {
+                return "At least one package must be selected";
+            }
+            return null;
+        }
 
-    @MemberSupport
-    public List<String> choices1DownloadMetaModelDiff() {
-        return namespaceChoices();
-    }
+        @MemberSupport public String default0Act() { return "metamodel-diff.txt"; }
+        @MemberSupport public List<String> choices1Act() { return namespaceChoices(); }
+        @MemberSupport public boolean default2Act() { return true; }
 
-    @MemberSupport
-    public boolean default2DownloadMetaModelDiff() {
-        return true;
     }
 
     
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/user/ImpersonateMenu.java b/api/applib/src/main/java/org/apache/isis/applib/services/user/ImpersonateMenu.java
index d4c69f0..c09844c 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/user/ImpersonateMenu.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/user/ImpersonateMenu.java
@@ -69,102 +69,106 @@ public class ImpersonateMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleApplib.NAMESPACE_SUDO + ".ImpersonateMenu";   // deliberately not part of isis.applib
 
+    public static abstract class ActionDomainEvent<T> extends IsisModuleApplib.ActionDomainEvent<T> {}
+
+
     final UserService userService;
     final MessageService messageService;
     final List<ImpersonateMenuAdvisor> impersonateMenuAdvisors;
 
 
 
-    public static abstract class ActionDomainEvent extends IsisModuleApplib.ActionDomainEvent<ImpersonateMenu> {}
-
-
-
-    public static class ImpersonateDomainEvent extends ActionDomainEvent { }
-
-    /**
-     * Simple implementation that is surfaced if there is no advisor.
-     *
-     * @param userName
-     */
     @Action(
-            domainEvent = ImpersonateDomainEvent.class,
+            domainEvent = impersonate.ActionEvent.class,
             semantics = SemanticsOf.IDEMPOTENT,
             commandPublishing = Publishing.DISABLED,
             executionPublishing = Publishing.DISABLED,
             restrictTo = RestrictTo.PROTOTYPING
     )
     @ActionLayout(sequence = "100.1", cssClassFa = "fa-mask")
-    public void impersonate(
-            final String userName) {
-
-        // TODO: should use an SPI for each configured viewer to add in its own role if necessary.
-        this.userService.impersonateUser(userName, Collections.singletonList("org.apache.isis.viewer.wicket.roles.USER"), null);
-        this.messageService.informUser("Now impersonating " + userName);
-    }
-    @MemberSupport public boolean hideImpersonate() {
-        return ! this.userService.supportsImpersonation() || !hideImpersonateWithRoles();
-    }
-    @MemberSupport public String disableImpersonate() {
-        return this.userService.isImpersonating() ? "currently impersonating" : null;
-    }
+    public class impersonate {
 
+        public class ActionEvent extends ActionDomainEvent<impersonate.ActionEvent> { }
 
+        /**
+         * Simple implementation that is surfaced if there is no advisor.
+         *
+         * @param userName
+         */
+        @MemberSupport public void act(
+                final String userName) {
 
+            // TODO: should use an SPI for each configured viewer to add in its own role if necessary.
+            userService.impersonateUser(userName, Collections.singletonList("org.apache.isis.viewer.wicket.roles.USER"), null);
+            messageService.informUser("Now impersonating " + userName);
+        }
+        @MemberSupport public boolean hideAct() {
+            return ! userService.supportsImpersonation() || !hideAct();
+        }
+        @MemberSupport public String disableAct() {
+            return userService.isImpersonating() ? "currently impersonating" : null;
+        }
 
+    }
 
-    public static class ImpersonateWithRolesDomainEvent extends ActionDomainEvent { }
 
-    /**
-     * Impersonate a selected user, either using their current roles or
-     * with a specific set of roles.
-     *
-     * <p>
-     * This more sophisticated implementation is only available if there is
-     * an {@link ImpersonateMenuAdvisor} implementation to provide the
-     * choices.
-     * </p>
-     *  @param userName
-     * @param roleNames
-     * @param multiTenancyToken
-     */
     @Action(
-            domainEvent = ImpersonateWithRolesDomainEvent.class,
+            domainEvent = impersonateWithRoles.ActionEvent.class,
             semantics = SemanticsOf.IDEMPOTENT,
             commandPublishing = Publishing.DISABLED,
             executionPublishing = Publishing.DISABLED,
             restrictTo = RestrictTo.PROTOTYPING
     )
     @ActionLayout(sequence = "100.2", cssClassFa = "fa-mask")
-    public void impersonateWithRoles(
-            final String userName,
-            final List<String> roleNames,
-            final String multiTenancyToken) {
-
-        // TODO: should use an SPI for each configured viewer to add in its own role if necessary.
-        val roleNamesCopy = new ArrayList<>(roleNames);
-        if(!roleNamesCopy.contains("org.apache.isis.viewer.wicket.roles.USER")) {
-            roleNamesCopy.add("org.apache.isis.viewer.wicket.roles.USER");
+    public class impersonateWithRoles {
+
+        public class ActionEvent extends ActionDomainEvent<impersonateWithRoles> { }
+
+        /**
+         * Impersonate a selected user, either using their current roles or
+         * with a specific set of roles.
+         *
+         * <p>
+         * This more sophisticated implementation is only available if there is
+         * an {@link ImpersonateMenuAdvisor} implementation to provide the
+         * choices.
+         * </p>
+         *
+         * @param userName - user name
+         * @param roleNames - role names
+         * @param multiTenancyToken - multi-tenancy token
+         */
+        @MemberSupport public void act(
+                final String userName,
+                final List<String> roleNames,
+                final String multiTenancyToken) {
+
+            // TODO: should use an SPI for each configured viewer to add in its own role if necessary.
+            val roleNamesCopy = new ArrayList<>(roleNames);
+            if(!roleNamesCopy.contains("org.apache.isis.viewer.wicket.roles.USER")) {
+                roleNamesCopy.add("org.apache.isis.viewer.wicket.roles.USER");
+            }
+            userService.impersonateUser(userName, roleNamesCopy, multiTenancyToken);
+            messageService.informUser("Now impersonating " + userName);
+        }
+        @MemberSupport public boolean hideAct() {
+            return ! userService.supportsImpersonation() || choices0Act().isEmpty();
+        }
+        @MemberSupport public String disableAct() {
+            return userService.isImpersonating() ? "currently impersonating" : null;
+        }
+        @MemberSupport public List<String> choices0Act() {
+            return impersonateMenuAdvisor().allUserNames();
+        }
+        @MemberSupport public List<String> choices1Act(final String userName) {
+            return impersonateMenuAdvisor().allRoleNames();
+        }
+        @MemberSupport public List<String> default1Act(final String userName) {
+            return impersonateMenuAdvisor().roleNamesFor(userName);
+        }
+        @MemberSupport public String default2Act(final String userName, final List<String> roleNames) {
+            return impersonateMenuAdvisor().multiTenancyTokenFor(userName);
         }
-        this.userService.impersonateUser(userName, roleNamesCopy, multiTenancyToken);
-        this.messageService.informUser("Now impersonating " + userName);
-    }
-    @MemberSupport public boolean hideImpersonateWithRoles() {
-        return ! this.userService.supportsImpersonation() || choices0ImpersonateWithRoles().isEmpty();
-    }
-    @MemberSupport public String disableImpersonateWithRoles() {
-        return this.userService.isImpersonating() ? "currently impersonating" : null;
-    }
-    @MemberSupport public List<String> choices0ImpersonateWithRoles() {
-        return impersonateMenuAdvisor().allUserNames();
-    }
-    @MemberSupport public List<String> choices1ImpersonateWithRoles(final String userName) {
-        return impersonateMenuAdvisor().allRoleNames();
-    }
-    @MemberSupport public List<String> default1ImpersonateWithRoles(final String userName) {
-        return impersonateMenuAdvisor().roleNamesFor(userName);
-    }
-    @MemberSupport public String default2ImpersonateWithRoles(final String userName, final List<String> roleNames) {
-        return impersonateMenuAdvisor().multiTenancyTokenFor(userName);
     }
 
     private ImpersonateMenuAdvisor impersonateMenuAdvisor() {
@@ -172,5 +176,4 @@ public class ImpersonateMenu {
         return impersonateMenuAdvisors.get(0);
     }
 
-
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/user/ImpersonateStopMenu.java b/api/applib/src/main/java/org/apache/isis/applib/services/user/ImpersonateStopMenu.java
index 3aa69e0..6233f14 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/user/ImpersonateStopMenu.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/user/ImpersonateStopMenu.java
@@ -69,26 +69,28 @@ public class ImpersonateStopMenu {
     final MessageService messageService;
 
 
-    public static abstract class ActionDomainEvent extends IsisModuleApplib.ActionDomainEvent<ImpersonateStopMenu> {}
+    public static abstract class ActionDomainEvent<T> extends IsisModuleApplib.ActionDomainEvent<T> {}
 
 
-    public static class StopImpersonatingDomainEvent extends ActionDomainEvent { }
-
     @Action(
-            domainEvent = ImpersonateStopMenu.StopImpersonatingDomainEvent.class,
+            domainEvent = stopImpersonating.ActionEvent.class,
             semantics = SemanticsOf.IDEMPOTENT,
             commandPublishing = Publishing.DISABLED,
             executionPublishing = Publishing.DISABLED,
             restrictTo = RestrictTo.PROTOTYPING
     )
     @ActionLayout(sequence = "100.3", redirectPolicy = Redirect.EVEN_IF_SAME)
-    public void stopImpersonating() {
-        this.userService.stopImpersonating();
-        this.messageService.informUser("No longer impersonating another user");
-    }
-    @MemberSupport
-    public boolean hideStopImpersonating() {
-        return ! isImpersonating();
+    public class stopImpersonating{
+
+        public class ActionEvent extends ActionDomainEvent<stopImpersonating> { }
+
+        @MemberSupport public void act() {
+            userService.stopImpersonating();
+            messageService.informUser("No longer impersonating another user");
+        }
+        @MemberSupport public boolean hideAct() {
+            return ! isImpersonating();
+        }
     }
 
     private boolean isImpersonating() {
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/userui/UserMenu.java b/api/applib/src/main/java/org/apache/isis/applib/services/userui/UserMenu.java
index 8727cc1..3c3ae10 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/userui/UserMenu.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/userui/UserMenu.java
@@ -50,15 +50,13 @@ public class UserMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleApplib.NAMESPACE + ".UserMenu";
 
-    public static abstract class ActionDomainEvent extends IsisModuleApplib.ActionDomainEvent<UserMenu> {}
+    public static abstract class ActionDomainEvent<T> extends IsisModuleApplib.ActionDomainEvent<T> {}
 
     final UserService userService;
 
 
-    public static class MeDomainEvent extends ActionDomainEvent {}
-
     @Action(
-            domainEvent = MeDomainEvent.class,
+            domainEvent = me.ActionEvent.class,
             semantics = SemanticsOf.SAFE
             )
     @ActionLayout(
@@ -66,13 +64,15 @@ public class UserMenu {
             describedAs = "Returns your user account details",
             sequence = "100"
         )
-    public UserMemento me() {
-        return userService.currentUser().orElse(null);
-    }
-    @MemberSupport
-    public String disableMe() {
-        return userService.currentUser().isPresent() ? null : "Current user not available";
-    }
+    public class me {
 
+        public class ActionEvent extends ActionDomainEvent<me> {}
+
+        @MemberSupport public UserMemento act() { return userService.currentUser().orElse(null); }
+        @MemberSupport public String disableAct() {
+            return userService.currentUser().isPresent() ? null : "Current user not available";
+        }
+
+    }
 
 }
diff --git a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/mixins/Object_openOnSecondary.java b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/mixins/Object_openOnSecondary.java
index 61c6e3e..94295ef 100644
--- a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/mixins/Object_openOnSecondary.java
+++ b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/mixins/Object_openOnSecondary.java
@@ -25,6 +25,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.Publishing;
 import org.apache.isis.applib.annotation.RestrictTo;
 import org.apache.isis.applib.annotation.SemanticsOf;
@@ -61,7 +62,10 @@ public class Object_openOnSecondary {
 
     final Object object;
 
-    public URL act() {
+    @Inject PrimaryConfig primaryConfig;
+    @Inject BookmarkService bookmarkService;
+
+    @MemberSupport public URL act() {
         val baseUrlPrefix = lookupBaseUrlPrefix();
         val urlSuffix = bookmarkService.bookmarkForElseFail(object).toString();
 
@@ -71,7 +75,8 @@ public class Object_openOnSecondary {
             throw new RecoverableException(e);
         }
     }
-    public boolean hideAct() {
+
+    @MemberSupport public boolean hideAct() {
         return !primaryConfig.isConfigured();
     }
 
@@ -79,7 +84,5 @@ public class Object_openOnSecondary {
         return primaryConfig.getSecondaryBaseUrlWicket() + "entity/";
     }
 
-    @Inject PrimaryConfig primaryConfig;
-    @Inject BookmarkService bookmarkService;
 
 }
diff --git a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
index ada96b6..fb56ee0 100644
--- a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
+++ b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
@@ -21,12 +21,20 @@ package org.apache.isis.extensions.commandreplay.primary.ui;
 import java.util.List;
 import java.util.UUID;
 
-import org.springframework.lang.Nullable;
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.apache.isis.applib.annotation.*;
+import org.springframework.lang.Nullable;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.ParameterLayout;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.exceptions.RecoverableException;
 import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
 import org.apache.isis.applib.services.jaxb.JaxbService;
@@ -67,11 +75,9 @@ public class CommandReplayOnPrimaryService {
     @Inject final ContentMappingServiceForCommandsDto contentMappingServiceForCommandsDto;
     @Inject final CommandRetrievalService commandRetrievalService;
 
-    public static abstract class ActionDomainEvent
-            extends IsisModuleExtCommandReplayPrimary.ActionDomainEvent<CommandReplayOnPrimaryService> { }
+    public static abstract class ActionDomainEvent<T> extends IsisModuleExtCommandReplayPrimary.ActionDomainEvent<T> { }
 
 
-    public static class FindCommandsDomainEvent extends ActionDomainEvent { }
     public static class NotFoundException extends RecoverableException {
         private static final long serialVersionUID = 1L;
         @Getter
@@ -82,106 +88,122 @@ public class CommandReplayOnPrimaryService {
         }
     }
 
-    /**
-     * These actions should be called with HTTP Accept Header set to:
-     * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.isis.schema.cmd.v1.CommandsDto"</code>
-     *
-     * @param interactionId - to search from.  This transactionId will <i>not</i> be included in the response.
-     * @param batchSize - the maximum number of commands to return.  If not specified, all found will be returned.
-     * @throws NotFoundException - if the command with specified transaction cannot be found.
-     */
-    @Action(domainEvent = FindCommandsDomainEvent.class, semantics = SemanticsOf.SAFE)
+    @Action(domainEvent = findCommands.ActionEvent.class, semantics = SemanticsOf.SAFE)
     @ActionLayout(cssClassFa = "fa-search", sequence="40")
-    public List<? extends CommandModel> findCommands(
-            @Nullable
-            @ParameterLayout(named="Interaction Id")
-            final UUID interactionId,
-            @Nullable
-            @ParameterLayout(named="Batch size")
-            final Integer batchSize)
-            throws NotFoundException {
-        return commandRetrievalService.findCommandsOnPrimaryFrom(interactionId, batchSize);
-    }
-    @MemberSupport public Integer default1FindCommandsOnPrimaryFrom() {
-        return commandRetrievalService.default1FindCommandsOnPrimaryFrom();
+    public class findCommands{
+
+        public class ActionEvent extends ActionDomainEvent<findCommands> { }
+
+        /**
+         * These actions should be called with HTTP Accept Header set to:
+         * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.isis.schema.cmd.v1.CommandsDto"</code>
+         *
+         * @param interactionId - to search from.  This transactionId will <i>not</i> be included in the response.
+         * @param batchSize - the maximum number of commands to return.  If not specified, all found will be returned.
+         * @throws NotFoundException - if the command with specified transaction cannot be found.
+         */
+        @MemberSupport public List<? extends CommandModel> act(
+                @Nullable
+                @ParameterLayout(named="Interaction Id")
+                final UUID interactionId,
+                @Nullable
+                @ParameterLayout(named="Batch size")
+                final Integer batchSize)
+                throws NotFoundException {
+            return commandRetrievalService.findCommandsOnPrimaryFrom(interactionId, batchSize);
+        }
+        @MemberSupport public Integer default1Act() {
+            return commandRetrievalService.default1FindCommandsOnPrimaryFrom();
+        }
+
     }
 
 
 
-    public static class DownloadCommandsDomainEvent extends ActionDomainEvent { }
-    /**
-     * These actions should be called with HTTP Accept Header set to:
-     * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.isis.schema.cmd.v1.CommandsDto"</code>
-     *
-     * @param interactionId - to search from.  This transactionId will <i>not</i> be included in the response.
-     * @param batchSize - the maximum number of commands to return.  If not specified, all found will be returned.
-     * @throws NotFoundException - if the command with specified transaction cannot be found.
-     */
-    @Action(domainEvent = DownloadCommandsDomainEvent.class, semantics = SemanticsOf.SAFE)
+
+    @Action(domainEvent = downloadCommands.ActionEvent.class, semantics = SemanticsOf.SAFE)
     @ActionLayout(cssClassFa = "fa-download", sequence="50")
-    public Clob downloadCommands(
-            @Nullable
-            final UUID interactionId,
-            @Nullable
-            final Integer batchSize,
-            final String filenamePrefix) {
-        final List<? extends CommandModel> commands = commandModelRepository.findSince(interactionId, batchSize);
-        if(commands == null) {
-            messageService.informUser("No commands found");
+    public class downloadCommands {
+
+        public class ActionEvent extends ActionDomainEvent<downloadCommands> { }
+
+        /**
+         * These actions should be called with HTTP Accept Header set to:
+         * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.isis.schema.cmd.v1.CommandsDto"</code>
+         *
+         * @param interactionId - to search from.  This transactionId will <i>not</i> be included in the response.
+         * @param batchSize - the maximum number of commands to return.  If not specified, all found will be returned.
+         * @throws NotFoundException - if the command with specified transaction cannot be found.
+         */
+        @MemberSupport public Clob act(
+                @Nullable
+                final UUID interactionId,
+                @Nullable
+                final Integer batchSize,
+                final String filenamePrefix) {
+            final List<? extends CommandModel> commands = commandModelRepository.findSince(interactionId, batchSize);
+            if(commands == null) {
+                messageService.informUser("No commands found");
+            }
+
+            final CommandsDto commandsDto =
+                    contentMappingServiceForCommandsDto.map(commands);
+
+            final String fileName = String.format(
+                    "%s_%s.xml", filenamePrefix, elseDefault(interactionId));
+
+            final String xml = jaxbService.toXml(commandsDto);
+            return new Clob(fileName, "application/xml", xml);
+        }
+        @MemberSupport public Integer default1Act() {
+            return 25;
+        }
+        @MemberSupport public String default2Act() {
+            return "commands_from";
         }
 
-        final CommandsDto commandsDto =
-                contentMappingServiceForCommandsDto.map(commands);
-
-        final String fileName = String.format(
-                "%s_%s.xml", filenamePrefix, elseDefault(interactionId));
-
-        final String xml = jaxbService.toXml(commandsDto);
-        return new Clob(fileName, "application/xml", xml);
-    }
-    @MemberSupport public Integer default1DownloadCommands() {
-        return 25;
-    }
-    @MemberSupport public String default2DownloadCommands() {
-        return "commands_from";
     }
 
 
 
-    public static class DownloadCommandByIdDomainEvent extends ActionDomainEvent { }
-    /**
-     * This action should be called with HTTP Accept Header set to:
-     * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.isis.schema.cmd.v1.CommandDto"</code>
-     *
-     * @param interactionId - to download.
-     * @throws NotFoundException - if the command with specified transaction cannot be found.
-     */
-    @Action(domainEvent = DownloadCommandByIdDomainEvent.class, semantics = SemanticsOf.SAFE)
+    @Action(domainEvent = downloadCommandById.ActionEvent.class, semantics = SemanticsOf.SAFE)
     @ActionLayout(cssClassFa = "fa-download", sequence="50")
-    public Clob downloadCommandById(
-            final UUID interactionId,
-            final String filenamePrefix) {
+    public class downloadCommandById {
 
-        return commandModelRepository.findByInteractionId(interactionId)
-                .map(commandJdo -> {
+        public class ActionEvent extends ActionDomainEvent<downloadCommandById> { }
 
-                    final CommandDto commandDto = commandJdo.getCommandDto();
+        /**
+         * This action should be called with HTTP Accept Header set to:
+         * <code>application/xml;profile="urn:org.restfulobjects:repr-types/action-result";x-ro-domain-type="org.apache.isis.schema.cmd.v1.CommandDto"</code>
+         *
+         * @param interactionId - to download.
+         * @throws NotFoundException - if the command with specified transaction cannot be found.
+         */
+        @MemberSupport public Clob act(
+                final UUID interactionId,
+                final String filenamePrefix) {
 
-                    final String fileName = String.format(
-                            "%s_%s.xml", filenamePrefix, elseDefault(interactionId));
+            return commandModelRepository.findByInteractionId(interactionId)
+                    .map(commandJdo -> {
 
-                    final String xml = jaxbService.toXml(commandDto);
-                    return new Clob(fileName, "application/xml", xml);
+                        final CommandDto commandDto = commandJdo.getCommandDto();
 
-                }).orElseGet(() -> {
-                    messageService.informUser("No command found");
-                    return null;
-                });
-    }
-    @MemberSupport public String default1DownloadCommandById() {
-        return "command";
-    }
+                        final String fileName = String.format(
+                                "%s_%s.xml", filenamePrefix, elseDefault(interactionId));
 
+                        final String xml = jaxbService.toXml(commandDto);
+                        return new Clob(fileName, "application/xml", xml);
+
+                    }).orElseGet(() -> {
+                        messageService.informUser("No command found");
+                        return null;
+                    });
+        }
+        @MemberSupport public String default1Act() {
+            return "command";
+        }
+
+    }
 
     private static String elseDefault(final UUID uuid) {
         return uuid != null ? uuid.toString() : "00000000-0000-0000-0000-000000000000";
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
index fecbc10..6c3bf0a 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
@@ -59,41 +59,48 @@ public class CommandReplayOnSecondaryService {
     @Inject CommandModelRepository<? extends CommandModel> commandModelRepository;
     @Inject final JaxbService jaxbService;
 
-    public static abstract class ActionDomainEvent
-            extends IsisModuleExtCommandReplaySecondary.ActionDomainEvent<CommandReplayOnSecondaryService> { }
+    public static abstract class ActionDomainEvent<T> extends IsisModuleExtCommandReplaySecondary.ActionDomainEvent<T> { }
 
-    public static class FindMostRecentReplayedDomainEvent extends ActionDomainEvent { }
-    @Action(domainEvent = FindMostRecentReplayedDomainEvent.class, semantics = SemanticsOf.SAFE)
+    @Action(domainEvent = findMostRecentReplayed.ActionEvent.class, semantics = SemanticsOf.SAFE)
     @ActionLayout(cssClassFa = "fa-bath", sequence="60.1")
-    public CommandModel findMostRecentReplayed() {
-        return commandModelRepository.findMostRecentReplayed().orElse(null);
+    public class findMostRecentReplayed{
+
+        public class ActionEvent extends ActionDomainEvent<findMostRecentReplayed> { }
+
+        @MemberSupport public CommandModel act() {
+            return commandModelRepository.findMostRecentReplayed().orElse(null);
+        }
     }
 
-    public static class UploadCommandsDomainEvent extends ActionDomainEvent { }
+
     @Action(
-        domainEvent = UploadCommandsDomainEvent.class,
+        domainEvent = uploadCommands.ActionEvent.class,
         semantics = SemanticsOf.NON_IDEMPOTENT
     )
     @ActionLayout(cssClassFa = "fa-upload", sequence="60.2")
-    public void uploadCommands(final Clob commandsDtoAsXml) {
-        val chars = commandsDtoAsXml.getChars();
-        List<CommandDto> commandDtoList;
+    public class uploadCommands{
 
-        try {
-            val commandsDto = jaxbService.fromXml(CommandsDto.class, chars.toString());
-            commandDtoList = commandsDto.getCommandDto();
+        public class ActionEvent extends ActionDomainEvent<uploadCommands> { }
 
-        } catch(Exception ex) {
-            val commandDto = jaxbService.fromXml(CommandDto.class, chars.toString());
-            commandDtoList = Collections.singletonList(commandDto);
-        }
+        @MemberSupport public void act(final Clob commandsDtoAsXml) {
+            val chars = commandsDtoAsXml.getChars();
+            List<CommandDto> commandDtoList;
 
-        for (final CommandDto commandDto : commandDtoList) {
-            commandModelRepository.saveForReplay(commandDto);
-        }
-    }
+            try {
+                val commandsDto = jaxbService.fromXml(CommandsDto.class, chars.toString());
+                commandDtoList = commandsDto.getCommandDto();
 
+            } catch(Exception ex) {
+                val commandDto = jaxbService.fromXml(CommandDto.class, chars.toString());
+                commandDtoList = Collections.singletonList(commandDto);
+            }
 
+            for (final CommandDto commandDto : commandDtoList) {
+                commandModelRepository.saveForReplay(commandDto);
+            }
+        }
+
+    }
 
 }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/app/mixins/ApplicationOrphanedPermissionManager_relocateSelected.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/app/mixins/ApplicationOrphanedPermissionManager_relocateSelected.java
index d199477..a45c540 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/app/mixins/ApplicationOrphanedPermissionManager_relocateSelected.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/app/mixins/ApplicationOrphanedPermissionManager_relocateSelected.java
@@ -26,6 +26,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Parameter;
 import org.apache.isis.applib.annotation.SemanticsOf;
@@ -59,7 +60,7 @@ public class ApplicationOrphanedPermissionManager_relocateSelected {
 
     private final ApplicationOrphanedPermissionManager target;
 
-    public ApplicationOrphanedPermissionManager act(
+    @MemberSupport public ApplicationOrphanedPermissionManager act(
             final Collection<ApplicationPermission> permissions,
             @Parameter(optionality = Optionality.MANDATORY)
             final String targetNamespace) {
@@ -68,7 +69,7 @@ public class ApplicationOrphanedPermissionManager_relocateSelected {
         return target;
     }
 
-    public Collection<String> choices1Act() {
+    @MemberSupport public Collection<String> choices1Act() {
         return featureRepository.allNamespaces().stream()
                     .map(ApplicationFeature::getFullyQualifiedName)
                     .collect(Collectors.toCollection(TreeSet::new));
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_allow.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_allow.java
index 908e57b..565e206 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_allow.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_allow.java
@@ -20,6 +20,7 @@ package org.apache.isis.extensions.secman.applib.permission.dom.mixins;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
 import org.apache.isis.extensions.secman.applib.permission.dom.ApplicationPermission;
@@ -44,12 +45,12 @@ public class ApplicationPermission_allow {
 
     private final ApplicationPermission target;
 
-    public ApplicationPermission act() {
+    @MemberSupport public ApplicationPermission act() {
         target.setRule(ApplicationPermissionRule.ALLOW);
         return target;
     }
 
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return target.getRule() == ApplicationPermissionRule.ALLOW? "Rule is already set to ALLOW": null;
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_changing.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_changing.java
index 0741536..7682935 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_changing.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_changing.java
@@ -20,6 +20,7 @@ package org.apache.isis.extensions.secman.applib.permission.dom.mixins;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
 import org.apache.isis.extensions.secman.applib.permission.dom.ApplicationPermission;
@@ -44,12 +45,12 @@ public class ApplicationPermission_changing {
 
     private final ApplicationPermission target;
 
-    public ApplicationPermission act() {
+    @MemberSupport public ApplicationPermission act() {
         target.setMode(ApplicationPermissionMode.CHANGING);
         return target;
     }
 
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return target.getMode() == ApplicationPermissionMode.CHANGING ? "Mode is already set to CHANGING": null;
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_delete.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_delete.java
index e47a51b..77f3d38 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_delete.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_delete.java
@@ -22,6 +22,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
@@ -51,7 +52,7 @@ public class ApplicationPermission_delete {
 
     private final ApplicationPermission target;
 
-    public ApplicationRole act() {
+    @MemberSupport public ApplicationRole act() {
         val owningRole = target.getRole();
         repository.remove(target);
         return owningRole;
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_feature.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_feature.java
index 8aea7aa..bacd442 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_feature.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_feature.java
@@ -20,15 +20,15 @@ package org.apache.isis.extensions.secman.applib.permission.dom.mixins;
 
 import javax.inject.Inject;
 
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.PropertyLayout;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
+import org.apache.isis.applib.services.appfeatui.ApplicationFeatureViewModel;
 import org.apache.isis.applib.services.factory.FactoryService;
-import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
-import org.apache.isis.applib.services.appfeatui.ApplicationFeatureViewModel;
 import org.apache.isis.extensions.secman.applib.permission.dom.ApplicationPermission;
 
 import lombok.RequiredArgsConstructor;
@@ -48,7 +48,10 @@ public class ApplicationPermission_feature {
 
     final ApplicationPermission target;
 
-    public ApplicationFeatureViewModel prop(final ApplicationPermission permission) {
+    @Inject FactoryService factory;
+    @Inject ApplicationFeatureRepository featureRepository;
+
+    @MemberSupport public ApplicationFeatureViewModel prop(final ApplicationPermission permission) {
         if(permission.getFeatureSort() == null) {
             return null;
         }
@@ -60,8 +63,5 @@ public class ApplicationPermission_feature {
         return ApplicationFeatureId.newFeature(permission.getFeatureSort(), permission.getFeatureFqn());
     }
 
-    @Inject RepositoryService repository;
-    @Inject FactoryService factory;
-    @Inject ApplicationFeatureRepository featureRepository;
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_updateRole.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_updateRole.java
index 09f11cb..3810bc2 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_updateRole.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_updateRole.java
@@ -54,19 +54,13 @@ public class ApplicationPermission_updateRole {
 
     private final ApplicationPermission target;
 
-    @MemberSupport
-    public ApplicationPermission act(final ApplicationRole applicationRole) {
+    @MemberSupport public ApplicationPermission act(final ApplicationRole applicationRole) {
         target.setRole(applicationRole);
         return target;
     }
 
-    @MemberSupport
-    public ApplicationRole default0Act() {
-        return target.getRole();
-    }
-
-    @MemberSupport
-    public Collection<? extends ApplicationRole> choices0Act() {
+    @MemberSupport public ApplicationRole default0Act() { return target.getRole(); }
+    @MemberSupport public Collection<? extends ApplicationRole> choices0Act() {
         return applicationRoleRepository.allRoles();
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_veto.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_veto.java
index 8c944a9..a9cf372 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_veto.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_veto.java
@@ -20,6 +20,7 @@ package org.apache.isis.extensions.secman.applib.permission.dom.mixins;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
 import org.apache.isis.extensions.secman.applib.permission.dom.ApplicationPermission;
@@ -44,11 +45,12 @@ public class ApplicationPermission_veto {
 
     private final ApplicationPermission target;
 
-    public ApplicationPermission act() {
+    @MemberSupport public ApplicationPermission act() {
         target.setRule(ApplicationPermissionRule.VETO);
         return target;
     }
-    public String disableAct() {
+
+    @MemberSupport public String disableAct() {
         return target.getRule() == ApplicationPermissionRule.VETO? "Rule is already set to VETO": null;
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_viewing.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_viewing.java
index ea4c2ec..579ddbf 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_viewing.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/dom/mixins/ApplicationPermission_viewing.java
@@ -20,6 +20,7 @@ package org.apache.isis.extensions.secman.applib.permission.dom.mixins;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
 import org.apache.isis.extensions.secman.applib.permission.dom.ApplicationPermission;
@@ -44,12 +45,12 @@ public class ApplicationPermission_viewing {
 
     private final ApplicationPermission target;
 
-    public ApplicationPermission act() {
+    @MemberSupport public ApplicationPermission act() {
         target.setMode(ApplicationPermissionMode.VIEWING);
         return target;
     }
 
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return target.getMode() == ApplicationPermissionMode.VIEWING ? "Mode is already set to VIEWING": null;
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/menu/ApplicationPermissionMenu.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/menu/ApplicationPermissionMenu.java
index ec390f2..3b8a677 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/menu/ApplicationPermissionMenu.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/permission/menu/ApplicationPermissionMenu.java
@@ -26,6 +26,7 @@ import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
@@ -50,45 +51,47 @@ public class ApplicationPermissionMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleExtSecmanApplib.NAMESPACE + ".ApplicationPermissionMenu";
 
-    // -- domain event classes
-    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApplib.PropertyDomainEvent<ApplicationPermissionMenu, T> {}
-    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApplib.CollectionDomainEvent<ApplicationPermissionMenu, T> {}
-    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApplib.ActionDomainEvent<ApplicationPermissionMenu> {}
+    public static abstract class ActionDomainEvent<T> extends IsisModuleExtSecmanApplib.ActionDomainEvent<T> {}
 
     @Inject private ApplicationPermissionRepository applicationPermissionRepository;
     @Inject private FactoryService factoryService;
 
-    // -- iconName
-    @ObjectSupport
-    public String iconName() {
+
+    @ObjectSupport public String iconName() {
         return "applicationPermission";
     }
 
 
-    // -- findOrphanedPermissions (action)
-    public static class FindOrphanedPermissionsDomainEvent extends ActionDomainEvent {}
-
     @Action(
-            domainEvent=FindOrphanedPermissionsDomainEvent.class,
+            domainEvent= findOrphanedPermissions.ActionEvent.class,
             semantics = SemanticsOf.SAFE
             )
     @ActionLayout(sequence = "100.50.1")
-    public ApplicationOrphanedPermissionManager findOrphanedPermissions() {
-        return factoryService.viewModel(new ApplicationOrphanedPermissionManager());
+    public class findOrphanedPermissions{
+
+        public class ActionEvent extends ActionDomainEvent<findOrphanedPermissions> {}
+
+        @MemberSupport public ApplicationOrphanedPermissionManager act() {
+            return factoryService.viewModel(new ApplicationOrphanedPermissionManager());
+        }
     }
 
 
-    // -- allPermissions (action)
-    public static class AllPermissionsDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent=AllPermissionsDomainEvent.class,
+            domainEvent= allPermissions.ActionEvent.class,
             semantics = SemanticsOf.SAFE,
             restrictTo = RestrictTo.PROTOTYPING
             )
     @ActionLayout(sequence = "100.50.2")
-    public Collection<? extends ApplicationPermission> allPermissions() {
-        return applicationPermissionRepository.allPermissions();
+    public class allPermissions {
+
+        public class ActionEvent extends ActionDomainEvent<allPermissions> {}
+
+        @MemberSupport public Collection<? extends ApplicationPermission> act() {
+            return applicationPermissionRepository.allPermissions();
+        }
+
     }
 
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_addPermission.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_addPermission.java
index ffc00d6..477819c 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_addPermission.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_addPermission.java
@@ -85,18 +85,9 @@ public class ApplicationRole_addPermission {
         return target;
     }
 
-    @MemberSupport
-    public ApplicationPermissionRule defaultRule(Parameters params) {
-        return ApplicationPermissionRule.ALLOW;
-    }
-
-    @MemberSupport
-    public ApplicationPermissionMode defaultMode(Parameters params) {
-        return ApplicationPermissionMode.CHANGING;
-    }
-
-    @MemberSupport
-    public java.util.Collection<ApplicationFeatureChoices.AppFeat> autoCompleteFeature(
+    @MemberSupport public ApplicationPermissionRule defaultRule(Parameters params) { return ApplicationPermissionRule.ALLOW; }
+    @MemberSupport public ApplicationPermissionMode defaultMode(Parameters params) { return ApplicationPermissionMode.CHANGING; }
+    @MemberSupport public java.util.Collection<ApplicationFeatureChoices.AppFeat> autoCompleteFeature(
             final Parameters params,
             final @MinLength(3) String search) {
         return applicationFeatureChoices.autoCompleteFeature(search);
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_addUser.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_addUser.java
index 0e6049b..c9d97f0 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_addUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_addUser.java
@@ -57,14 +57,12 @@ public class ApplicationRole_addUser {
 
     private final ApplicationRole target;
 
-    @MemberSupport
-    public ApplicationRole act(final ApplicationUser applicationUser) {
+    @MemberSupport public ApplicationRole act(final ApplicationUser applicationUser) {
         applicationRoleRepository.addRoleToUser(target, applicationUser);
         return target;
     }
 
-    @MemberSupport
-    public List<? extends ApplicationUser> autoComplete0Act(final String search) {
+    @MemberSupport public List<? extends ApplicationUser> autoComplete0Act(final String search) {
         final Collection<? extends ApplicationUser> matchingSearch = applicationUserRepository.find(search);
         final List<? extends ApplicationUser> list = _Lists.newArrayList(matchingSearch);
         list.removeAll(applicationUserRepository.findByRole(target));
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_delete.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_delete.java
index 4826f2b..b05da68 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_delete.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_delete.java
@@ -52,14 +52,12 @@ public class ApplicationRole_delete {
 
     private final ApplicationRole holder;
 
-    @MemberSupport
-    public Collection<ApplicationRole> act() {
+    @MemberSupport public Collection<ApplicationRole> act() {
         applicationRoleRepository.deleteRole(holder);
         return applicationRoleRepository.allRoles();
     }
 
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return applicationRoleRepository.isAdminRole(holder) ? "Cannot delete the admin role" : null;
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_removePermissions.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_removePermissions.java
index f0bbb79..e7a8d36 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_removePermissions.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_removePermissions.java
@@ -64,8 +64,7 @@ public class ApplicationRole_removePermissions {
 
     private final ApplicationRole target;
 
-    @MemberSupport
-    public ApplicationRole act(Collection<ApplicationPermission> permissions) {
+    @MemberSupport public ApplicationRole act(Collection<ApplicationPermission> permissions) {
 
         _NullSafe.stream(permissions)
         .filter(this::canRemove)
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_removeUsers.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_removeUsers.java
index 7714491..014541f 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_removeUsers.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_removeUsers.java
@@ -24,6 +24,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.Domain;
 import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.message.MessageService;
@@ -59,8 +60,7 @@ public class ApplicationRole_removeUsers {
 
     private final ApplicationRole target;
 
-    @MemberSupport
-    public ApplicationRole act(Collection<ApplicationUser> users) {
+    @MemberSupport public ApplicationRole act(Collection<ApplicationUser> users) {
 
         _NullSafe.stream(users)
         .filter(this::canRemove)
@@ -69,7 +69,8 @@ public class ApplicationRole_removeUsers {
         return target;
     }
 
-    public boolean canRemove(ApplicationUser applicationUser) {
+    @Domain.Exclude
+    boolean canRemove(ApplicationUser applicationUser) {
         if(applicationUserRepository.isAdminUser(applicationUser)
                 && applicationRoleRepository.isAdminRole(target)) {
             messageService.warnUser("Cannot remove admin user from the admin role.");
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_updateDescription.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_updateDescription.java
index 8c740a5..67c925a 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_updateDescription.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_updateDescription.java
@@ -46,8 +46,7 @@ public class ApplicationRole_updateDescription {
 
     private final ApplicationRole target;
 
-    @MemberSupport
-    public ApplicationRole act(
+    @MemberSupport public ApplicationRole act(
             @ApplicationRole.Description
             final String description) {
 
@@ -55,10 +54,7 @@ public class ApplicationRole_updateDescription {
         return target;
     }
 
-    @MemberSupport
-    public String default0Act() {
-        return target.getDescription();
-    }
+    @MemberSupport public String default0Act() { return target.getDescription(); }
 
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_updateName.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_updateName.java
index dac7af0..2dae1ac 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_updateName.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/mixins/ApplicationRole_updateName.java
@@ -46,8 +46,7 @@ public class ApplicationRole_updateName {
 
     private final ApplicationRole target;
 
-    @MemberSupport
-    public ApplicationRole act(
+    @MemberSupport public ApplicationRole act(
             @ApplicationRole.Name
             final String name) {
 
@@ -55,9 +54,6 @@ public class ApplicationRole_updateName {
         return target;
     }
 
-    @MemberSupport
-    public String default0Act() {
-        return target.getName();
-    }
+    @MemberSupport public String default0Act() { return target.getName(); }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/menu/ApplicationRoleMenu.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/menu/ApplicationRoleMenu.java
index b752182..18af54c 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/menu/ApplicationRoleMenu.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/menu/ApplicationRoleMenu.java
@@ -26,6 +26,7 @@ import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.applib.annotation.Optionality;
@@ -53,66 +54,71 @@ public class ApplicationRoleMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleExtSecmanApplib.NAMESPACE + ".ApplicationRoleMenu";
 
-    // -- domain event classes
-    public static class PropertyDomainEvent<T> extends IsisModuleExtSecmanApplib.PropertyDomainEvent<ApplicationRoleMenu, T> {}
-    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApplib.CollectionDomainEvent<ApplicationRoleMenu, T> {}
-    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApplib.ActionDomainEvent<ApplicationRoleMenu> {}
+    public static abstract class ActionDomainEvent<T> extends IsisModuleExtSecmanApplib.ActionDomainEvent<T> {}
 
     private final ApplicationRoleRepository applicationRoleRepository;
 
 
-    // -- iconName
-    @ObjectSupport
-    public String iconName() {
+    @ObjectSupport public String iconName() {
         return "applicationRole";
     }
 
 
-    // -- findRoles
-    public static class FindRolesDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = FindRolesDomainEvent.class,
+            domainEvent = findRoles.ActionEvent.class,
             semantics = SemanticsOf.SAFE
             )
     @ActionLayout(sequence = "100.20.1")
-    public Collection<? extends ApplicationRole> findRoles(
-            @Parameter(maxLength = ApplicationRole.Name.MAX_LENGTH)
-            @ParameterLayout(named = "Search", typicalLength = ApplicationRole.Name.TYPICAL_LENGTH)
-            final String search) {
-        return applicationRoleRepository.findNameContaining(search);
-    }
+    public class findRoles {
+
+        public class ActionEvent extends ActionDomainEvent<findRoles> {}
 
+        @MemberSupport public Collection<? extends ApplicationRole> act(
+                @Parameter(maxLength = ApplicationRole.Name.MAX_LENGTH)
+                @ParameterLayout(named = "Search", typicalLength = ApplicationRole.Name.TYPICAL_LENGTH)
+                final String search) {
+            return applicationRoleRepository.findNameContaining(search);
+        }
+
+    }
 
-    // -- newRole
-    public static class NewRoleDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = NewRoleDomainEvent.class,
+            domainEvent = newRole.ActionEvent.class,
             semantics = SemanticsOf.IDEMPOTENT
             )
     @ActionLayout(sequence = "100.20.2")
-    public ApplicationRole newRole(
-            @Parameter(maxLength = ApplicationRole.Name.MAX_LENGTH)
-            @ParameterLayout(named="Name", typicalLength= ApplicationRole.Name.TYPICAL_LENGTH)
-            final String name,
-            @Parameter(maxLength = ApplicationRole.Description.MAX_LENGTH, optionality = Optionality.OPTIONAL)
-            @ParameterLayout(named="Description", typicalLength= ApplicationRole.Description.TYPICAL_LENGTH)
-            final String description) {
-        return applicationRoleRepository.newRole(name, description);
+    public class newRole{
+
+        public class ActionEvent extends ActionDomainEvent<newRole> {}
+
+        @MemberSupport public ApplicationRole act (
+                @Parameter(maxLength = ApplicationRole.Name.MAX_LENGTH)
+                @ParameterLayout(named="Name", typicalLength= ApplicationRole.Name.TYPICAL_LENGTH)
+                final String name,
+                @Parameter(maxLength = ApplicationRole.Description.MAX_LENGTH, optionality = Optionality.OPTIONAL)
+                @ParameterLayout(named="Description", typicalLength= ApplicationRole.Description.TYPICAL_LENGTH)
+                final String description) {
+            return applicationRoleRepository.newRole(name, description);
+        }
     }
 
 
-    // -- allRoles
-    public static class AllRolesDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = AllRolesDomainEvent.class,
+            domainEvent = allRoles.ActionEvent.class,
             semantics = SemanticsOf.SAFE
             )
     @ActionLayout(sequence = "100.20.3")
-    public Collection<? extends ApplicationRole> allRoles() {
-        return applicationRoleRepository.allRoles();
+    public class allRoles {
+
+        public class ActionEvent extends ActionDomainEvent<allRoles> {}
+
+        @MemberSupport public Collection<? extends ApplicationRole> act() {
+            return applicationRoleRepository.allRoles();
+        }
+
     }
 
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_addChild.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_addChild.java
index 24564d9..3aafeb2 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_addChild.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_addChild.java
@@ -50,8 +50,7 @@ public class ApplicationTenancy_addChild {
 
     private final ApplicationTenancy target;
 
-    @MemberSupport
-    public ApplicationTenancy act(final ApplicationTenancy child) {
+    @MemberSupport public ApplicationTenancy act(final ApplicationTenancy child) {
         applicationTenancyRepository.setParentOnTenancy(child, target);
         return target;
     }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_addUser.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_addUser.java
index c3c0611..f1211e4 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_addUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_addUser.java
@@ -57,14 +57,12 @@ public class ApplicationTenancy_addUser {
 
     private final ApplicationTenancy target;
 
-    @MemberSupport
-    public ApplicationTenancy act(final ApplicationUser applicationUser) {
+    @MemberSupport public ApplicationTenancy act(final ApplicationUser applicationUser) {
         applicationTenancyRepository.setTenancyOnUser(target, applicationUser);
         return target;
     }
 
-    @MemberSupport
-    public List<? extends ApplicationUser> autoComplete0Act(final String search) {
+    @MemberSupport public List<? extends ApplicationUser> autoComplete0Act(final String search) {
         final Collection<? extends ApplicationUser> matchingSearch = applicationUserRepository.find(search);
         final List<? extends ApplicationUser> list = _Lists.newArrayList(matchingSearch);
         list.removeAll(applicationUserRepository.findByTenancy(target));
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_delete.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_delete.java
index 7f7f100..ae7284c 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_delete.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_delete.java
@@ -61,8 +61,7 @@ public class ApplicationTenancy_delete {
     private final ApplicationTenancy target;
 
 
-    @MemberSupport
-    public Collection<? extends ApplicationTenancy> act() {
+    @MemberSupport public Collection<? extends ApplicationTenancy> act() {
         for (val user : applicationUserRepository.findByTenancy(target)) {
             val updateAtPathMixin = factoryService.mixin(ApplicationUser_updateAtPath.class, user);
             updateAtPathMixin.act(null);
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_removeChild.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_removeChild.java
index 5766eac..6593b9a 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_removeChild.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_removeChild.java
@@ -52,20 +52,14 @@ public class ApplicationTenancy_removeChild {
 
     private final ApplicationTenancy target;
 
-    @MemberSupport
-    public ApplicationTenancy act(final ApplicationTenancy child) {
+    @MemberSupport public ApplicationTenancy act(final ApplicationTenancy child) {
         applicationTenancyRepository.clearParentOnTenancy(child);
         return target;
     }
 
-    @MemberSupport
-    public Collection<? extends ApplicationTenancy> choices0Act() {
+    @MemberSupport public Collection<? extends ApplicationTenancy> choices0Act() {
         return applicationTenancyRepository.getChildren(target);
     }
-
-    @MemberSupport
-    public String disableAct() {
-        return choices0Act().isEmpty()? "No children to remove": null;
-    }
+    @MemberSupport public String disableAct() { return choices0Act().isEmpty()? "No children to remove": null; }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_removeUser.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_removeUser.java
index 83777c0..9dccc55 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_removeUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_removeUser.java
@@ -55,20 +55,14 @@ public class ApplicationTenancy_removeUser {
 
     private final ApplicationTenancy target;
 
-    @MemberSupport
-    public ApplicationTenancy act(final ApplicationUser applicationUser) {
+    @MemberSupport public ApplicationTenancy act(final ApplicationUser applicationUser) {
         applicationTenancyRepository.clearTenancyOnUser(applicationUser);
         return target;
     }
 
-    @MemberSupport
-    public Collection<? extends ApplicationUser> choices0Act() {
+    @MemberSupport public Collection<? extends ApplicationUser> choices0Act() {
         return applicationUserRepository.findByTenancy(target);
     }
-
-    @MemberSupport
-    public String disableAct() {
-        return choices0Act().isEmpty()? "No users to remove": null;
-    }
+    @MemberSupport public String disableAct() { return choices0Act().isEmpty()? "No users to remove": null; }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_updateName.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_updateName.java
index da19bde..2ff8190 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_updateName.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_updateName.java
@@ -46,17 +46,13 @@ public class ApplicationTenancy_updateName {
 
     private final ApplicationTenancy target;
 
-    @MemberSupport
-    public ApplicationTenancy act(
+    @MemberSupport public ApplicationTenancy act(
             @ApplicationTenancy.Name
             final String name) {
         target.setName(name);
         return target;
     }
 
-    @MemberSupport
-    public String default0Act() {
-        return target.getName();
-    }
+    @MemberSupport public String default0Act() { return target.getName(); }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_users.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_users.java
index e24dff6..9d0dea6 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_users.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/dom/mixins/ApplicationTenancy_users.java
@@ -22,6 +22,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.extensions.secman.applib.tenancy.dom.ApplicationTenancy;
 import org.apache.isis.extensions.secman.applib.tenancy.dom.ApplicationTenancy.CollectionDomainEvent;
 import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUser;
@@ -45,7 +46,7 @@ public class ApplicationTenancy_users {
     public static class DomainEvent
             extends CollectionDomainEvent<ApplicationUser> {}
 
-    public java.util.Collection<ApplicationUser> coll() {
+    @MemberSupport public java.util.Collection<ApplicationUser> coll() {
         return applicationUserRepository.findByAtPath(target.getPath());
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/menu/ApplicationTenancyMenu.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/menu/ApplicationTenancyMenu.java
index ffb8646..6584e26 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/menu/ApplicationTenancyMenu.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/tenancy/menu/ApplicationTenancyMenu.java
@@ -26,6 +26,7 @@ import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.MinLength;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.ObjectSupport;
@@ -52,71 +53,76 @@ public class ApplicationTenancyMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleExtSecmanApplib.NAMESPACE + ".ApplicationTenancyMenu";
 
-    // -- domain event classes
-    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApplib.PropertyDomainEvent<ApplicationTenancyMenu, T> {}
-    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApplib.CollectionDomainEvent<ApplicationTenancyMenu, T> {}
-    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApplib.ActionDomainEvent<ApplicationTenancyMenu> {}
+    public static abstract class ActionDomainEvent<T> extends IsisModuleExtSecmanApplib.ActionDomainEvent<T> {}
 
     @Inject private ApplicationTenancyRepository applicationTenancyRepository;
 
-    // -- iconName
+
     @ObjectSupport
     public String iconName() {
         return "applicationTenancy";
     }
 
 
-    // -- findTenancies
-    public static class FindTenanciesDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = FindTenanciesDomainEvent.class,
+            domainEvent = findTenancies.ActionEvent.class,
             semantics = SemanticsOf.SAFE
             )
     @ActionLayout(sequence = "100.30.1")
-    public Collection<? extends ApplicationTenancy> findTenancies(
-            @Parameter(optionality = Optionality.OPTIONAL)
-            @ParameterLayout(named = "Partial Name Or Path", describedAs = "String to search for, wildcard (*) can be used")
-            @MinLength(1) // for auto-complete
-            final String partialNameOrPath) {
-        return applicationTenancyRepository.findByNameOrPathMatchingCached(partialNameOrPath);
+    public class findTenancies{
+
+        public class ActionEvent extends ActionDomainEvent<findTenancies> {}
+
+        @MemberSupport public Collection<? extends ApplicationTenancy> act(
+                @Parameter(optionality = Optionality.OPTIONAL)
+                @ParameterLayout(named = "Partial Name Or Path", describedAs = "String to search for, wildcard (*) can be used")
+                @MinLength(1) // for auto-complete
+                final String partialNameOrPath) {
+            return applicationTenancyRepository.findByNameOrPathMatchingCached(partialNameOrPath);
+        }
     }
 
 
-    // -- newTenancy
-    public static class NewTenancyDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = NewTenancyDomainEvent.class,
+            domainEvent = newTenancy.ActionEvent.class,
             semantics = SemanticsOf.IDEMPOTENT
             )
     @ActionLayout(sequence = "100.30.3")
-    public ApplicationTenancy newTenancy(
-            @Parameter(maxLength = ApplicationTenancy.Name.MAX_LENGTH)
-            @ParameterLayout(named = "Name", typicalLength = ApplicationTenancy.Name.TYPICAL_LENGTH)
-            final String name,
-            @Parameter(maxLength = ApplicationTenancy.Path.MAX_LENGTH)
-            @ParameterLayout(named = "Path")
-            final String path,
-            @Parameter(optionality = Optionality.OPTIONAL)
-            @ParameterLayout(named = "Parent")
-            final ApplicationTenancy parent) {
-        return applicationTenancyRepository.newTenancy(name, path, parent);
-    }
+    public class newTenancy{
+
+        public class ActionEvent extends ActionDomainEvent<newTenancy> {}
+
+        @MemberSupport public ApplicationTenancy act(
+                @Parameter(maxLength = ApplicationTenancy.Name.MAX_LENGTH)
+                @ParameterLayout(named = "Name", typicalLength = ApplicationTenancy.Name.TYPICAL_LENGTH)
+                final String name,
+                @Parameter(maxLength = ApplicationTenancy.Path.MAX_LENGTH)
+                @ParameterLayout(named = "Path")
+                final String path,
+                @Parameter(optionality = Optionality.OPTIONAL)
+                @ParameterLayout(named = "Parent")
+                final ApplicationTenancy parent) {
+            return applicationTenancyRepository.newTenancy(name, path, parent);
+        }
 
+    }
 
-    // -- allTenancies
-    public static class AllTenanciesDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = AllTenanciesDomainEvent.class,
+            domainEvent = allTenancies.ActionEvent.class,
             semantics = SemanticsOf.SAFE,
             restrictTo = RestrictTo.PROTOTYPING
             )
     @ActionLayout(sequence = "100.30.4")
-    public Collection<? extends ApplicationTenancy> allTenancies() {
-        return applicationTenancyRepository.allTenancies();
-    }
+    public class allTenancies{
 
+        public class ActionEvent extends ActionDomainEvent<allTenancies> {}
+
+        @MemberSupport public Collection<? extends ApplicationTenancy> act() {
+            return applicationTenancyRepository.allTenancies();
+        }
+    }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/ApplicationUserManager.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/ApplicationUserManager.java
index c43c0ac..5b598b5 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/ApplicationUserManager.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/ApplicationUserManager.java
@@ -20,7 +20,7 @@ package org.apache.isis.extensions.secman.applib.user.app;
 
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Nature;
-import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
 
 @DomainObject(
@@ -31,8 +31,7 @@ public class ApplicationUserManager {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleExtSecmanApplib.NAMESPACE + ".ApplicationUserManager";
 
-    @Title
-    public String title() {
+    @ObjectSupport public String title() {
         return "Application User Manager";
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_allUsers.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_allUsers.java
index 6d59d29..176dd4c 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_allUsers.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_allUsers.java
@@ -39,8 +39,7 @@ public class ApplicationUserManager_allUsers {
     @Inject
     private ApplicationUserRepository applicationUserRepository;
 
-    @MemberSupport
-    public Collection<ApplicationUser> coll() {
+    @MemberSupport public Collection<ApplicationUser> coll() {
         return applicationUserRepository.allUsers();
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_newDelegateUser.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_newDelegateUser.java
index 0a53610..f4e6b99 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_newDelegateUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_newDelegateUser.java
@@ -59,8 +59,7 @@ public class ApplicationUserManager_newDelegateUser {
 
     private final ApplicationUserManager target;
 
-    @MemberSupport
-    public ApplicationUserManager act(
+    @MemberSupport public ApplicationUserManager act(
 
           @Parameter(maxLength = ApplicationUser.Username.MAX_LENGTH)
           @ParameterLayout(named = "Name")
@@ -86,8 +85,7 @@ public class ApplicationUserManager_newDelegateUser {
         return target;
     }
 
-    @MemberSupport
-    public ApplicationRole default1Act() {
+    @MemberSupport public ApplicationRole default1Act() {
         return applicationRoleRepository
                 .findByNameCached(configBean.getRegularUserRoleName())
                 .orElse(null);
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_newLocalUser.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_newLocalUser.java
index 90d4b14..3936b57 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_newLocalUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_newLocalUser.java
@@ -66,8 +66,7 @@ extends ApplicationUserManager_newLocalUserAbstract {
 
     private final ApplicationUserManager target;
 
-    @MemberSupport
-    public ApplicationUserManager act(
+    @MemberSupport public ApplicationUserManager act(
           @Parameter(maxLength = ApplicationUser.Username.MAX_LENGTH)
           @ParameterLayout(named = "Name")
           final String username,
@@ -108,8 +107,7 @@ extends ApplicationUserManager_newLocalUserAbstract {
         return target;
     }
 
-    @MemberSupport
-    public String validateAct(
+    @MemberSupport public String validateAct(
             final String username,
             final Password newPassword,
             final Password newPasswordRepeat,
@@ -124,8 +122,7 @@ extends ApplicationUserManager_newLocalUserAbstract {
         return null;
     }
 
-    @MemberSupport
-    public ApplicationRole default3Act() {
+    @MemberSupport public ApplicationRole default3Act() {
         return applicationRoleRepository
                 .findByNameCached(configBean.getRegularUserRoleName())
                 .orElse(null);
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/contributions/HasUsername_open.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/contributions/HasUsername_open.java
index af9026e..d5879c1 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/contributions/HasUsername_open.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/contributions/HasUsername_open.java
@@ -22,6 +22,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.mixins.security.HasUsername;
 import org.apache.isis.applib.services.i18n.TranslatableString;
@@ -48,22 +49,16 @@ public class HasUsername_open {
 
     public static class ActionDomainEvent extends IsisModuleExtSecmanApplib.ActionDomainEvent<HasUsername_open> {}
 
-    public ApplicationUser act() {
+    @MemberSupport public ApplicationUser act() {
         if (target == null || target.getUsername() == null) {
             return null;
         }
         return applicationUserRepository.findByUsername(target.getUsername()).orElse(null);
     }
 
-    public boolean hideAct() {
-        return target instanceof ApplicationUser;
-    }
-
-    public TranslatableString disableAct() {
-        if (target == null || target.getUsername() == null) {
-            return TranslatableString.tr("No username");
-        }
-        return null;
+    @MemberSupport public boolean hideAct() { return target instanceof ApplicationUser; }
+    @MemberSupport public TranslatableString disableAct() {
+        return target == null || target.getUsername() == null ? TranslatableString.tr("No username") : null;
     }
 
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUser.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUser.java
index 456cfba..294cc77 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUser.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/ApplicationUser.java
@@ -117,14 +117,12 @@ public abstract class ApplicationUser
     public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApplib.PropertyDomainEvent<ApplicationUser, T> {}
     public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApplib.CollectionDomainEvent<ApplicationUser, T> {}
 
-    // -- MODEL
 
-    @Title
-    public String title() {
+
+    @ObjectSupport public String title() {
         return getName();
     }
-    @ObjectSupport
-    public String iconName() {
+    @ObjectSupport public String iconName() {
         return getStatus().isUnlocked() ? "unlocked" : "locked";
     }
 
@@ -474,8 +472,7 @@ public abstract class ApplicationUser
     @EncryptedPassword
     public abstract String getEncryptedPassword();
     public abstract void setEncryptedPassword(String encryptedPassword);
-    @MemberSupport
-    public boolean hideEncryptedPassword() {
+    @MemberSupport public boolean hideEncryptedPassword() {
         return !getApplicationUserRepository().isPasswordFeatureEnabled(this);
     }
 
@@ -501,8 +498,7 @@ public abstract class ApplicationUser
     public boolean isHasPassword() {
         return _Strings.isNotEmpty(getEncryptedPassword());
     }
-    @MemberSupport
-    public boolean hideHasPassword() {
+    @MemberSupport public boolean hideHasPassword() {
         return !getApplicationUserRepository().isPasswordFeatureEnabled(this);
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_addRole.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_addRole.java
index 3e3921f..2164b30 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_addRole.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_addRole.java
@@ -24,6 +24,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.extensions.secman.applib.IsisModuleExtSecmanApplib;
@@ -54,20 +55,18 @@ public class ApplicationUser_addRole {
 
     private final ApplicationUser target;
 
-    public ApplicationUser act(final ApplicationRole role) {
+    @MemberSupport public ApplicationUser act(final ApplicationRole role) {
         applicationRoleRepository.addRoleToUser(role, target);
         return target;
     }
 
-    public Collection<? extends ApplicationRole> choices0Act() {
+    @MemberSupport public Collection<? extends ApplicationRole> choices0Act() {
         val allRoles = applicationRoleRepository.allRoles();
         val applicationRoles = _Sets.newTreeSet(allRoles);
         applicationRoles.removeAll(target.getRoles());
         return applicationRoles;
     }
 
-    public String disableAct() {
-        return choices0Act().isEmpty()? "All roles added": null;
-    }
+    @MemberSupport public String disableAct() { return choices0Act().isEmpty()? "All roles added": null; }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_delete.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_delete.java
index 44ba2d0..6d7ae67 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_delete.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_delete.java
@@ -54,14 +54,12 @@ public class ApplicationUser_delete {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public Collection<ApplicationUser> act() {
+    @MemberSupport public Collection<ApplicationUser> act() {
         repository.removeAndFlush(target);
         return applicationUserRepository.allUsers();
     }
 
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return applicationUserRepository.isAdminUser(target)? "Cannot delete the admin user": null;
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_duplicate.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_duplicate.java
index f27bda5..0ab7d46 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_duplicate.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_duplicate.java
@@ -18,9 +18,10 @@
  */
 package org.apache.isis.extensions.secman.applib.user.dom.mixins;
 
-import org.springframework.lang.Nullable;
 import javax.inject.Inject;
 
+import org.springframework.lang.Nullable;
+
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.MemberSupport;
@@ -55,8 +56,7 @@ public class ApplicationUser_duplicate {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             final String username,
             @Nullable
             final String emailAddress) {
@@ -74,5 +74,4 @@ public class ApplicationUser_duplicate {
                 });
 
     }
-
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_lock.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_lock.java
index b779aff..5811802 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_lock.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_lock.java
@@ -53,14 +53,12 @@ public class ApplicationUser_lock {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act() {
+    @MemberSupport public ApplicationUser act() {
         target.setStatus(ApplicationUserStatus.LOCKED);
         return target;
     }
 
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         if(applicationUserRepository.isAdminUser(target)) {
             return String.format("Cannot lock the '%s' user.", configBean.getAdminUserName());
         }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_removeRoles.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_removeRoles.java
index 7df6bcf..95223b2 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_removeRoles.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_removeRoles.java
@@ -60,8 +60,7 @@ public class ApplicationUser_removeRoles {
     private final ApplicationUser target;
 
 
-    @MemberSupport
-    public ApplicationUser act(Collection<ApplicationRole> roles) {
+    @MemberSupport public ApplicationUser act(Collection<ApplicationRole> roles) {
 
         _NullSafe.stream(roles)
         .filter(this::canRemove)
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_resetPassword.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_resetPassword.java
index 01b56cb..0d1ce30 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_resetPassword.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_resetPassword.java
@@ -52,8 +52,7 @@ public class ApplicationUser_resetPassword {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             final Password newPassword,
             final Password repeatPassword) {
 
@@ -61,13 +60,11 @@ public class ApplicationUser_resetPassword {
         return target;
     }
 
-    @MemberSupport
-    public boolean hideAct() {
+    @MemberSupport public boolean hideAct() {
         return !applicationUserRepository.isPasswordFeatureEnabled(target);
     }
 
-    @MemberSupport
-    public String validateAct(
+    @MemberSupport public String validateAct(
             final Password newPassword,
             final Password repeatPassword) {
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_unlock.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_unlock.java
index 934b2fe..2ef795e 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_unlock.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_unlock.java
@@ -46,14 +46,12 @@ public class ApplicationUser_unlock {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act() {
+    @MemberSupport public ApplicationUser act() {
         target.setStatus(ApplicationUserStatus.UNLOCKED);
         return target;
     }
 
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return target.getStatus() == ApplicationUserStatus.UNLOCKED ? "Status is already set to UNLOCKED": null;
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateAccountType.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateAccountType.java
index 2b01f98..32db577 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateAccountType.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateAccountType.java
@@ -52,21 +52,18 @@ public class ApplicationUser_updateAccountType {
 
     private final ApplicationUser target;
 
-    @MemberSupport
     public ApplicationUser act(final AccountType accountType) {
         target.setAccountType(accountType);
         return target;
     }
 
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return applicationUserRepository.isAdminUser(target)
                 ? "Cannot change account type for admin user"
                         : null;
     }
 
-    @MemberSupport
-    public AccountType default0Act() {
+    @MemberSupport public AccountType default0Act() {
         return target.getAccountType();
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateAtPath.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateAtPath.java
index f8947c4..12f7f32 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateAtPath.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateAtPath.java
@@ -46,16 +46,14 @@ public class ApplicationUser_updateAtPath {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             @ApplicationUser.AtPath
             final String atPath) {
         target.setAtPath(atPath);
         return target;
     }
 
-    @MemberSupport
-    public String default0Act() {
+    @MemberSupport public String default0Act() {
         return target.getAtPath();
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateEmailAddress.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateEmailAddress.java
index d988b6b..c859b44 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateEmailAddress.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateEmailAddress.java
@@ -46,21 +46,18 @@ public class ApplicationUser_updateEmailAddress {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             @ApplicationUser.EmailAddress
             final String emailAddress) {
         target.setEmailAddress(emailAddress);
         return target;
     }
 
-    @MemberSupport
-    public String default0Act() {
+    @MemberSupport public String default0Act() {
         return target.getEmailAddress();
     }
 
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return target.isForSelfOrRunAsAdministrator()? null: "Can only update your own user record.";
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateFaxNumber.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateFaxNumber.java
index a26c54c..42bff1b 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateFaxNumber.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateFaxNumber.java
@@ -46,21 +46,15 @@ public class ApplicationUser_updateFaxNumber {
 
     private final ApplicationUser holder;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             @ApplicationUser.FaxNumber
             final String fax) {
         holder.setFaxNumber(fax);
         return holder;
     }
 
-    @MemberSupport
-    public String default0Act() {
-        return holder.getFaxNumber();
-    }
-
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String default0Act() { return holder.getFaxNumber(); }
+    @MemberSupport public String disableAct() {
         return holder.isForSelfOrRunAsAdministrator()? null: "Can only update your own user record.";
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateName.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateName.java
index 8acb8fd..6f7fecb 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateName.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateName.java
@@ -46,8 +46,7 @@ public class ApplicationUser_updateName {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             @ApplicationUser.FamilyName
             final String familyName,
             @ApplicationUser.GivenName
@@ -61,28 +60,14 @@ public class ApplicationUser_updateName {
         return target;
     }
 
-    @MemberSupport
-    public String default0Act() {
-        return target.getFamilyName();
-    }
-
-    @MemberSupport
-    public String default1Act() {
-        return target.getGivenName();
-    }
-
-    @MemberSupport
-    public String default2Act() {
-        return target.getKnownAs();
-    }
-
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String default0Act() { return target.getFamilyName(); }
+    @MemberSupport public String default1Act() { return target.getGivenName(); }
+    @MemberSupport public String default2Act() { return target.getKnownAs(); }
+    @MemberSupport public String disableAct() {
         return target.isForSelfOrRunAsAdministrator()? null: "Can only update your own user record.";
     }
 
-    @MemberSupport
-    public String validateAct(final String familyName, final String givenName, final String knownAs) {
+    @MemberSupport public String validateAct(final String familyName, final String givenName, final String knownAs) {
         if(familyName != null && givenName == null) {
             return "Must provide given name if family name has been provided.";
         }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePassword.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePassword.java
index 5e7eb05..24cabdc 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePassword.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePassword.java
@@ -57,8 +57,7 @@ public class ApplicationUser_updatePassword {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             final Password existingPassword,
             final Password newPassword,
             final Password repeatNewPassword) {
@@ -67,13 +66,8 @@ public class ApplicationUser_updatePassword {
         return target;
     }
 
-    @MemberSupport
-    public boolean hideAct() {
-        return !applicationUserRepository.isPasswordFeatureEnabled(target);
-    }
-
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public boolean hideAct() { return !applicationUserRepository.isPasswordFeatureEnabled(target); }
+    @MemberSupport public String disableAct() {
 
         if(!target.isForSelfOrRunAsAdministrator()) {
             return "Can only update password for your own user account.";
@@ -84,8 +78,7 @@ public class ApplicationUser_updatePassword {
         return null;
     }
 
-    @MemberSupport
-    public String validateAct(
+    @MemberSupport public String validateAct(
             final Password existingPassword,
             final Password newPassword,
             final Password repeatNewPassword) {
@@ -111,5 +104,4 @@ public class ApplicationUser_updatePassword {
         return null;
     }
 
-
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePhoneNumber.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePhoneNumber.java
index 6acebd8..77b7a5d 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePhoneNumber.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updatePhoneNumber.java
@@ -46,22 +46,16 @@ public class ApplicationUser_updatePhoneNumber {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             @ApplicationUser.PhoneNumber
             final String phoneNumber) {
         target.setPhoneNumber(phoneNumber);
         return target;
     }
 
-    @MemberSupport
-    public String disableAct() {
+    @MemberSupport public String disableAct() {
         return target.isForSelfOrRunAsAdministrator()? null: "Can only update your own user record.";
     }
-
-    @MemberSupport
-    public String default0Act() {
-        return target.getPhoneNumber();
-    }
+    @MemberSupport public String default0Act() { return target.getPhoneNumber(); }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateUsername.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateUsername.java
index 6cfb544..9138a93 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateUsername.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/ApplicationUser_updateUsername.java
@@ -46,17 +46,13 @@ public class ApplicationUser_updateUsername {
 
     private final ApplicationUser target;
 
-    @MemberSupport
-    public ApplicationUser act(
+    @MemberSupport public ApplicationUser act(
             @ApplicationUser.Username
             final String username) {
         target.setUsername(username);
         return target;
     }
 
-    @MemberSupport
-    public String default0Act() {
-        return target.getUsername();
-    }
+    @MemberSupport public String default0Act() { return target.getUsername(); }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/ApplicationUser_effectiveMemberPermissions.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/ApplicationUser_effectiveMemberPermissions.java
index 727aabb..5a10ea3 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/ApplicationUser_effectiveMemberPermissions.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/ApplicationUser_effectiveMemberPermissions.java
@@ -52,8 +52,7 @@ public class ApplicationUser_effectiveMemberPermissions {
 
     private final ApplicationUser user;
 
-    @MemberSupport
-    public List<UserPermissionViewModel> coll() {
+    @MemberSupport public List<UserPermissionViewModel> coll() {
         return applicationFeatureRepository
                 .allMembers()
                 .stream()
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/ApplicationUser_filterEffectiveMemberPermissions.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/ApplicationUser_filterEffectiveMemberPermissions.java
index 951ae7c..d5e3086 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/ApplicationUser_filterEffectiveMemberPermissions.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/ApplicationUser_filterEffectiveMemberPermissions.java
@@ -61,8 +61,7 @@ public class ApplicationUser_filterEffectiveMemberPermissions {
 
     private final ApplicationUser user;
 
-    @MemberSupport
-    public List<UserPermissionViewModel> act(
+    @MemberSupport public List<UserPermissionViewModel> act(
 
             @ParameterLayout(
                     describedAs = ApplicationFeatureChoices.DESCRIBED_AS
@@ -78,8 +77,7 @@ public class ApplicationUser_filterEffectiveMemberPermissions {
             .collect(Collectors.toList());
     }
 
-    @MemberSupport
-    public java.util.Collection<ApplicationFeatureChoices.AppFeat> autoComplete0Act(
+    @MemberSupport public java.util.Collection<ApplicationFeatureChoices.AppFeat> autoComplete0Act(
             final @MinLength(3) String search) {
         return applicationFeatureChoices.autoCompleteFeature(search);
     }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/UserPermissionViewModel.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/UserPermissionViewModel.java
index 7bb82d4..166289b 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/UserPermissionViewModel.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/dom/mixins/perms/UserPermissionViewModel.java
@@ -40,7 +40,6 @@ import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.PropertyLayout;
-import org.apache.isis.applib.annotation.Title;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.appfeat.ApplicationFeature;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureId;
@@ -111,13 +110,11 @@ public class UserPermissionViewModel implements ViewModel {
 
     // -- identification
 
-    @Title
-    public String title() {
+    @ObjectSupport public String title() {
         return getVerb() + " " + getFeatureId().getFullyQualifiedName();
     }
 
-    @ObjectSupport
-    public String iconName() {
+    @ObjectSupport public String iconName() {
         return "userPermission";
     }
 
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/ApplicationUserMenu.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/ApplicationUserMenu.java
index 41b4285..8f45f82 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/ApplicationUserMenu.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/ApplicationUserMenu.java
@@ -26,6 +26,7 @@ import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.applib.annotation.ParameterLayout;
@@ -53,47 +54,54 @@ public class ApplicationUserMenu {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleExtSecmanApplib.NAMESPACE + ".ApplicationUserMenu";
 
-    public static abstract class ActionDomainEvent
-        extends IsisModuleExtSecmanApplib.ActionDomainEvent<ApplicationUserMenu> { }
+    public static abstract class ActionDomainEvent<T> extends IsisModuleExtSecmanApplib.ActionDomainEvent<T> { }
 
     private final ApplicationUserRepository applicationUserRepository;
     private final FactoryService factory;
 
 
-    @ObjectSupport
-    public String iconName() {
+    @ObjectSupport public String iconName() {
         return "applicationUser";
     }
 
 
 
-    public static class FindUsersByNameDomainEvent
-        extends ActionDomainEvent { }
-
     @Action(
-            domainEvent = FindUsersByNameDomainEvent.class,
+            domainEvent = findUsers.ActionEvent.class,
             semantics = SemanticsOf.SAFE
     )
     @ActionLayout(sequence = "100.10.2")
-    public Collection<? extends ApplicationUser> findUsers(
-            final @ParameterLayout(named = "Search") String search) {
-        return applicationUserRepository.find(search);
+    public class findUsers{
+
+        public class ActionEvent
+                extends ActionDomainEvent<findUsers> { }
+
+        @MemberSupport public Collection<? extends ApplicationUser> act(
+                final @ParameterLayout(named = "Search") String search) {
+            return applicationUserRepository.find(search);
+        }
+
     }
 
 
 
-    public static class UserManagerDomainEvent extends ActionDomainEvent { }
 
     @Action(
-            domainEvent = UserManagerDomainEvent.class,
+            domainEvent = userManager.ActionEvent.class,
             semantics = SemanticsOf.IDEMPOTENT
     )
     @ActionLayout(
             sequence = "100.10.3",
             cssClassFa = "user-plus"
     )
-    public ApplicationUserManager userManager() {
-        return factory.viewModel(new ApplicationUserManager());
+    public class userManager{
+
+        public class ActionEvent extends ActionDomainEvent<userManager> { }
+
+        @MemberSupport public ApplicationUserManager act(){
+            return factory.viewModel(new ApplicationUserManager());
+        }
+
     }
 
 }
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/MeService.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/MeService.java
index d847e85..9dafa4c 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/MeService.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/user/menu/MeService.java
@@ -27,8 +27,10 @@ import org.springframework.stereotype.Component;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.Domain;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
@@ -61,23 +63,20 @@ public class MeService {
 
     public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApplib.PropertyDomainEvent<MeService, T> {}
     public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApplib.CollectionDomainEvent<MeService, T> {}
-    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApplib.ActionDomainEvent<MeService> {}
+    public static abstract class ActionDomainEvent<T> extends IsisModuleExtSecmanApplib.ActionDomainEvent<T> {}
 
     final ApplicationUserRepository applicationUserRepository;
     final UserService userService;
     final javax.inject.Provider<QueryResultsCache> queryResultsCacheProvider;
 
-    // -- iconName
-    @ObjectSupport
-    public String iconName() {
+
+    @ObjectSupport public String iconName() {
         return "applicationUser";
     }
 
-    // -- me (action)
-    public static class MeDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = MeDomainEvent.class,
+            domainEvent = me.ActionEvent.class,
             semantics = SemanticsOf.SAFE
             )
     @ActionLayout(
@@ -87,33 +86,35 @@ public class MeService {
             sequence = "100"
             )
 
-    public ApplicationUser me() {
-        return queryResultsCacheProvider.get().execute(new Callable<ApplicationUser>() {
-            @Override
-            public ApplicationUser call() throws Exception {
-                return doMe();
-            }
-        }, MeService.class, "me");
-    }
+    public class me{
 
-    protected ApplicationUser doMe() {
-        final String myName = userService.currentUserNameElseNobody();
-        return applicationUserRepository.findOrCreateUserByUsername(myName);
-    }
+        public class ActionEvent extends ActionDomainEvent<me> {}
+
+        @MemberSupport public ApplicationUser act() {
+            return queryResultsCacheProvider.get().execute(
+                    (Callable<ApplicationUser>) this::doMe, MeService.class, "me");
+        }
+
+        @Domain.Exclude protected ApplicationUser doMe() {
+            final String myName = userService.currentUserNameElseNobody();
+            return applicationUserRepository.findOrCreateUserByUsername(myName);
+        }
+
+        @Domain.Exclude protected ApplicationUser doMe(final String myName) {
+            return applicationUserRepository.findOrCreateUserByUsername(myName);
+        }
 
-    protected ApplicationUser doMe(final String myName) {
-        return applicationUserRepository.findOrCreateUserByUsername(myName);
     }
 
 
     @Component
-    @RequiredArgsConstructor(onConstructor_ = {@Inject})
+    @RequiredArgsConstructor
     public static class UserMenuMeActionAdvisor {
 
         final IsisConfiguration isisConfiguration;
 
-        @EventListener(UserMenu.MeDomainEvent.class)
-        public void on(final UserMenu.MeDomainEvent event) {
+        @EventListener(UserMenu.me.ActionEvent.class)
+        public void on(final UserMenu.me.ActionEvent event) {
             switch (isisConfiguration.getExtensions().getSecman().getUserMenuMeActionPolicy()) {
                 case HIDE:
                     event.hide();
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/isis/persistence/jdo/metamodel/menu/JdoMetamodelMenu.java b/persistence/jdo/metamodel/src/main/java/org/apache/isis/persistence/jdo/metamodel/menu/JdoMetamodelMenu.java
index c7f1657..61ec1b9 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/isis/persistence/jdo/metamodel/menu/JdoMetamodelMenu.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/isis/persistence/jdo/metamodel/menu/JdoMetamodelMenu.java
@@ -28,6 +28,7 @@ import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.annotation.RestrictTo;
 import org.apache.isis.applib.annotation.SemanticsOf;
@@ -58,13 +59,12 @@ public class JdoMetamodelMenu {
     final JdoSupportService jdoSupport;
     final JdoFacetContext jdoFacetContext;
 
-    public static abstract class ActionDomainEvent
-    extends IsisModuleApplib.ActionDomainEvent<JdoMetamodelMenu> {}
+    public static abstract class ActionDomainEvent<T>
+    extends IsisModuleApplib.ActionDomainEvent<T> {}
 
-    public static class DownloadJdoMetamodelDomainEvent extends ActionDomainEvent {}
 
     @Action(
-            domainEvent = DownloadJdoMetamodelDomainEvent.class,
+            domainEvent = downloadMetamodels.ActionEvent.class,
             semantics = SemanticsOf.NON_IDEMPOTENT, //disable client-side caching
             restrictTo = RestrictTo.PROTOTYPING
             )
@@ -72,12 +72,17 @@ public class JdoMetamodelMenu {
             cssClassFa = "fa-download",
             named = "Download JDO Metamodels (ZIP)",
             sequence="500.670.1")
-    public Blob downloadMetamodels() {
+    public class downloadMetamodels{
 
-        final byte[] zipBytes = zip();
-        return Blob.of("jdo-metamodels", CommonMimeType.ZIP, zipBytes);
+        public class ActionEvent extends ActionDomainEvent<downloadMetamodels> {}
+
+        @MemberSupport public Blob act() {
+            final byte[] zipBytes = zip();
+            return Blob.of("jdo-metamodels", CommonMimeType.ZIP, zipBytes);
+        }
     }
 
+
     // -- HELPER
 
     private byte[] zip() {
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/swagger/SwaggerServiceMenu.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/swagger/SwaggerServiceMenu.java
index 2f64170..5e45398 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/swagger/SwaggerServiceMenu.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/swagger/SwaggerServiceMenu.java
@@ -31,6 +31,7 @@ import org.apache.isis.applib.annotation.DomainServiceLayout;
 import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.ParameterLayout;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.RestrictTo;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
@@ -75,77 +76,89 @@ public class SwaggerServiceMenu {
     }
 
     public static abstract class ActionDomainEvent extends IsisModuleApplib.ActionDomainEvent<SwaggerServiceMenu> { }
-    public static class OpenSwaggerUiDomainEvent extends ActionDomainEvent { }
 
     @Action(
             semantics = SemanticsOf.SAFE,
-            domainEvent = OpenSwaggerUiDomainEvent.class,
+            domainEvent = openSwaggerUi.ActionEvent.class,
             restrictTo = RestrictTo.PROTOTYPING
             )
     @ActionLayout(
             cssClassFa = "fa-external-link-alt",
             sequence="500.600.1")
-    public LocalResourcePath openSwaggerUi() {
+    public class openSwaggerUi {
+        public class ActionEvent extends ActionDomainEvent { }
+
+        public LocalResourcePath act() {
         return new LocalResourcePath("/swagger-ui/index.thtml");
     }
     @MemberSupport
-    public String disableOpenSwaggerUi() {
-        return disableReasonWhenRequiresROViewer();
+        public String disableAct() {
+            return disableReasonWhenRequiresROViewer();
+        }
     }
 
-    public static class OpenRestApiDomainEvent extends ActionDomainEvent { }
+
+
 
     @Action(
             semantics = SemanticsOf.SAFE,
-            domainEvent = OpenSwaggerUiDomainEvent.class,
+            domainEvent = openRestApi.ActionEvent.class,
             restrictTo = RestrictTo.PROTOTYPING
             )
     @ActionLayout(
             cssClassFa = "fa-external-link-alt",
             sequence="500.600.2")
-    public LocalResourcePath openRestApi() {
-        return new LocalResourcePath(basePath);
-    }
+    public class openRestApi {
+
+        public class ActionEvent extends ActionDomainEvent { }
+
+        LocalResourcePath act() {
+            return new LocalResourcePath(basePath);
+        }
     @MemberSupport
-    public String disableOpenRestApi() {
-        return disableReasonWhenRequiresROViewer();
+        @MemberSupport String disableAct() {
+            return disableReasonWhenRequiresROViewer();
+        }
     }
 
-    public static class DownloadSwaggerSpecDomainEvent extends ActionDomainEvent { }
+
 
     @Action(
             semantics = SemanticsOf.SAFE,
-            domainEvent = DownloadSwaggerSpecDomainEvent.class,
+            domainEvent = downloadSwaggerSchemaDefinition.ActionEvent.class,
             restrictTo = RestrictTo.PROTOTYPING
             )
     @ActionLayout(
             cssClassFa = "fa-download",
             sequence="500.600.3")
-    public Clob downloadSwaggerSchemaDefinition(
-            @ParameterLayout(named = "Filename")
-            final String fileNamePrefix,
-            final Visibility visibility,
-            final Format format) {
 
-        final String fileName = buildFileName(fileNamePrefix, visibility, format);
-        final String spec = swaggerService.generateSwaggerSpec(visibility, format);
-        return new Clob(fileName, format.mediaType(), spec);
-    }
-    @MemberSupport
-    public String default0DownloadSwaggerSchemaDefinition() {
-        return "swagger";
-    }
-    @MemberSupport
-    public Visibility default1DownloadSwaggerSchemaDefinition() {
-        return Visibility.PRIVATE;
-    }
-    @MemberSupport
-    public Format default2DownloadSwaggerSchemaDefinition() {
-        return Format.YAML;
+    public class downloadSwaggerSchemaDefinition {
+
+        public class ActionEvent extends ActionDomainEvent { }
+
+        public Clob act(
+                @ParameterLayout(named = "Filename")
+                final String fileNamePrefix,
+                final Visibility visibility,
+                final Format format) {
+
+            val fileName = buildFileName(fileNamePrefix, visibility, format);
+            val spec = swaggerService.generateSwaggerSpec(visibility, format);
+            return new Clob(fileName, format.mediaType(), spec);
+        }
+
+        @MemberSupport public String default0Act() { return "swagger"; }
+        @MemberSupport public Visibility default1Act() {
+            return Visibility.PRIVATE;
+        }
+        @MemberSupport public Format default2Act() {
+            return Format.YAML;
+        }
     }
 
-    // -- HELPER
 
+    // -- HELPER
+    @Programmatic
     private String disableReasonWhenRequiresROViewer() {
         final Optional<?> moduleIfAny = serviceRegistry
                 .lookupBeanById("isis.viewer.ro.WebModuleJaxrsRestEasy4");

[isis] 01/02: ISIS-2867: adds arch tests 'unique logical type name'; 'logicalTypeName matches JDO discriminator'

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

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

commit 56cfee96f573542cceb57d2d15927e2b9ca97187
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Sep 6 16:45:14 2021 +0100

    ISIS-2867: adds arch tests 'unique logical type name'; 'logicalTypeName matches JDO discriminator'
---
 .../adoc/modules/archtestsupport/pages/about.adoc  | 12 ++++
 .../applib/classrules/ArchitectureDomainRules.java | 66 +++++++++++++++++++---
 .../applib/classrules/ArchitectureJdoRules.java    | 65 +++++++++++++++++----
 .../applib/classrules/CommonPredicates.java        | 24 ++++++++
 4 files changed, 149 insertions(+), 18 deletions(-)

diff --git a/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc b/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
index d472aeb..3530f0e 100644
--- a/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
+++ b/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
@@ -113,6 +113,12 @@ This ensures that persisted/serialized bookmarks of domain objects are stable ev
 *** `every_DomainObject_must_specify_logicalTypeName()`
 *** `every_DomainService_must_specify_logicalTypeName()`
 
+** unique logical type name
++
+This ensures that the logical type name is also unique across ``@DomainObject``s and ``@DomainService``s.
++
+Note: this rule ignores any abstract classes because strictly the framework only requires that instantiatable classes have unique logical type names.
+
 ** JAXB view models are annotated with `@DomainObject(nature=VIEW_MODEL)`
 +
 This ensures that the framework is able to correctly detect the view models in the metamodel:
@@ -193,6 +199,12 @@ Enforces a very common convention for JPA entities:
 
 *** `every_jpa_Entity_must_have_an_id_field()`
 
+** ensure JDO entity's logicalTypeName matches its discriminator (if any)
++
+Both the logical type name and the `@Discriminator` for subclasses are a unique identifier of a type; this rule says they should be the same value.
+
+*** `every_logicalTypeName_and_jdo_discriminator_must_be_same()`
+
 
 * Recommended
 
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainRules.java
index 4eefef9..ddbc14b 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainRules.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainRules.java
@@ -20,6 +20,8 @@ package org.apache.isis.testing.archtestsupport.applib.classrules;
 
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.TreeMap;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -33,7 +35,6 @@ import com.tngtech.archunit.core.domain.JavaClass;
 import com.tngtech.archunit.core.domain.JavaCodeUnit;
 import com.tngtech.archunit.core.domain.JavaMethod;
 import com.tngtech.archunit.core.domain.JavaModifier;
-import com.tngtech.archunit.junit.ArchTest;
 import com.tngtech.archunit.lang.ArchCondition;
 import com.tngtech.archunit.lang.ArchRule;
 import com.tngtech.archunit.lang.ConditionEvents;
@@ -56,6 +57,7 @@ import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
 import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.commons.internal.base._Strings;
 
 import lombok.val;
 import lombok.experimental.UtilityClass;
@@ -90,6 +92,56 @@ public class ArchitectureDomainRules {
                 .should().beAnnotatedWith(DomainService_logicalTypeName());
     }
 
+    private static Map<String, JavaClass> javaClassByLogicalTypeName = new TreeMap<>();
+
+    /**
+     * This rule requires that classes annotated the <code>logicalTypeName</code> must be unique across all
+     * non-abstract {@link DomainObject}s and {@link DomainService}s
+     */
+    public static ArchRule every_logicalTypeName_must_be_unique() {
+        return classes()
+                .that().areAnnotatedWith(DomainObject.class)
+                .or().areAnnotatedWith(DomainService.class)
+                .and(new DescribedPredicate<>("have an logicalTypeName") {
+                    @Override
+                    public boolean apply(JavaClass javaClass) {
+                        val domainObjectIfAny = javaClass.tryGetAnnotationOfType(DomainObject.class);
+                        if (domainObjectIfAny.isPresent() && !_Strings.isNullOrEmpty(domainObjectIfAny.get().logicalTypeName())) {
+                            return true;
+                        }
+                        val domainServiceIfAny = javaClass.tryGetAnnotationOfType(DomainService.class);
+                        if (domainServiceIfAny.isPresent() && !_Strings.isNullOrEmpty(domainServiceIfAny.get().logicalTypeName())) {
+                            return true;
+                        }
+
+                        return false;
+                    }
+                })
+                .should(new ArchCondition<>("be unique") {
+                    @Override
+                    public void check(JavaClass javaClass, ConditionEvents conditionEvents) {
+                        val domainObjectIfAny = javaClass.tryGetAnnotationOfType(DomainObject.class);
+                        String logicalTypeName = null;
+                        if (domainObjectIfAny.isPresent()) {
+                            logicalTypeName = domainObjectIfAny.get().logicalTypeName();
+                        } else {
+                            val domainServiceIfAny = javaClass.tryGetAnnotationOfType(DomainService.class);
+                            if (domainServiceIfAny.isPresent()) {
+                                logicalTypeName = domainServiceIfAny.get().logicalTypeName();
+                            }
+                        }
+                        final JavaClass existing = javaClassByLogicalTypeName.get(logicalTypeName);
+                        if (existing != null) {
+                            conditionEvents.add(
+                                    new SimpleConditionEvent(javaClass, false,
+                                            String.format("Classes '%s' and '%s' have the same logicalTypeName '%s'", javaClass.getName(), existing.getName(), logicalTypeName)));
+                        } else {
+                            javaClassByLogicalTypeName.put(logicalTypeName, javaClass);
+                        }
+                    }
+                });
+    }
+
     /**
      * This rule requires that classes annotated with the {@link DomainObject} annotation must also be
      * annotated with the {@link DomainObjectLayout} annotation.
@@ -134,7 +186,7 @@ public class ArchitectureDomainRules {
 
     /**
      * This rule requires that classes annotated with the {@link XmlRootElement} annotation must also be
-     * annotated with the {@link DomainObject} annotation specifying a {@link Nature nature} of {@link Nature#MIXIN MIXIN}.
+     * annotated with the {@link DomainObject} annotation specifying a {@link Nature nature} of {@link Nature#VIEW_MODEL VIEW_MODEL}.
      *
      * <p>
      *     This is required because the framework uses Spring to detect entities and view models (the
@@ -142,11 +194,11 @@ public class ArchitectureDomainRules {
      *     {@link org.springframework.stereotype.Component} annotation.
      * </p>
      */
-    @ArchTest
-    public static ArchRule every_jaxb_view_model_must_also_be_annotated_with_DomainObject_nature_MIXIN =
-            ArchRuleDefinition.classes().that()
-                    .areAnnotatedWith(XmlRootElement.class)
-                    .should().beAnnotatedWith(CommonPredicates.DomainObject_nature_MIXIN());
+    public static ArchRule every_jaxb_view_model_must_also_be_annotated_with_DomainObject_nature_VIEW_MODEL() {
+        return ArchRuleDefinition.classes().that()
+                .areAnnotatedWith(XmlRootElement.class)
+                .should().beAnnotatedWith(CommonPredicates.DomainObject_nature_VIEW_MODEL());
+    }
 
     /**
      * This rule requires that action mixin classes should follow the naming convention
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java
index 112be23..bde6633 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java
@@ -18,7 +18,10 @@
  */
 package org.apache.isis.testing.archtestsupport.applib.classrules;
 
+import java.util.Objects;
+
 import javax.inject.Inject;
+import javax.jdo.annotations.Discriminator;
 import javax.jdo.annotations.NotPersistent;
 import javax.jdo.annotations.PersistenceCapable;
 
@@ -26,15 +29,20 @@ import com.tngtech.archunit.base.DescribedPredicate;
 import com.tngtech.archunit.core.domain.JavaAnnotation;
 import com.tngtech.archunit.core.domain.JavaClass;
 import com.tngtech.archunit.core.domain.JavaEnumConstant;
+import com.tngtech.archunit.lang.ArchCondition;
 import com.tngtech.archunit.lang.ArchRule;
-
-import org.apache.isis.applib.annotation.DomainObject;
+import com.tngtech.archunit.lang.ConditionEvents;
+import com.tngtech.archunit.lang.SimpleConditionEvent;
 
 import static com.tngtech.archunit.base.DescribedPredicate.not;
 import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
 import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
-import lombok.experimental.UtilityClass;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.commons.internal.base._Strings;
+
 import lombok.val;
+import lombok.experimental.UtilityClass;
 
 /**
  * A library of architecture tests to ensure coding conventions are followed for classes annotated with
@@ -45,6 +53,38 @@ import lombok.val;
 @UtilityClass
 public class ArchitectureJdoRules {
 
+    public static ArchRule every_logicalTypeName_and_jdo_discriminator_must_be_same() {
+        return classes()
+                .that().areAnnotatedWith(DomainObject.class)
+                .and(new DescribedPredicate<>("have a logicalTypeName") {
+                    @Override
+                    public boolean apply(JavaClass javaClass) {
+                        val domainObjectIfAny = javaClass.tryGetAnnotationOfType(DomainObject.class);
+                        return domainObjectIfAny.isPresent() && !_Strings.isNullOrEmpty(domainObjectIfAny.get().logicalTypeName());
+                    }
+                })
+                .and(new DescribedPredicate<>("have a @Discriminator") {
+                    @Override
+                    public boolean apply(JavaClass javaClass) {
+                        val discriminatorIfAny = javaClass.tryGetAnnotationOfType(Discriminator.class);
+                        return discriminatorIfAny.isPresent();
+                    }
+                })
+                .should(new ArchCondition<>("be the same") {
+                    @Override
+                    public void check(JavaClass javaClass, ConditionEvents conditionEvents) {
+                        val logicalTypeName = javaClass.getAnnotationOfType(DomainObject.class).logicalTypeName();
+                        val discriminatorValue = javaClass.getAnnotationOfType(Discriminator.class).value();
+                        if (!Objects.equals(logicalTypeName, discriminatorValue)) {
+                            conditionEvents.add(
+                                    new SimpleConditionEvent(javaClass, false,
+                                    String.format("@DomainObject(logicalTypeName = '%s') vs @Discriminator('%s')",
+                                            logicalTypeName, discriminatorValue)));
+                        }
+                    }
+                });
+    }
+
     /**
      * This rule requires that classes annotated with the JDO {@link javax.jdo.annotations.PersistenceCapable} annotation
      * must also be annotated with the Apache Isis {@link DomainObject} annotation specifying that its
@@ -154,8 +194,9 @@ public class ArchitectureJdoRules {
 
 
     static DescribedPredicate<JavaAnnotation<?>> PersistenceCapable_schema() {
-        return new DescribedPredicate<JavaAnnotation<?>>("@PersistenceCapable(schema=...)") {
-            @Override public boolean apply(final JavaAnnotation<?> javaAnnotation) {
+        return new DescribedPredicate<>("@PersistenceCapable(schema=...)") {
+            @Override
+            public boolean apply(final JavaAnnotation<?> javaAnnotation) {
                 if (!javaAnnotation.getRawType().isAssignableTo(PersistenceCapable.class)) {
                     return false;
                 }
@@ -168,8 +209,9 @@ public class ArchitectureJdoRules {
     }
 
     static DescribedPredicate<JavaAnnotation<?>> PersistenceCapable_with_DATASTORE_identityType() {
-        return new DescribedPredicate<JavaAnnotation<?>>("@PersistenceCapable(identityType=DATASTORE)") {
-            @Override public boolean apply(final JavaAnnotation<?> javaAnnotation) {
+        return new DescribedPredicate<>("@PersistenceCapable(identityType=DATASTORE)") {
+            @Override
+            public boolean apply(final JavaAnnotation<?> javaAnnotation) {
                 if (!javaAnnotation.getRawType().isAssignableTo(PersistenceCapable.class)) {
                     return false;
                 }
@@ -203,7 +245,7 @@ public class ArchitectureJdoRules {
     }
 
     static DescribedPredicate<JavaClass> areEntities() {
-        return new DescribedPredicate<JavaClass>("are entities") {
+        return new DescribedPredicate<>("are entities") {
             @Override
             public boolean apply(JavaClass input) {
                 return input.isAnnotatedWith(PersistenceCapable.class);
@@ -212,10 +254,11 @@ public class ArchitectureJdoRules {
     }
 
     static DescribedPredicate<? super JavaClass> areSubtypeEntities() {
-        return new DescribedPredicate<JavaClass>("are subtype entities ") {
-            @Override public boolean apply(final JavaClass input) {
+        return new DescribedPredicate<>("are subtype entities ") {
+            @Override
+            public boolean apply(final JavaClass input) {
                 val superclassIfAny = input.getSuperclass();
-                if(!superclassIfAny.isPresent()) {
+                if (!superclassIfAny.isPresent()) {
                     return false;
                 }
                 val superType = superclassIfAny.get();
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/CommonPredicates.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/CommonPredicates.java
index 33b4cc5..dacf942 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/CommonPredicates.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/CommonPredicates.java
@@ -42,6 +42,16 @@ import lombok.experimental.UtilityClass;
 @UtilityClass
 class CommonPredicates {
 
+    static DescribedPredicate<JavaClass> notAbstract() {
+        return new DescribedPredicate<>("not abstract") {
+            @Override
+            public boolean apply(JavaClass javaClass) {
+                val isAbstract = javaClass.getModifiers().stream().anyMatch((JavaModifier x) -> x == JavaModifier.ABSTRACT);
+                return !isAbstract;
+            }
+        };
+    }
+
     static DescribedPredicate<JavaAnnotation<?>> DomainObject_nature_ENTITY() {
         return DomainObject_nature(Nature.ENTITY);
     }
@@ -50,6 +60,10 @@ class CommonPredicates {
         return DomainObject_nature(Nature.MIXIN);
     }
 
+    static DescribedPredicate<JavaAnnotation<?>> DomainObject_nature_VIEW_MODEL() {
+        return DomainObject_nature(Nature.VIEW_MODEL);
+    }
+
     static DescribedPredicate<JavaAnnotation<?>> DomainObject_nature(final Nature expectedNature) {
         return new DescribedPredicate<>(String.format("@DomainObject(nature=%s)", expectedNature.name())) {
             @Override
@@ -131,4 +145,14 @@ class CommonPredicates {
         };
     }
 
+    static DescribedPredicate<JavaClass> areNotAbstract() {
+        return new DescribedPredicate<JavaClass>("are not abstract") {
+            @Override
+            public boolean apply(JavaClass javaClass) {
+                val isAbstract = javaClass.getModifiers().stream().anyMatch((JavaModifier x) -> x == JavaModifier.ABSTRACT);
+                return !isAbstract;
+            }
+        };
+    }
+
 }