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 2014/08/13 00:23:13 UTC

[13/21] git commit: ISIS-839: updating todoapp archetype

ISIS-839: updating todoapp archetype


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/04e8ba20
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/04e8ba20
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/04e8ba20

Branch: refs/heads/prepare/isis-1.6.0-RC2
Commit: 04e8ba20ddeb32e9db25c9a0bd387980dcdd872d
Parents: 967d4c5
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Mon Jul 21 16:07:07 2014 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Mon Jul 21 16:07:58 2014 +0100

----------------------------------------------------------------------
 example/archetype/todoapp/pom.xml               |    8 +-
 .../META-INF/maven/archetype-metadata.xml       |    2 +-
 .../resources/archetype-resources/dom/pom.xml   |   16 +-
 .../dom/src/main/java/app/ToDoAppDashboard.java |    9 -
 .../main/java/app/ToDoAppDashboardService.java  |   41 +-
 .../dom/src/main/java/app/ToDoItemAnalysis.java |   13 +-
 .../java/app/ToDoItemAnalysisContributions.java |   10 +-
 .../java/app/ToDoItemsByCategoryViewModel.java  |   15 -
 .../java/app/ToDoItemsByDateRangeViewModel.java |   16 -
 .../dom/src/main/java/dom/todo/Categorized.java |   35 +
 .../dom/src/main/java/dom/todo/ToDoItem.java    |  126 ++-
 .../src/main/java/dom/todo/ToDoItem.layout.json |  192 ++--
 .../dom/todo/ToDoItemChangedPayloadFactory.java |    1 +
 .../java/dom/todo/ToDoItemContributions.java    |   27 +-
 .../java/dom/todo/ToDoItemSubscriptions.java    |  248 ++++-
 .../dom/src/main/java/dom/todo/ToDoItems.java   |   41 +-
 .../src/test/java/dom/todo/ToDoItemTest.java    |   94 ++
 .../test/java/dom/todo/ToDoTest_completed.java  |   71 --
 .../java/dom/todo/ToDoTest_notYetCompleted.java |   71 --
 .../archetype-resources/fixture/pom.xml         |    2 +-
 .../fixture/todo/ToDoItemsFixturesService.java  |   10 +
 .../archetype-resources/integtests/pom.xml      |   10 +-
 .../java/integration/ToDoSystemInitializer.java |   47 +-
 .../tests/AbstractToDoIntegTest.java            |   41 +
 .../java/integration/tests/ToDoIntegTest.java   |   41 -
 .../tests/ToDoItemContributionsIntegTest.java   |  152 +++
 .../integration/tests/ToDoItemIntegTest.java    | 1040 ++++++++++++++++++
 .../integration/tests/ToDoItemsIntegTest.java   |  116 ++
 ...oDoItemContributionsTest_updateCategory.java |  106 --
 .../tests/actions/ToDoItemTest_completed.java   |  174 ---
 .../tests/actions/ToDoItemTest_duplicate.java   |   83 --
 .../actions/ToDoItemTest_notYetCompleted.java   |   79 --
 .../ToDoItemContributionsTest_similarTo.java    |   71 --
 .../colls/ToDoItemTest_dependencies_add.java    |  181 ---
 .../colls/ToDoItemTest_dependencies_remove.java |  176 ---
 .../ToDoItemContributionsTest_priority.java     |   75 --
 .../tests/props/ToDoItemTest_attachment.java    |   82 --
 .../tests/props/ToDoItemTest_category.java      |   62 --
 .../tests/props/ToDoItemTest_cost.java          |  100 --
 .../tests/props/ToDoItemTest_description.java   |  220 ----
 .../tests/props/ToDoItemTest_dueBy.java         |  106 --
 .../tests/props/ToDoItemTest_notes.java         |   90 --
 .../tests/props/ToDoItemTest_ownedBy.java       |   61 -
 .../tests/props/ToDoItemTest_subcategory.java   |   62 --
 .../tests/repo/ToDoItemsTest_finders.java       |   84 --
 .../repo/ToDoItemsTest_newToDo_and_delete.java  |   68 --
 .../tests/smoke/ToDoItemTest_title.java         |  116 --
 .../main/resources/archetype-resources/pom.xml  |   40 +-
 .../launch/ToDoApp-PROTOTYPE-jrebel.launch      |    2 +-
 .../ToDoApp-PROTOTYPE-with-fixtures.launch      |    2 +-
 .../ide/eclipse/launch/ToDoApp-PROTOTYPE.launch |    2 +-
 .../ide/eclipse/launch/ToDoApp-SERVER.launch    |    2 +-
 .../archetype-resources/webapp/pom.xml          |   52 +-
 .../src/main/java/webapp/admin/Admin.java       |    1 +
 .../java/webapp/admin/AdminContributions.java   |    7 +-
 .../webapp/prototyping/DeveloperUtilities.java  |    6 +-
 .../prototyping/ExternalLinksService.java       |    8 +-
 .../prototyping/WicketDeveloperUtilities.java   |   42 +
 .../webapp/ToDoApplication_en.properties        |   54 +
 .../webapp/src/main/resources/webapp/realm1.ini |   26 +
 .../src/main/resources/webapp/welcome.html      |    2 +-
 .../src/main/webapp/WEB-INF/isis.properties     |   76 +-
 .../src/main/webapp/WEB-INF/logging.properties  |   32 +
 .../main/webapp/WEB-INF/persistor.properties    |   12 +-
 .../webapp/src/main/webapp/WEB-INF/shiro.ini    |   10 +
 .../webapp/src/main/webapp/about/index.html     |   54 +-
 .../webapp/src/main/webapp/css/application.css  |    2 +-
 .../projects/basic/archetype.properties         |    2 +-
 68 files changed, 2177 insertions(+), 2748 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/pom.xml
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/pom.xml b/example/archetype/todoapp/pom.xml
index 90d7b9c..19c35fe 100644
--- a/example/archetype/todoapp/pom.xml
+++ b/example/archetype/todoapp/pom.xml
@@ -17,13 +17,13 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.apache.isis.archetype</groupId>
-  <artifactId>quickstart_wicket_restful_jdo-archetype</artifactId>
+  <artifactId>todoapp-archetype</artifactId>
   <version>1.6.0-SNAPSHOT</version>
   <packaging>maven-archetype</packaging>
-  <name>quickstart_wicket_restful_jdo-archetype</name>
+  <name>todoapp-archetype</name>
   <build>
     <extensions>
       <extension>
@@ -44,7 +44,7 @@
   <parent>
     <groupId>org.apache.isis.core</groupId>
     <artifactId>isis</artifactId>
-    <version>1.6.0-SNAPSHOT</version>
+    <version>1.6.0</version>
     <relativePath>../../../core/pom.xml</relativePath>
   </parent>
 </project>

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/META-INF/maven/archetype-metadata.xml
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/META-INF/maven/archetype-metadata.xml b/example/archetype/todoapp/src/main/resources/META-INF/maven/archetype-metadata.xml
index 6e02d99..4addf7b 100644
--- a/example/archetype/todoapp/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/example/archetype/todoapp/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -17,7 +17,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<archetype-descriptor xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0" xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" name="quickstart_wicket_restful_jdo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<archetype-descriptor xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0" xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" name="todoapp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <modules>
     <module id="${rootArtifactId}-dom" dir="dom" name="${rootArtifactId}-dom">
       <fileSets>

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/pom.xml
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/pom.xml b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/pom.xml
index d8ad66c..4b60832 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/pom.xml
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/pom.xml
@@ -26,7 +26,7 @@
 	</parent>
 
 	<artifactId>${artifactId}</artifactId>
-	<name>Quickstart Wicket/Restful/JDO DOM</name>
+	<name>ToDo App DOM</name>
 
 	<build>
         <resources>
@@ -101,11 +101,15 @@
 			<artifactId>isis-core-applib</artifactId>
 		</dependency>
 
-		<dependency>
-            <groupId>org.apache.isis.objectstore</groupId>
-			<artifactId>isis-objectstore-jdo-applib</artifactId>
-		</dependency>
-        
+        <dependency>
+            <groupId>org.apache.isis.module</groupId>
+            <artifactId>isis-module-settings-applib</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.isis.module</groupId>
+            <artifactId>isis-module-devutils-applib</artifactId>
+        </dependency>
+
 		<dependency>
             <groupId>org.apache.isis.core</groupId>
 			<artifactId>isis-core-unittestsupport</artifactId>

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboard.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboard.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboard.java
index bf007d5..c63b566 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboard.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboard.java
@@ -32,7 +32,6 @@ import org.apache.isis.applib.annotation.Render.Type;
 public class ToDoAppDashboard extends AbstractViewModel {
 
     //region > identification in the UI
-    // //////////////////////////////////////
     public String title() {
         return "Dashboard";
     }
@@ -43,8 +42,6 @@ public class ToDoAppDashboard extends AbstractViewModel {
     //endregion
 
     //region > ViewModel contract
-    // //////////////////////////////////////
-
     private String memento;
     
     @Override
@@ -60,8 +57,6 @@ public class ToDoAppDashboard extends AbstractViewModel {
     //endregion
 
     //region > getAnalysisByCategory (collection)
-    // //////////////////////////////////////
-
     @Named("By Category")
     @Render(Type.EAGERLY)
     @Disabled
@@ -71,8 +66,6 @@ public class ToDoAppDashboard extends AbstractViewModel {
     //endregion
 
     //region > getAnalysisByDateRange (collection)
-    // //////////////////////////////////////
-    
     @Named("By Date Range")
     @Render(Type.EAGERLY)
     @Disabled
@@ -82,8 +75,6 @@ public class ToDoAppDashboard extends AbstractViewModel {
     //endregion
 
     //region > injected services
-    // //////////////////////////////////////
-    
     @javax.inject.Inject
     private ToDoItemAnalysis toDoItemAnalysis;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboardService.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboardService.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboardService.java
index abd6268..0062a3e 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboardService.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoAppDashboardService.java
@@ -21,45 +21,16 @@
  */
 package app;
 
-import org.apache.isis.applib.DomainObjectContainer;
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.Hidden;
-import org.apache.isis.applib.annotation.HomePage;
+import org.apache.isis.applib.services.homepage.AbstractHomePageDashboardService;
 
+@DomainService
 @Hidden
-public class ToDoAppDashboardService  {
+public class ToDoAppDashboardService extends AbstractHomePageDashboardService<ToDoAppDashboard> {
 
-    //region > identification in the UI
-    // //////////////////////////////////////
-
-    private static final String ID = "dashboard";
-
-    public String getId() {
-        return ID;
-    }
-
-    public String iconName() {
-        return ID;
-    }
-    //endregion
-
-    //region > lookup (action)
-    // //////////////////////////////////////
-    @ActionSemantics(Of.SAFE)
-    @HomePage
-    public ToDoAppDashboard lookup() {
-        return container.newViewModelInstance(ToDoAppDashboard.class, ID);
+    public ToDoAppDashboardService() {
+        super(ToDoAppDashboard.class);
     }
 
-    //endregion
-
-    //region > injected services
-    // //////////////////////////////////////
-
-    @javax.inject.Inject
-    private DomainObjectContainer container;
-
-    //endregion
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysis.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysis.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysis.java
index 1d4c73b..9609164 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysis.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysis.java
@@ -33,12 +33,11 @@ import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
 
 @Named("Analysis")
+@DomainService(menuOrder = "20")
 public class ToDoItemAnalysis {
 
 
     //region > identification in the UI
-    // //////////////////////////////////////
-
     public String getId() {
         return "analysis";
     }
@@ -49,8 +48,6 @@ public class ToDoItemAnalysis {
     //endregion
 
     //region > byCategory (action)
-    // //////////////////////////////////////
-
     @Named("By Category")
     @Bookmarkable
     @ActionSemantics(Of.SAFE)
@@ -71,12 +68,10 @@ public class ToDoItemAnalysis {
              }
          };
     }
-
     //endregion
 
     //region > byDateRange (action)
-    // //////////////////////////////////////
-    
+
     public enum DateRange {
         OverDue,
         Today,
@@ -109,8 +104,6 @@ public class ToDoItemAnalysis {
     //endregion
 
     //region > forCategory (programmatic)
-    // //////////////////////////////////////
-
     @Programmatic
     public ToDoItemsByCategoryViewModel toDoItemsForCategory(Category category) {
         return byCategory().apply(category);
@@ -119,8 +112,6 @@ public class ToDoItemAnalysis {
     //endregion
 
     //region > injected services
-    // //////////////////////////////////////
-
     @javax.inject.Inject
     private DomainObjectContainer container;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysisContributions.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysisContributions.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysisContributions.java
index ead5e79..21ed821 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysisContributions.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemAnalysisContributions.java
@@ -23,20 +23,16 @@ package app;
 
 import dom.todo.ToDoItem;
 
-import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.ActionSemantics.Of;
-import org.apache.isis.applib.annotation.Hidden;
-import org.apache.isis.applib.annotation.NotContributed;
 import org.apache.isis.applib.annotation.NotContributed.As;
-import org.apache.isis.applib.annotation.NotInServiceMenu;
 
+@DomainService
 @Hidden
 public class ToDoItemAnalysisContributions {
 
 
     //region > analyseCategory (action)
-    // //////////////////////////////////////
-
     @NotInServiceMenu
     @NotContributed(As.ASSOCIATION)
     @ActionSemantics(Of.SAFE)
@@ -46,8 +42,6 @@ public class ToDoItemAnalysisContributions {
     //endregion
 
     //region > injected services
-    // //////////////////////////////////////
-
     @javax.inject.Inject
     private ToDoItemAnalysis toDoAppAnalysis;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByCategoryViewModel.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByCategoryViewModel.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByCategoryViewModel.java
index 68f23b0..f92cbac 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByCategoryViewModel.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByCategoryViewModel.java
@@ -44,8 +44,6 @@ public class ToDoItemsByCategoryViewModel
         implements Comparable<ToDoItemsByCategoryViewModel> {
 
     //region > viewModel implementation
-    // //////////////////////////////////////
-
     @Override
     public String viewModelMemento() {
         return getCategory().name();
@@ -55,12 +53,9 @@ public class ToDoItemsByCategoryViewModel
     public void viewModelInit(String memento) {
         setCategory(Category.valueOf(memento));
     }
-
     //endregion
 
     //region > category (property)
-    // //////////////////////////////////////
-
     private Category category;
 
     /**
@@ -77,8 +72,6 @@ public class ToDoItemsByCategoryViewModel
     //endregion
 
     //region > notYetComplete (property), complete (property)
-    // //////////////////////////////////////
-
     @MultiLine(numberOfLines=5)
     public String getNotYetComplete() {
         final List<ToDoItem> notYetComplete = getItemsNotYetComplete();
@@ -115,8 +108,6 @@ public class ToDoItemsByCategoryViewModel
     //endregion
 
     //region > getItemsNotYetComplete (collection), getItemsComplete (collection)
-    // //////////////////////////////////////
-
     /**
      * All those items {@link ToDoItems${symbol_pound}notYetComplete() not yet complete}, for this {@link ${symbol_pound}getCategory() category}.
      */
@@ -138,8 +129,6 @@ public class ToDoItemsByCategoryViewModel
     //endregion
 
     //region > deleteCompleted (action)
-    // //////////////////////////////////////
-
     @Named("Delete")
     public ToDoItemsByCategoryViewModel deleteCompleted() {
         for (ToDoItem item : getItemsComplete()) {
@@ -152,8 +141,6 @@ public class ToDoItemsByCategoryViewModel
     //endregion
 
     //region > compareTo
-    // //////////////////////////////////////
-
     @Override
     public int compareTo(ToDoItemsByCategoryViewModel other) {
         return ObjectContracts.compare(this, other, "category");
@@ -161,8 +148,6 @@ public class ToDoItemsByCategoryViewModel
     //endregion
 
     //region > injected services
-    // //////////////////////////////////////
-
     @javax.inject.Inject
     private ToDoItems toDoItems;
     //endregion

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByDateRangeViewModel.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByDateRangeViewModel.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByDateRangeViewModel.java
index ecd2f45..5d2b118 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByDateRangeViewModel.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/app/ToDoItemsByDateRangeViewModel.java
@@ -51,8 +51,6 @@ public class ToDoItemsByDateRangeViewModel
         implements Comparable<ToDoItemsByDateRangeViewModel> {
 
     //region > viewModel implementation
-    // //////////////////////////////////////
-
     @Override
     public String viewModelMemento() {
         return getDateRange().name();
@@ -66,10 +64,6 @@ public class ToDoItemsByDateRangeViewModel
     //endregion
 
     //region > dateRange (property)
-    // //////////////////////////////////////
-    //
-    // //////////////////////////////////////
-
     private DateRange dateRange;
 
     /**
@@ -86,17 +80,12 @@ public class ToDoItemsByDateRangeViewModel
     //endregion
 
     //region > count (derived property)
-    // //////////////////////////////////////
-
     public int getCount() {
         return getItemsNotYetComplete().size();
     }
-
     //endregion
 
     //region > getItemsNotYetComplete (collection)
-    // //////////////////////////////////////
-
     /**
      * All those items {@link ToDoItems${symbol_pound}notYetComplete() not yet complete}, for this {@link ${symbol_pound}getCategory() category}.
      */
@@ -139,8 +128,6 @@ public class ToDoItemsByDateRangeViewModel
     //endregion
 
     //region > compareTo
-    // //////////////////////////////////////
-
     @Override
     public int compareTo(ToDoItemsByDateRangeViewModel other) {
         return ObjectContracts.compare(this, other, "dateRange");
@@ -148,14 +135,11 @@ public class ToDoItemsByDateRangeViewModel
     //endregion
 
     //region > injected services
-    // //////////////////////////////////////
-    
     @javax.inject.Inject
     private ToDoItems toDoItems;
 
     @javax.inject.Inject
     private ClockService clockService;
-
     //endregion
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/Categorized.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/Categorized.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/Categorized.java
new file mode 100644
index 0000000..9bb6bad
--- /dev/null
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/Categorized.java
@@ -0,0 +1,35 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package dom.todo;
+
+/**
+ * Enables {@link dom.todo.ToDoItemContributions${symbol_pound}updateCategory(dom.todo.Categorized, dom.todo.ToDoItem.Category, dom.todo.ToDoItem.Subcategory)} to be contributed to
+ * both the {@link dom.todo.ToDoItem} (entity) and also the {@link app.ToDoItemWizard} (wizard).
+ */
+public interface Categorized {
+
+    ToDoItem.Category getCategory();
+    void setCategory(ToDoItem.Category category);
+
+    ToDoItem.Subcategory getSubcategory();
+    void setSubcategory(ToDoItem.Subcategory subcategory);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.java
index 13672f8..4ddc10d 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.java
@@ -42,11 +42,10 @@ import org.apache.isis.applib.annotation.Bulk.AppliesTo;
 import org.apache.isis.applib.annotation.Bulk.InteractionContext.InvokedAs;
 import org.apache.isis.applib.annotation.Command.ExecuteIn;
 import org.apache.isis.applib.annotation.Optional;
-import org.apache.isis.applib.clock.Clock;
 import org.apache.isis.applib.services.background.BackgroundService;
 import org.apache.isis.applib.services.clock.ClockService;
 import org.apache.isis.applib.services.command.CommandContext;
-import org.apache.isis.applib.services.eventbus.ActionInvokedEvent;
+import org.apache.isis.applib.services.eventbus.ActionInteractionEvent;
 import org.apache.isis.applib.services.eventbus.EventBusService;
 import org.apache.isis.applib.services.scratchpad.Scratchpad;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
@@ -104,7 +103,7 @@ import org.apache.isis.applib.value.Clob;
 @AutoComplete(repository=ToDoItems.class, action="autoComplete") // default unless overridden by autoCompleteNXxx() method
 //@Bounded - if there were a small number of instances only (overrides autoComplete functionality)
 @Bookmarkable
-public class ToDoItem implements Comparable<ToDoItem> {
+public class ToDoItem implements Categorized, Comparable<ToDoItem> {
 
     //region > LOG
     /**
@@ -115,7 +114,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     // region > title, icon
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     public String title() {
         final TitleBuffer buf = new TitleBuffer();
         buf.append(getDescription());
@@ -135,13 +133,11 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > description (property)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     private String description;
 
     @javax.jdo.annotations.Column(allowsNull="false", length=100)
-    @PostsPropertyChangedEvent()
+    @PropertyInteraction()
     @RegEx(validation = "${symbol_escape}${symbol_escape}w[@&:${symbol_escape}${symbol_escape}-${symbol_escape}${symbol_escape},${symbol_escape}${symbol_escape}.${symbol_escape}${symbol_escape}+ ${symbol_escape}${symbol_escape}w]*") 
-    @TypicalLength(50)
     public String getDescription() {
         return description;
     }
@@ -158,7 +154,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > dueBy (property)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     @javax.jdo.annotations.Persistent(defaultFetchGroup="true")
     private LocalDate dueBy;
 
@@ -178,13 +173,12 @@ public class ToDoItem implements Comparable<ToDoItem> {
         if (dueBy == null) {
             return null;
         }
-        return isMoreThanOneWeekInPast(dueBy) ? "Due by date cannot be more than one week old" : null;
+        return toDoItems.validateDueBy(dueBy);
     }
     //endregion
 
     //region > category and subcategory (property)
 
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     public static enum Category {
         Professional {
             @Override
@@ -243,6 +237,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
     private Category category;
 
     @javax.jdo.annotations.Column(allowsNull="false")
+    @Disabled(reason="Use action to update both category and subcategory")
     public Category getCategory() {
         return category;
     }
@@ -256,6 +251,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
     private Subcategory subcategory;
 
     @javax.jdo.annotations.Column(allowsNull="true")
+    @Disabled(reason="Use action to update both category and subcategory")
     public Subcategory getSubcategory() {
         return subcategory;
     }
@@ -265,7 +261,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > ownedBy (property)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
     private String ownedBy;
 
     @javax.jdo.annotations.Column(allowsNull="false")
@@ -279,7 +275,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > complete (property), completed (action), notYetCompleted (action)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
     private boolean complete;
 
     @Disabled
@@ -291,7 +287,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
         this.complete = complete;
     }
 
-    @PostsActionInvokedEvent(CompletedEvent.class)
+    @ActionInteraction(CompletedEvent.class)
     @Command
     @PublishedAction
     @Bulk
@@ -319,7 +315,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
         return isComplete() ? "Already completed" : null;
     }
 
-    @PostsActionInvokedEvent(NoLongerCompletedEvent.class)
+    @ActionInteraction(NoLongerCompletedEvent.class)
     @Command
     @PublishedAction
     @Bulk
@@ -349,7 +345,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > cost (property), updateCost (action)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     private BigDecimal cost;
 
     @javax.jdo.annotations.Column(allowsNull="true", scale=2)
@@ -362,7 +357,8 @@ public class ToDoItem implements Comparable<ToDoItem> {
     public void setCost(final BigDecimal cost) {
         this.cost = cost!=null?cost.setScale(2):null;
     }
-    
+
+    @ActionSemantics(Of.IDEMPOTENT)
     public ToDoItem updateCost(
             @Named("New cost") 
             @javax.validation.constraints.Digits(integer=10, fraction=2) 
@@ -393,7 +389,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > notes (property)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     private String notes;
 
     @javax.jdo.annotations.Column(allowsNull="true", length=400)
@@ -407,7 +402,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > attachment (property)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     private Blob attachment;
     @javax.jdo.annotations.Persistent(defaultFetchGroup="false", columns = {
             @javax.jdo.annotations.Column(name = "attachment_name"),
@@ -425,7 +419,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > doc (property)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     private Clob doc;
     @javax.jdo.annotations.Persistent(defaultFetchGroup="false", columns = {
             @javax.jdo.annotations.Column(name = "doc_name"),
@@ -443,7 +436,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > version (derived property)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     public Long getVersionSequence() {
         if(!(this instanceof javax.jdo.spi.PersistenceCapable)) {
             return null;
@@ -459,7 +451,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > dependencies (property), add (action), remove (action)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
     // overrides the natural ordering
     public static class DependenciesComparator implements Comparator<ToDoItem> {
@@ -481,8 +472,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
     @javax.jdo.annotations.Element(column="dependentId")
     private SortedSet<ToDoItem> dependencies = new TreeSet<ToDoItem>();
 
-    @PostsCollectionAddedToEvent
-    @PostsCollectionRemovedFromEvent
+    @CollectionInteraction
     @SortedBy(DependenciesComparator.class)
     public SortedSet<ToDoItem> getDependencies() {
         return dependencies;
@@ -491,7 +481,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
     public void setDependencies(final SortedSet<ToDoItem> dependencies) {
         this.dependencies = dependencies;
     }
-    
+
     public void addToDependencies(final ToDoItem toDoItem) {
         getDependencies().add(toDoItem);
     }
@@ -504,7 +494,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
             @TypicalLength(20)
             final ToDoItem toDoItem) {
     	// By wrapping the call, Isis will detect that the collection is modified 
-    	// and it will automatically send a CollectionAddedToEvent to the Event Bus.
+    	// and it will automatically send CollectionInteractionEvents to the Event Bus.
     	// ToDoItemSubscriptions is a demo subscriber to this event
         wrapperFactory.wrapSkipRules(this).addToDependencies(toDoItem);
         return this;
@@ -537,7 +527,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
             @TypicalLength(20)
             final ToDoItem toDoItem) {
     	// By wrapping the call, Isis will detect that the collection is modified 
-    	// and it will automatically send a CollectionRemovedFromEvent to the Event Bus.
+    	// and it will automatically send a CollectionInteractionEvent to the Event Bus.
         // ToDoItemSubscriptions is a demo subscriber to this event
         wrapperFactory.wrapSkipRules(this).removeFromDependencies(toDoItem);
         return this;
@@ -563,7 +553,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > clone (action)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
     // the name of the action in the UI
     // nb: method is not called "clone()" is inherited by java.lang.Object and
@@ -602,8 +591,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > delete (action)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-    @PostsActionInvokedEvent(DeletedEvent.class)
+    @ActionInteraction(DeletedEvent.class)
     @Bulk
     public List<ToDoItem> delete() {
         
@@ -617,7 +605,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > totalCost (property)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     @ActionSemantics(Of.SAFE)
     @Bulk(AppliesTo.BULK_ONLY)
     public BigDecimal totalCost() {
@@ -631,7 +618,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > scheduleExplicitly (action), scheduleImplicitly (background action)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     @ActionSemantics(Of.IDEMPOTENT)
     @Prototype
     public ToDoItem scheduleExplicitly() {
@@ -640,8 +626,6 @@ public class ToDoItem implements Comparable<ToDoItem> {
         return this;
     }
     
-    // //////////////////////////////////////
-
     @ActionSemantics(Of.IDEMPOTENT)
     @Command(executeIn=ExecuteIn.BACKGROUND)
     @Prototype
@@ -652,17 +636,15 @@ public class ToDoItem implements Comparable<ToDoItem> {
     //endregion
 
     //region > openSourceCodeOnGithub (action)
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     @Prototype
     @ActionSemantics(Of.SAFE)
     public URL openSourceCodeOnGithub() throws MalformedURLException {
-        return new URL("https://github.com/apache/isis/tree/master/example/application/${parentArtifactId}/dom/src/main/java/dom/todo/ToDoItem.java");
+        return new URL("https://github.com/apache/isis/tree/master/example/application/quickstart_wicket_restful_jdo/dom/src/main/java/dom/todo/ToDoItem.java");
     }
     //endregion
 
     //region > demoException (action)
 
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     static enum DemoExceptionType {
         RecoverableException,
         RecoverableExceptionAutoEscalated,
@@ -691,8 +673,41 @@ public class ToDoItem implements Comparable<ToDoItem> {
     }
     //endregion
 
+    //region > lifecycle callbacks
+
+    public void created() {
+        LOG.debug("lifecycle callback: created: " + container.titleOf(this));
+    }
+
+    public void loaded() {
+        LOG.debug("lifecycle callback: loaded: " + container.titleOf(this));
+    }
+
+    public void persisting() {
+        LOG.debug("lifecycle callback: persisting: " + container.titleOf(this));
+    }
+
+    public void persisted() {
+        LOG.debug("lifecycle callback: persisted: " + container.titleOf(this));
+    }
+
+    public void updating() {
+        LOG.debug("lifecycle callback: updating: " + container.titleOf(this));
+    }
+    public void updated() {
+        LOG.debug("lifecycle callback: updated: " + container.titleOf(this));
+    }
+
+    public void removing() {
+        LOG.debug("lifecycle callback: removing: " + container.titleOf(this));
+    }
+
+    public void removed() {
+        LOG.debug("lifecycle callback: removed: " + container.titleOf(this));
+    }
+    //endregion
+
     //region > object-level validation
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
     /**
      * In a real app, if this were actually a rule, then we'd expect that
@@ -705,36 +720,27 @@ public class ToDoItem implements Comparable<ToDoItem> {
         }
         return null;
     }
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     //endregion
 
     //region > programmatic helpers
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-    private static final long ONE_WEEK_IN_MILLIS = 7 * 24 * 60 * 60 * 1000L;
-
     @Programmatic // excluded from the framework's metamodel
     public boolean isDue() {
         if (getDueBy() == null) {
             return false;
         }
-        return !isMoreThanOneWeekInPast(getDueBy());
-    }
-
-    private static boolean isMoreThanOneWeekInPast(final LocalDate dueBy) {
-        return dueBy.toDateTimeAtStartOfDay().getMillis() < Clock.getTime() - ONE_WEEK_IN_MILLIS;
+        return !toDoItems.isMoreThanOneWeekInPast(getDueBy());
     }
     //endregion
 
     //region > events
 
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-    public static abstract class AbstractActionInvokedEvent extends ActionInvokedEvent<ToDoItem> {
+    public static abstract class AbstractActionInteractionEvent extends ActionInteractionEvent<ToDoItem> {
         private static final long serialVersionUID = 1L;
         private final String description;
-        public AbstractActionInvokedEvent(
-                final String description, 
-                final ToDoItem source, 
-                final Identifier identifier, 
+        public AbstractActionInteractionEvent(
+                final String description,
+                final ToDoItem source,
+                final Identifier identifier,
                 final Object... arguments) {
             super(source, identifier, arguments);
             this.description = description;
@@ -744,7 +750,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
         }
     }
 
-    public static class CompletedEvent extends AbstractActionInvokedEvent {
+    public static class CompletedEvent extends AbstractActionInteractionEvent {
         private static final long serialVersionUID = 1L;
         public CompletedEvent(
                 final ToDoItem source, 
@@ -754,7 +760,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
         }
     }
 
-    public static class NoLongerCompletedEvent extends AbstractActionInvokedEvent {
+    public static class NoLongerCompletedEvent extends AbstractActionInteractionEvent {
         private static final long serialVersionUID = 1L;
         public NoLongerCompletedEvent(
                 final ToDoItem source, 
@@ -764,7 +770,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
         }
     }
 
-    public static class DeletedEvent extends AbstractActionInvokedEvent {
+    public static class DeletedEvent extends AbstractActionInteractionEvent {
         private static final long serialVersionUID = 1L;
         public DeletedEvent(
                 final ToDoItem source, 
@@ -773,12 +779,11 @@ public class ToDoItem implements Comparable<ToDoItem> {
             super("deleted", source, identifier, arguments);
         }
     }
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
     //endregion
 
     //region > predicates
 
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     public static class Predicates {
         
         public static Predicate<ToDoItem> thoseOwnedBy(final String currentUser) {
@@ -853,7 +858,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
         }
 
     }
-    // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
     //endregion
 
     //region > toString, compareTo
@@ -884,12 +889,12 @@ public class ToDoItem implements Comparable<ToDoItem> {
     @javax.inject.Inject
     @SuppressWarnings("unused")
     private ClockService clockService;
-    
+
     Bulk.InteractionContext bulkInteractionContext;
     public void injectBulkInteractionContext(Bulk.InteractionContext bulkInteractionContext) {
         this.bulkInteractionContext = bulkInteractionContext;
     }
-    
+
     @SuppressWarnings("unused")
     @javax.inject.Inject
     private CommandContext commandContext;
@@ -907,6 +912,7 @@ public class ToDoItem implements Comparable<ToDoItem> {
 
     @javax.inject.Inject
     private WrapperFactory wrapperFactory;
+
     // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     //endregion
 

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.layout.json
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.layout.json b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.layout.json
index db9e7d2..f15424d 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.layout.json
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItem.layout.json
@@ -15,68 +15,62 @@
  *  limitations under the License.
  */
 {
-    columns: [
+    "columns": [
     {
-        span: 6,
-        memberGroups: {
-            General: {
-                members: {
-                    description: {
-                        typicalLength: { value: 50 }
+        "span": 6,
+        "memberGroups": {
+            "General": {
+                "members": {
+                    "description": {
+                        "typicalLength": { "value": 50 }
                     },
-                    category: {
-                        disabled: {
-                            reason: "Use action to update both category and subcategory"
-                        }
+                    "category": {
                     },
-                    subcategory: {
-                        disabled: {
-                            reason: "Use action to update both category and subcategory"
-                        },
-                        actions: {
-                            updateCategory: {
-                                named: {
-                                    value: "Update"
+                    "subcategory": {
+                        "actions": {
+                            "updateCategory": {
+                                "named": {
+                                    "value": "Update"
                                 }
                             },
-                            analyseCategory: {}
+                            "analyseCategory": {}
                         }
                     },
-                    ownedBy: {
-                        hidden: {}
+                    "ownedBy": {
+                        "hidden": {}
                     },
-                    complete: {
-                        actions: {
-                            completed: {
-                                named: { value: "Done" },
-                                describedAs: {
-                                    value: "Mark this todo item as having been completed"
+                    "complete": {
+                        "actions": {
+                            "completed": {
+                                "named": { "value": "Done" },
+                                "describedAs": {
+                                    "value": "Mark this todo item as having been completed"
                                 },
-                                cssClass: { value: "x-highlight" }
+                                "cssClass": { "value": "x-highlight" }
                             },
-                            notYetCompleted: {
-                                named: { value: "Not done" }
+                            "notYetCompleted": {
+                                "named": { "value": "Not done" }
                             },
-                            scheduleExplicitly: {
+                            "scheduleExplicitly": {
                             },
-                            scheduleImplicitly: {
+                            "scheduleImplicitly": {
                             }
                         },
-                        describedAs: {
-                            value: "Whether this todo item has been completed"
+                        "describedAs": {
+                            "value": "Whether this todo item has been completed"
                         }
                     }
                 }
             },
-            Misc: {
-                members: {
-                    versionSequence: {
-                        named: { 
-                            value: "Version" 
+            "Misc": {
+                "members": {
+                    "versionSequence": {
+                        "named": {
+                            "value": "Version"
                         },
-                        disabled: {},
-                        hidden: {
-                            where: ALL_TABLES
+                        "disabled": {},
+                        "hidden": {
+                            "where": "ALL_TABLES"
                         }
                     }
                 }
@@ -84,48 +78,48 @@
         }
     },
     {
-        span: 6,
-        memberGroups: {
-            Priority: {
-                members: {
-                     relativePriority: {
-                        actions: {
-                            previous: {},
-                            next: {}
+        "span": 6,
+        "memberGroups": {
+            "Priority": {
+                "members": {
+                     "relativePriority": {
+                        "actions": {
+                            "previous": {},
+                            "next": {}
                         }
                     },
-                    dueBy: {
-                        cssClass: { value: "x-key" }
+                    "dueBy": {
+                        "cssClass": { "value": "x-key" }
                     }
                 }
             },
-            Other: {
-                members: {
-                    cost: {
-                        actions: {
-                            updateCost:{
-                                named: {
-                                    value: "Update"
+            "Other": {
+                "members": {
+                    "cost": {
+                        "actions": {
+                            "updateCost":{
+                                "named": {
+                                    "value": "Update"
                                 }
                             }
                         }
                     },
-                    notes: {
-                        multiLine: {
-                            numberOfLines: 5
+                    "notes": {
+                        "multiLine": {
+                            "numberOfLines": 5
                         },
-                        hidden: {
-                            where: ALL_TABLES
+                        "hidden": {
+                            "where": "ALL_TABLES"
                         }
                     },
-                    attachment: {
-                        hidden: {
-                            where: STANDALONE_TABLES
+                    "attachment": {
+                        "hidden": {
+                            "where": "STANDALONE_TABLES"
                         }
                     },
-                    doc: {
-                        hidden: {
-                            where: STANDALONE_TABLES
+                    "doc": {
+                        "hidden": {
+                            "where": "STANDALONE_TABLES"
                         }
                     }
                 }
@@ -133,49 +127,49 @@
         }
     },
     {
-        span: 0
+        "span": 0
     },
     {
-        span: 6,
-        collections: {
-            dependencies: {
-                disabled: {},
-                actions: {
-                    add:{},
-                    remove: {
-                        cssClass: { value: "x-caution" }
+        "span": 6,
+        "collections": {
+            "dependencies": {
+                "disabled": {},
+                "actions": {
+                    "add":{},
+                    "remove": {
+                        "cssClass": { "value": "x-caution" }
                     }
                 },
-                paged: {
-                    value: 5
+                "paged": {
+                    "value": 5
                 },
-                render: {
-                    value: EAGERLY
+                "render": {
+                    "value": "EAGERLY"
                 }
             },
-            similarTo: {
-                disabled: {},
-                paged: {
-                    value: 3
+            "similarTo": {
+                "disabled": {},
+                "paged": {
+                    "value": 3
                 },
-                render: {
-                    value: LAZILY
+                "render": {
+                    "value": "LAZILY"
                 }
             }
         }
     }
     ],
-    actions: {
-        totalCost: {},
-        delete: {
-            cssClass: { value: "x-caution" }
+    "actions": {
+        "totalCost": {},
+        "delete": {
+            "cssClass": { "value": "x-caution" }
         },
-        duplicate: {
-	        named: {
-	            value: "Clone"
+        "duplicate": {
+	        "named": {
+	            "value": "Clone"
 	        },
-            describedAs: {
-                value: "Create a new todo item from this one"
+            "describedAs": {
+                "value": "Create a new todo item from this one"
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java
index 7c03f19..f88ec1c 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java
@@ -42,6 +42,7 @@ public class ToDoItemChangedPayloadFactory implements PayloadFactory{
             return getChanged().getDescription();
         }
     }
+
     @Override
     public EventPayload payloadFor(Object changedObject, ChangeKind changeKind) {
         return new ToDoItemPayload((ToDoItem) changedObject);

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemContributions.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemContributions.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemContributions.java
index eb7c606..789796d 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemContributions.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemContributions.java
@@ -39,11 +39,10 @@ import org.apache.isis.applib.annotation.NotContributed.As;
 import org.apache.isis.applib.query.QueryDefault;
 import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 
+@DomainService
 public class ToDoItemContributions extends AbstractFactoryAndRepository {
 
     //region > priority (contributed property)
-    // //////////////////////////////////////
-    
     @DescribedAs("The relative priority of this item compared to others not yet complete (using 'due by' date)")
     @NotInServiceMenu
     @ActionSemantics(Of.SAFE)
@@ -70,7 +69,6 @@ public class ToDoItemContributions extends AbstractFactoryAndRepository {
             }}, ToDoItemContributions.class, "relativePriority", toDoItem);
     }
 
-
     private List<ToDoItem> sortedNotYetComplete() {
         return ORDERING_DUE_BY
         .compound(ORDERING_DESCRIPTION)
@@ -97,8 +95,6 @@ public class ToDoItemContributions extends AbstractFactoryAndRepository {
     //endregion
 
     //region >  next, previous (contributed actions)
-    // //////////////////////////////////////
-
     @DescribedAs("The next item not yet completed")
     @NotInServiceMenu
     @ActionSemantics(Of.SAFE)
@@ -163,8 +159,6 @@ public class ToDoItemContributions extends AbstractFactoryAndRepository {
     //endregion
 
     //region > similarTo (contributed collection)
-    // //////////////////////////////////////
-    
     @NotInServiceMenu
     @ActionSemantics(Of.SAFE)
     @NotContributed(As.ACTION)
@@ -189,52 +183,45 @@ public class ToDoItemContributions extends AbstractFactoryAndRepository {
     //endregion
 
     //region > updateCategory (contributed action)
-    // //////////////////////////////////////
 
     @DescribedAs("Update category and subcategory")
     @NotInServiceMenu
     @ActionSemantics(Of.IDEMPOTENT)
-    public ToDoItem updateCategory(
-            final ToDoItem item, 
+    public Categorized updateCategory(
+            final Categorized item,
             final @Named("Category") Category category,
             final @Optional @Named("Subcategory") Subcategory subcategory) {
         item.setCategory(category);
         item.setSubcategory(subcategory);
         return item;
     }
-
     public Category default1UpdateCategory(
-            final ToDoItem item) {
+            final Categorized item) {
         return item != null? item.getCategory(): null;
     }
     public Subcategory default2UpdateCategory(
-            final ToDoItem item) {
+            final Categorized item) {
         return item != null? item.getSubcategory(): null;
     }
 
     public List<Subcategory> choices2UpdateCategory(
-            final ToDoItem item, final Category category) {
+            final Categorized item, final Category category) {
         return Subcategory.listFor(category);
     }
     
     public String validateUpdateCategory(
-            final ToDoItem item, final Category category, final Subcategory subcategory) {
+            final Categorized item, final Category category, final Subcategory subcategory) {
         return Subcategory.validate(category, subcategory);
     }
     //endregion
 
     //region > helpers
-    // //////////////////////////////////////
-    
     protected String currentUserName() {
         return getContainer().getUser().getName();
     }
-
     //endregion
 
     //region > injected services
-    // //////////////////////////////////////
-
     @javax.inject.Inject
     private ToDoItems toDoItems;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
index 1ee6d98..dab085e 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java
@@ -23,33 +23,52 @@ package dom.todo;
 
 import java.util.EventObject;
 import java.util.List;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
 import com.google.common.collect.Lists;
 import com.google.common.eventbus.Subscribe;
 import org.apache.isis.applib.DomainObjectContainer;
 import org.apache.isis.applib.NonRecoverableException;
 import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.annotation.*;
-import org.apache.isis.applib.services.eventbus.CollectionAddedToEvent;
-import org.apache.isis.applib.services.eventbus.CollectionRemovedFromEvent;
+import org.apache.isis.applib.services.eventbus.ActionInteractionEvent;
+import org.apache.isis.applib.services.eventbus.CollectionInteractionEvent;
 import org.apache.isis.applib.services.eventbus.EventBusService;
-import org.apache.isis.applib.services.eventbus.PropertyChangedEvent;
+import org.apache.isis.applib.services.eventbus.PropertyInteractionEvent;
 
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Lists.newArrayList;
+
+@DomainService
 public class ToDoItemSubscriptions {
 
     //region > LOG
     private final static org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(ToDoItemSubscriptions.class);
     //endregion
 
-
     //region > on(Event)...
-    // //////////////////////////////////////
+
     public static enum Behaviour {
-        AcceptEvents,
-        RejectEventsWithRecoverableException,
-        RejectEventsWithNonRecoverableException,
-        ThrowOtherException
+        AnyExecuteAccept,
+        AnyExecuteVetoWithRecoverableException,
+        AnyExecuteVetoWithNonRecoverableException,
+        AnyExecuteVetoWithOtherException,
+        UpdateCostActionHide,
+        UpdateCostActionDisable,
+        UpdateCostActionInvalidate,
+        DescriptionPropertyHide,
+        DescriptionPropertyDisable,
+        DescriptionPropertyInvalidate,
+        DependenciesCollectionHide,
+        // not implemented in Wicket viewer, but supported in wrapped objects
+        DependenciesCollectionDisable,
+        // not implemented in Wicket viewer, but supported in wrapped objects
+        DependenciesCollectionInvalidateAdd,
+        DependenciesCollectionInvalidateRemove,
+        SimilarToCollectionHide
     }
-    private Behaviour behaviour = Behaviour.AcceptEvents;
+    private Behaviour behaviour = Behaviour.AnyExecuteAccept;
 
     /**
      * To demo/test what occurs if a subscriber that might veto an event.
@@ -58,77 +77,177 @@ public class ToDoItemSubscriptions {
     @MemberOrder(name = "Prototyping", sequence = "80")
     @Named("Set subscriber behaviour")
     @ActionSemantics(ActionSemantics.Of.IDEMPOTENT)
-    public String subscriberBehaviour(@Named("Behaviour") Behaviour behaviour) {
+    public ToDoItem subscriberBehaviour(ToDoItem toDoItem, @Named("Behaviour") Behaviour behaviour) {
         this.behaviour = behaviour;
-        return "Subscriber behaviour set to: " + behaviour;
+        container.informUser("Subscriber behaviour set to: " + behaviour);
+        return toDoItem;
     }
-    public Behaviour default0SubscriberBehaviour() {
+    public Behaviour default1SubscriberBehaviour() {
         return this.behaviour;
     }
+
     @Programmatic
     public Behaviour getSubscriberBehaviour() {
         return behaviour;
     }
-    private void rejectIfRequired() {
-        if(behaviour == Behaviour.RejectEventsWithRecoverableException) {
+    private void onExecutedVetoIfRequired() {
+        if(behaviour == Behaviour.AnyExecuteVetoWithRecoverableException) {
             throw new RecoverableException("Rejecting event (recoverable exception thrown)");
         }
-        if(behaviour == Behaviour.RejectEventsWithNonRecoverableException) {
+        if(behaviour == Behaviour.AnyExecuteVetoWithNonRecoverableException) {
             throw new NonRecoverableException("Rejecting event (recoverable exception thrown)");
         }
-        if(behaviour == Behaviour.ThrowOtherException) {
+        if(behaviour == Behaviour.AnyExecuteVetoWithOtherException) {
             throw new RuntimeException("Throwing some other exception");
         }
     }
     //endregion
 
-    //region > on(Event)...
-    // //////////////////////////////////////
-
+    //region > on(Event) for ToDoItem-specific events
     @Programmatic
     @Subscribe
-    public void on(ToDoItem.AbstractActionInvokedEvent ev) {
-        rejectIfRequired();
+    public void on(final ToDoItem.CompletedEvent ev) {
         recordEvent(ev);
-        LOG.info(ev.getEventDescription() + ": " + container.titleOf(ev.getSource()));
+        switch(ev.getPhase()) {
+            case HIDE:
+                break;
+            case DISABLE:
+                break;
+            case VALIDATE:
+                break;
+            case EXECUTING:
+                break;
+            case EXECUTED:
+                LOG.info("Received ToDoItem.CompletedEvent for : " + container.titleOf(ev.getSource()));
+                break;
+        }
     }
+    //endregion
 
+    //region > on(Event) ... general purpose
 
     @Programmatic
     @Subscribe
-    public void on(PropertyChangedEvent<?,?> ev) {
-        rejectIfRequired();
+    public void on(final ActionInteractionEvent<?> ev) {
         recordEvent(ev);
-        if(ev.getIdentifier().getMemberName().contains("description")) {
-            String newValue = (String) ev.getNewValue();
-            if(newValue.matches(".*demo veto.*")) {
-                throw new RecoverableException("oh no you don't! " + ev.getNewValue());
-            }
+        switch(ev.getPhase()) {
+            case HIDE:
+                if(getSubscriberBehaviour() == Behaviour.UpdateCostActionHide) {
+                    if(ev.getIdentifier().getMemberName().equals("updateCost")) {
+                        ev.hide();
+                    }
+                }
+                break;
+            case DISABLE:
+                if(getSubscriberBehaviour() == Behaviour.UpdateCostActionDisable) {
+                    if(ev.getIdentifier().getMemberName().equals("updateCost")) {
+                        ev.disable("ToDoItemSubscriptions says: updateCost action disabled!");
+                    }
+                }
+                break;
+            case VALIDATE:
+                if(getSubscriberBehaviour() == Behaviour.UpdateCostActionInvalidate &&
+                        ev.getIdentifier().getMemberName().equals("updateCost")) {
+                    ev.disable("ToDoItemSubscriptions says: can't invoke updateCostaction with these args!");
+                }
+                break;
+            case EXECUTING:
+                break;
+            case EXECUTED:
+                LOG.info("Received ActionInteractionEvent, " + container.titleOf(ev.getSource()) + ", invoked " + ev.getIdentifier().getMemberName());
+                onExecutedVetoIfRequired();
+                break;
         }
-        LOG.info(container.titleOf(ev.getSource()) + ", changed " + ev.getIdentifier().getMemberName() + " : " + ev.getOldValue() + " -> " + ev.getNewValue());
     }
-    
+
     @Programmatic
     @Subscribe
-    public void on(CollectionAddedToEvent<?,?> ev) {
-        rejectIfRequired();
+    public void on(PropertyInteractionEvent<?,?> ev) {
         recordEvent(ev);
-        LOG.info(container.titleOf(ev.getSource()) + ", added to " + ev.getIdentifier().getMemberName() + " : " + ev.getValue());
+        switch(ev.getPhase()) {
+            case HIDE:
+                if(getSubscriberBehaviour() == Behaviour.DescriptionPropertyHide &&
+                    ev.getIdentifier().getMemberName().equals("description")) {
+                    ev.hide();
+                }
+                break;
+            case DISABLE:
+                if(getSubscriberBehaviour() == Behaviour.DescriptionPropertyDisable &&
+                    ev.getIdentifier().getMemberName().equals("description")) {
+                    ev.disable("ToDoItemSubscriptions says: description property disabled!");
+                }
+                break;
+            case VALIDATE:
+                if(getSubscriberBehaviour() == Behaviour.DescriptionPropertyInvalidate &&
+                    ev.getIdentifier().getMemberName().equals("description")) {
+                    ev.disable("ToDoItemSubscriptions says: can't change description property to this value!");
+                }
+                break;
+            case EXECUTING:
+                break;
+            case EXECUTED:
+                LOG.info("Received PropertyInteractionEvent, " + container.titleOf(ev.getSource()) + ", changed " + ev.getIdentifier().getMemberName() + " : " + ev.getOldValue() + " -> " + ev.getNewValue());
+                onExecutedVetoIfRequired();
+
+                if(ev.getIdentifier().getMemberName().contains("description")) {
+                    String newValue = (String) ev.getNewValue();
+                    if(newValue.matches(".*demo veto.*")) {
+                        throw new RecoverableException("oh no you don't! " + ev.getNewValue());
+                    }
+                }
+                break;
+        }
     }
     
     @Programmatic
     @Subscribe
-    public void on(CollectionRemovedFromEvent<?,?> ev) {
-        rejectIfRequired();
+    public void on(CollectionInteractionEvent<?,?> ev) {
         recordEvent(ev);
-        LOG.info(container.titleOf(ev.getSource()) + ", removed from " + ev.getIdentifier().getMemberName() + " : " + ev.getValue());
-    }
+        switch (ev.getPhase()) {
+            case HIDE:
+                if(getSubscriberBehaviour() == Behaviour.DependenciesCollectionHide &&
+                    ev.getIdentifier().getMemberName().equals("dependencies")) {
+                    ev.hide();
+                }
+                if (getSubscriberBehaviour() == Behaviour.SimilarToCollectionHide &&
+                    ev.getIdentifier().getMemberName().equals("similarTo")) {
+                    ev.hide();
+                }
+                break;
+            case DISABLE:
+                if (getSubscriberBehaviour() == Behaviour.DependenciesCollectionDisable &&
+                    ev.getIdentifier().getMemberName().equals("dependencies")) {
+                    ev.disable("ToDoItemSubscriptions says: dependencies collection disabled!");
+                }
+                break;
+            case VALIDATE:
+                if(getSubscriberBehaviour() == Behaviour.DependenciesCollectionInvalidateAdd &&
+                    ev.getIdentifier().getMemberName().equals("dependencies") &&
+                    ev.getOf() == CollectionInteractionEvent.Of.ADD_TO ) {
+                    ev.invalidate("ToDoItemSubscriptions says: can't add this object to dependencies collection!");
+                }
+                if(getSubscriberBehaviour() == Behaviour.DependenciesCollectionInvalidateRemove &&
+                    ev.getIdentifier().getMemberName().equals("dependencies") &&
+                    ev.getOf() == CollectionInteractionEvent.Of.REMOVE_FROM ) {
+                    ev.invalidate("ToDoItemSubscriptions says: can't remove this object from dependencies collection!");
+                }
+                break;
+            case EXECUTING:
+                break;
+            case EXECUTED:
+                if(ev.getOf() == CollectionInteractionEvent.Of.ADD_TO) {
+                    LOG.info("Received CollectionInteractionEvent, " + container.titleOf(ev.getSource()) + ", added to " + ev.getIdentifier().getMemberName() + " : " + ev.getValue());
+                } else {
+                    LOG.info("Received CollectionInteractionEvent, " + container.titleOf(ev.getSource()) + ", removed from " + ev.getIdentifier().getMemberName() + " : " + ev.getValue());
+                }
+                onExecutedVetoIfRequired();
+                break;
+        }
 
+    }
     //endregion
 
     //region > receivedEvents
-    // //////////////////////////////////////
-    
     private final List<java.util.EventObject> receivedEvents = Lists.newLinkedList();
 
     /**
@@ -138,19 +257,43 @@ public class ToDoItemSubscriptions {
     public List<java.util.EventObject> receivedEvents() {
         return receivedEvents;
     }
+
+    /**
+     * Used in integration tests.
+     */
+    @Programmatic
+    public <T extends java.util.EventObject> List<T> receivedEvents(final Class<T> expectedType) {
+        return newArrayList(
+                    transform(
+                        filter(receivedEvents, instanceOf(expectedType)),
+                        castTo(expectedType)));
+    }
+
+    private static <T extends EventObject> Function<EventObject, T> castTo(Class<T> expectedType) {
+        return new Function<EventObject, T>() {
+                    @Override
+                    public T apply(EventObject input) {
+                        return (T) input;
+                    }
+                };
+    }
+
+    private static <T extends EventObject> Predicate<EventObject> instanceOf(final Class<T> expectedType) {
+        return new Predicate<EventObject>() {
+            @Override
+            public boolean apply(EventObject input) {
+                return expectedType.isInstance(input);
+            }
+        };
+    }
+
     /**
      * Used in integration tests.
      */
     @Programmatic
     public <T extends java.util.EventObject> T mostRecentlyReceivedEvent(Class<T> expectedType) {
-        if (receivedEvents.isEmpty()) {
-            return null;
-        } 
-        final EventObject ev = receivedEvents.get(0);
-        if(!expectedType.isAssignableFrom(ev.getClass())) {
-            return null;
-        } 
-        return expectedType.cast(ev);
+        final List<T> receivedEvents = receivedEvents(expectedType);
+        return !receivedEvents.isEmpty() ? receivedEvents.get(0) : null;
     }
     private void recordEvent(final java.util.EventObject ev) {
         receivedEvents.add(0, ev);
@@ -161,20 +304,17 @@ public class ToDoItemSubscriptions {
     @Programmatic
     public void reset() {
         receivedEvents.clear();
-        subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AcceptEvents);
+        subscriberBehaviour(null, ToDoItemSubscriptions.Behaviour.AnyExecuteAccept);
     }
-
     //endregion
 
-
     //region > injected services
-    // //////////////////////////////////////
-    
     @javax.inject.Inject
     private DomainObjectContainer container;
 
     @SuppressWarnings("unused")
     private EventBusService eventBusService;
+    @Programmatic
     public final void injectEventBusService(EventBusService eventBusService) {
         eventBusService.register(this);
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItems.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItems.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItems.java
index 7c58d7c..bee7631 100644
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItems.java
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/main/java/dom/todo/ToDoItems.java
@@ -35,11 +35,10 @@ import org.apache.isis.applib.query.QueryDefault;
 import org.apache.isis.applib.services.clock.ClockService;
 
 @Named("ToDos")
+@DomainService(menuOrder = "10", repositoryFor = ToDoItem.class)
 public class ToDoItems {
 
     //region > identification in the UI
-    // //////////////////////////////////////
-
     public String getId() {
         return "toDoItems";
     }
@@ -50,11 +49,9 @@ public class ToDoItems {
     //endregion
 
     //region > notYetComplete (action)
-    // //////////////////////////////////////
-
     @Bookmarkable
     @ActionSemantics(Of.SAFE)
-    @MemberOrder(sequence = "1")
+    @MemberOrder(sequence = "10")
     public List<ToDoItem> notYetComplete() {
         final List<ToDoItem> items = notYetCompleteNoUi();
         if(items.isEmpty()) {
@@ -73,10 +70,8 @@ public class ToDoItems {
     //endregion
 
     //region > complete (action)
-    // //////////////////////////////////////
-    
     @ActionSemantics(Of.SAFE)
-    @MemberOrder(sequence = "3")
+    @MemberOrder(sequence = "20")
     public List<ToDoItem> complete() {
         final List<ToDoItem> items = completeNoUi();
         if(items.isEmpty()) {
@@ -95,12 +90,10 @@ public class ToDoItems {
     //endregion
 
     //region > categorized (action)
-    // //////////////////////////////////////
-
 	@SuppressWarnings("unchecked")
 	@Bookmarkable
     @ActionSemantics(Of.SAFE)
-    @MemberOrder(sequence = "30")
+    @MemberOrder(sequence = "40")
     public List<ToDoItem> categorized(
     		@Named("Category") final Category category,
     		@Named("Subcategory") final Subcategory subcategory,
@@ -134,9 +127,7 @@ public class ToDoItems {
     //endregion
 
     //region > newToDo (action)
-    // //////////////////////////////////////
-
-    @MemberOrder(sequence = "40")
+    @MemberOrder(sequence = "5")
     public ToDoItem newToDo(
             final @RegEx(validation = "${symbol_escape}${symbol_escape}w[@&:${symbol_escape}${symbol_escape}-${symbol_escape}${symbol_escape},${symbol_escape}${symbol_escape}.${symbol_escape}${symbol_escape}+ ${symbol_escape}${symbol_escape}w]*") @Named("Description") String description, 
             final @Named("Category") Category category,
@@ -167,8 +158,7 @@ public class ToDoItems {
     //endregion
 
     //region > allToDos (action)
-    // //////////////////////////////////////
-
+    @Prototype
     @ActionSemantics(Of.SAFE)
     @MemberOrder(sequence = "50")
     public List<ToDoItem> allToDos() {
@@ -184,8 +174,6 @@ public class ToDoItems {
     //endregion
 
     //region > autoComplete (programmatic)
-    // //////////////////////////////////////
-
     @Programmatic // not part of metamodel
     public List<ToDoItem> autoComplete(final String description) {
         return container.allMatches(
@@ -197,8 +185,6 @@ public class ToDoItems {
     //endregion
 
     //region > helpers
-    // //////////////////////////////////////
-
     @Programmatic // for use by fixtures
     public ToDoItem newToDo(
             final String description, 
@@ -226,9 +212,20 @@ public class ToDoItems {
 
     //endregion
 
+    //region > common validation
+    private static final long ONE_WEEK_IN_MILLIS = 7 * 24 * 60 * 60 * 1000L;
+
+    @Programmatic
+    public String validateDueBy(LocalDate dueBy) {
+        return isMoreThanOneWeekInPast(dueBy) ? "Due by date cannot be more than one week old" : null;
+    }
+    @Programmatic
+    boolean isMoreThanOneWeekInPast(final LocalDate dueBy) {
+        return dueBy.toDateTimeAtStartOfDay().getMillis() < clockService.nowAsMillis() - ONE_WEEK_IN_MILLIS;
+    }
+    //endregion
+
     //region > injected services
-    // //////////////////////////////////////
-    
     @javax.inject.Inject
     private DomainObjectContainer container;
 

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoItemTest.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoItemTest.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoItemTest.java
new file mode 100644
index 0000000..5020190
--- /dev/null
+++ b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoItemTest.java
@@ -0,0 +1,94 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package dom.todo;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.apache.isis.applib.annotation.Bulk;
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertThat;
+
+public abstract class ToDoItemTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    EventBusService eventBusService;
+
+    ToDoItem toDoItem;
+
+    @Before
+    public void setUp() throws Exception {
+        toDoItem = new ToDoItem();
+
+        toDoItem.bulkInteractionContext = Bulk.InteractionContext.regularAction(toDoItem);
+        toDoItem.eventBusService = eventBusService;
+
+        context.ignoring(eventBusService);
+    }
+
+    public static class Actions {
+
+        public static class Completed extends ToDoItemTest {
+
+            @Test
+            public void happyCase() throws Exception {
+
+                // given
+                toDoItem.setComplete(false);
+                assertThat(toDoItem.disableCompleted(), is(nullValue()));
+
+                // when
+                toDoItem.completed();
+
+                // then
+                assertThat(toDoItem.isComplete(), is(true));
+                assertThat(toDoItem.disableCompleted(), is(not(nullValue())));
+            }
+        }
+
+        public static class NotYetCompleted extends ToDoItemTest {
+
+            @Test
+            public void happyCase() throws Exception {
+
+                // given
+                toDoItem.setComplete(true);
+                assertThat(toDoItem.disableNotYetCompleted(), is(nullValue()));
+
+                // when
+                toDoItem.notYetCompleted();
+
+                // then
+                assertThat(toDoItem.isComplete(), is(false));
+                assertThat(toDoItem.disableNotYetCompleted(), is(not(nullValue())));
+            }
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoTest_completed.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoTest_completed.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoTest_completed.java
deleted file mode 100644
index 718460f..0000000
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoTest_completed.java
+++ /dev/null
@@ -1,71 +0,0 @@
-#set( $symbol_pound = '#' )
-#set( $symbol_dollar = '$' )
-#set( $symbol_escape = '\' )
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package dom.todo;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertThat;
-
-import org.jmock.auto.Mock;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import org.apache.isis.applib.annotation.Bulk;
-import org.apache.isis.applib.services.eventbus.EventBusService;
-import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
-import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
-
-public class ToDoTest_completed {
-
-    @Rule
-    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
-
-    @Mock
-    private EventBusService eventBusService;
-
-    private ToDoItem toDoItem;
-
-    @Before
-    public void setUp() throws Exception {
-        toDoItem = new ToDoItem();
-        toDoItem.bulkInteractionContext = Bulk.InteractionContext.regularAction(toDoItem);
-        toDoItem.eventBusService = eventBusService;
-
-        context.ignoring(eventBusService);
-
-        toDoItem.setComplete(false);
-    }
-    
-    @Test
-    public void happyCase() throws Exception {
-        // given
-        assertThat(toDoItem.disableCompleted(), is(nullValue()));
-        
-        // when
-        toDoItem.completed();
-        
-        // then
-        assertThat(toDoItem.isComplete(), is(true));
-        assertThat(toDoItem.disableCompleted(), is(not(nullValue())));
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/04e8ba20/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoTest_notYetCompleted.java
----------------------------------------------------------------------
diff --git a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoTest_notYetCompleted.java b/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoTest_notYetCompleted.java
deleted file mode 100644
index bc02446..0000000
--- a/example/archetype/todoapp/src/main/resources/archetype-resources/dom/src/test/java/dom/todo/ToDoTest_notYetCompleted.java
+++ /dev/null
@@ -1,71 +0,0 @@
-#set( $symbol_pound = '#' )
-#set( $symbol_dollar = '$' )
-#set( $symbol_escape = '\' )
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package dom.todo;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertThat;
-
-import org.jmock.auto.Mock;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import org.apache.isis.applib.annotation.Bulk;
-import org.apache.isis.applib.services.eventbus.EventBusService;
-import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
-import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
-
-public class ToDoTest_notYetCompleted {
-
-    @Rule
-    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
-
-    @Mock
-    private EventBusService eventBusService;
-    
-    private ToDoItem toDoItem;
-
-    @Before
-    public void setUp() throws Exception {
-        toDoItem = new ToDoItem();
-
-        toDoItem.bulkInteractionContext = Bulk.InteractionContext.regularAction(toDoItem);
-        toDoItem.eventBusService = eventBusService;
-
-        context.ignoring(eventBusService);
-        toDoItem.setComplete(true);
-    }
-    
-    @Test
-    public void happyCase() throws Exception {
-        // given
-        assertThat(toDoItem.disableNotYetCompleted(), is(nullValue()));
-        
-        // when
-        toDoItem.notYetCompleted();
-        
-        // then
-        assertThat(toDoItem.isComplete(), is(false));
-        assertThat(toDoItem.disableNotYetCompleted(), is(not(nullValue())));
-    }
-    
-}