You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2023/06/18 17:13:05 UTC

[causeway] 12/12: CAUSEWAY-3395: reworking user guide

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

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

commit 669ccb6626a56ec45a3456a9e15132e18085b1b7
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sun Jun 18 18:12:45 2023 +0100

    CAUSEWAY-3395: reworking user guide
---
 .../modules/ROOT/pages/domain-services.adoc        |   1 +
 .../ROOT/pages/drop-downs-and-defaults.adoc        | 217 +---------
 .../userguide/modules/ROOT/pages/events.adoc       | 110 +----
 .../modules/ROOT/pages/meta-annotations.adoc       | 103 -----
 .../userguide/modules/ROOT/pages/mixins.adoc       | 236 +----------
 .../userguide/modules/ROOT/pages/view-models.adoc  | 457 +--------------------
 .../ROOT/partials/domain-services/_intro.adoc      | 331 ---------------
 .../ROOT/partials/domain-services/progmodel.adoc   | 336 +++++++++++++++
 .../partials/drop-down-and-defaults/_intro.adoc    |  14 +
 .../partials/drop-down-and-defaults/progmodel.adoc | 203 +++++++++
 .../modules/ROOT/partials/events/_intro.adoc       |  10 +
 .../modules/ROOT/partials/events/progmodel.adoc    | 131 ++++++
 .../ROOT/partials/meta-annotations/_intro.adoc     |   6 +
 .../ROOT/partials/meta-annotations/progmodel.adoc  | 102 +++++
 .../modules/ROOT/partials/mixins/_intro.adoc       |  43 ++
 .../modules/ROOT/partials/mixins/progmodel.adoc    | 186 +++++++++
 .../modules/ROOT/partials/module-nav.adoc          |   3 +-
 .../modules/ROOT/partials/view-models/_intro.adoc  | 136 ++++++
 .../ROOT/partials/view-models/progmodel.adoc       | 314 ++++++++++++++
 19 files changed, 1492 insertions(+), 1447 deletions(-)

diff --git a/antora/components/userguide/modules/ROOT/pages/domain-services.adoc b/antora/components/userguide/modules/ROOT/pages/domain-services.adoc
index 00e0010c45..46c122782c 100644
--- a/antora/components/userguide/modules/ROOT/pages/domain-services.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/domain-services.adoc
@@ -5,6 +5,7 @@
 
 
 include::partial$domain-services/_intro.adoc[leveloffset=+1]
+include::partial$domain-services/progmodel.adoc[leveloffset=+1]
 include::partial$domain-services/inject-services.adoc[leveloffset=+1]
 include::partial$domain-services/crud.adoc[leveloffset=+1]
 
diff --git a/antora/components/userguide/modules/ROOT/pages/drop-downs-and-defaults.adoc b/antora/components/userguide/modules/ROOT/pages/drop-downs-and-defaults.adoc
index 011a042e7a..0046b46756 100644
--- a/antora/components/userguide/modules/ROOT/pages/drop-downs-and-defaults.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/drop-downs-and-defaults.adoc
@@ -4,220 +4,7 @@
 :page-partial:
 
 
-Invoking an action whose parameters are primitives or values (int, date, string etc) is simple: the user can just type in or use a date picker.
-Invoking an action with a parameter of reference type (such as `Customer` or `Order`) requires the viewer to provide some mechanism by which the end-user can select the relevant instance.
 
-If the list of available options is fixed then the developer can provided a list a xref:refguide:applib-methods:prefixes.adoc#choices[choices...()] supporting method (for either and action parameter or when editing a property).
-These are rendered in a drop-down.
-
-If the list of available options is much larger, then the developer can use an xref:refguide:applib-methods:prefixes.adoc#autoComplete[autoComplete...()] supporting method.
-The user user enters a few characters and this is used to search for matching reference(s), again rendered in a drop-down.
-
-Similarly, when invoking an action, there may well be suitable defaults for the action arguments.
-For example, if placing an `Order` then -- even if the `Product` argument might not have a sensible default -- the quantity argument could reasonably be defaulted to 1.
-Or, the `Product` might indeed have a default, say the product previously placed by this user.
-The developer indicates this using a xref:refguide:applib-methods:prefixes.adoc#default[default...()] supporting method.
-
-
-== Choices and Default
-
-For example, _choices_ for a property are specified using:
-
-[source,java]
-----
-import lombok.Getter;
-import lombok.Setter;
-
-@Property(editing = Editing.ENABLED)                    // <.>
-@Getter @Setter
-private String paymentMethod;
-
-public List<String> choicesPaymentMethod() {            // <.>
-    return Arrays.asList("Visa", "Mastercard", "Amex");
-}
-----
-<.> If required; properties are by default disabled globally.
-<.> Note the "choices" prefix and the suffix matching up with the getter.
-The method must return a collection of the same type as the property.
-
-For an action the _choices_ and a suitable _default_ method would be:
-
-[source,java]
-----
-public Order changePaymentMethod(String paymentMethod) {
-    setPaymentMethod(paymentMethod);
-    return this;
-}
-
-public List<String> choices0ChangePaymentMethod() {         // <.>
-    return Arrays.asList("Visa", "Mastercard", "Amex");
-}
-public String default0ChangePaymentMethod() {               // <.>
-    return getPaymentMethod();                              // <.>
-}
-----
-<.> "choices" prefix, N-th param, suffix matches up with the action's name. +
-Returns a collection of the same type as the parameter.
-<.> "default" prefix, N-th param, the suffix matches up with the action's name. +
-Returns object of same type as parameter.
-<.> Common idiom to return the current value of an property of the object.
-
-
-== AutoComplete
-
-The _autocomplete_ is similar to _choices_, but accepts a string parameter, to search for matching results.
-A property for example might have:
-
-[source,java]
-----
-@Property(editing = Editing.ENABLED)                        // <.>
-@Getter @Setter
-private Product product;
-
-public List<Product> autoCompleteProduct(                   // <.>
-                        @MinLength(2) String search) {      // <.>
-    return productRepository.findByReferenceOrName(search);
-}
-----
-<.> If required; properties are by default disabled globally.
-<.> "autoComplete" prefix, suffix matches property name. +
-Returns a collection of the property's type.
-<.> The `@MinLength(...)` annotation indicates the minimum number of characters that must be entered before a search is initiated.
-
-Actions are very similar:
-
-[source,java]
-----
-public Order changeProduct(Product product) {
-    setProduct(product);
-    return this;
-}
-
-public List<Product> autoComplete0Product(                  // <.>
-                        @MinLength(2) String search) {
-    return productRepository.findByReferenceOrName(search);
-}
-----
-<.> "autoComplete" prefix, N-th param, suffix matches action name. +
-Returns a collection of the parameters type.
-
-An _autoComplete_ method can be used in conjunction with a _default_ method, but it doesn't make sense to provide both an _autoComplete_ and a _choices_ method.
-
-
-
-== "Globally" defined drop-downs
-
-Very often the set of available choices depends on the data type of the property/action parameter, rather than the individual property/parameter itself.
-And similarly the algorithm to search for references again may well depend only on that reference type.
-
-In the case of _choices_, annotating a class as "bounded" (as in a "bounded" or fixed number of instances) means that a _choices_ drop-down will automatically be defined.
-For example:
-
-[source,java]
-----
-@DomainObject(
-    bounded = true
-)
-public class Product { /* ... */ }
-----
-
-For more on this, see xref:refguide:applib:index/annotation/DomainObject.adoc#bounding[@DomainObject#bounding].
-
-Or, if the data type is an enum, then a drop-down will be provided automatically.
-A payment method is a good example of this:
-
-
-[source,java]
-----
-public enum PaymentMethod {
-    VISA, MASTERCARD, AMEX;
-}
-----
-
-Something similar can be achieved for _autoComplete_.
-Here the domain object indicates a repository query to execute.
-For example:
-
-[source,java]
-----
-@DomainObject(
-    autoCompleteRepository = Customers.class,
-    autoCompleteMethod = "findByReferenceOrName"
-)
-public class Customer { /* ... */ }
-----
-
-with:
-
-[source,java]
-----
-@DomainService(nature = NatureOfService.VIEW)
-public class Customers {
-    @Action(semantics=SemanticsOf.SAFE)
-    public List<Customer> findByReferenceOrName(@MinLength(3) String refOrName) {
-        ...
-    }
-}
-----
-
-For more on this, see xref:refguide:applib:index/annotation/DomainObject.adoc#autoCompleteRepository[@DomainObject#autoCompleteRepository].
-
-[TIP]
-====
-There's no need for the nominated method to be an actual action; any method of any domain service will do, so long as it accepts a string and returns the correct list.
-====
-
-
-== Multi-select action parameters
-
-As well as scalar values, action parameters can also be collections.
-For this to be valid, a _choices_ or _autoComplete_ supporting method must be provided.
-
-For example, suppose we want to "tag" or "label" an object:
-
-[source,java]
-----
-public StoryCard tag(List<Tag> tags) {
-    getTags().addAll(tags);
-}
-
-public List<Tag> autoCompleteTag(@MinLength(1) search) {
-    return tagRepository.findByName(search);
-}
-----
-
-
-If the action has been associated with a collection, using xref:refguide:applib:index/annotation/Action.adoc#choicesFrom[@Action#choicesFrom()], then the collection can be used to provide a list of candidate values.
-
-The xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)] handles this by rendering checkboxes against the associated collection; the user can select/deselect these checkboxes and the selected items are taken as the values for the multi-select action.
-
-
-
-== Dependent choices for action parameters
-
-For action it is also possible (in a limited form) to define dependencies between parameters.
-Specifically, if one parameter is a drop-down choice, then other drop-down choices can be derived from it.
-
-A good example is a category/sub-category:
-
-[source,java]
-----
-public ToDoItem categorize(
-            Category category,
-            Subcategory subcategory) {
-    setCategory(category);
-    setSubcategory(subcategory);
-}
-
-public List<Category> choices0Categorize() {
-    return categoryRepository.allCategories();
-}
-public List<Subcategory> choices1Categorize(        // <.>
-                                Category category) {
-    return subcategoryRepository.findBy(category);
-}
-----
-<.> Returns a list of choices for the 2nd parameter based on the argument provided for the first.
-
-p
 
+include::partial$drop-down-and-defaults/_intro.adoc[leveloffset=+1]
+include::partial$drop-down-and-defaults/progmodel.adoc[leveloffset=+1]
diff --git a/antora/components/userguide/modules/ROOT/pages/events.adoc b/antora/components/userguide/modules/ROOT/pages/events.adoc
index 5b753058d8..08e6843986 100644
--- a/antora/components/userguide/modules/ROOT/pages/events.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/events.adoc
@@ -5,112 +5,6 @@
 :page-partial:
 
 
-When the framework renders a domain object, and as the end-user interacts with the domain object, the framework it emits multiple events using the intra-process xref:refguide:applib:index/services/eventbus/EventBusService.adoc[event bus].
-These events enable other domain services (possibly in other modules) to influence how the domain object is rendered, or to perform side-effects or even veto an action invocation.
-
-The framework has several categories of events: domain events, UI events and lifecycle events.
-Learn more about these in xref:events.adoc[events] page.
-
-To receive the events, the domain service should subscribe to the xref:refguide:applib:index/services/eventbus/EventBusService.adoc[EventBusService], and implement an appropriately annotated method to receive the events.
-
-
-[#domain-events]
-== Domain Events
-
-Domain events are fired -- through the internal xref:refguide:applib:index/services/eventbus/EventBusService.adoc[event bus] -- for every user interaction with each object member (property, collection or action).
-
-By default, rendering a property causes a `PropertyDomainEvent` to be fired, though the xref:refguide:applib:index/annotation/Property.adoc#domainEvent[@Property#domainEvent()] attribute allows a custom subclass to be specified if necessary.
-Similarly, rendering a collection causes a `DomainEvent` to be fired, and rendering an action causes an `ActionDomainEvent` to be fired.
-
-In fact, each event can be fired up to five times, with the event's `getEventPhase()` method indicating to the subscriber the phase:
-
-* *hide* phase allows the subscriber to hide the member
-
-* *disable* phase allows the subscriber to disable the member.
-+
-For a property this makes it read-only; for an action this makes it "greyed out".
-(Collections are implicitly read-only).
-
-* *validate* phase allows the subscriber to validate the proposed change.
-+
-For a property this means validating the proposed new value of the property; for an action this means validating the action parameter arguments.
-For example, a referential integrity restrict could be implemented here.
-
-* *executing* phase is prior to the actual property edit/action invocation, allowing the subscriber to perform side-effects.
-+
-For example, a cascade delete could be implemented here.
-
-* *executed* phase is after the actual property edit/action invocation.
-+
-For example, a business audit event could be implemented here.
-
-For more details on the actual domain event classes, see the xref:refguide:applib-classes:events.adoc#domain-event-classes[domain event] section of the relevant reference guide.
-
-== UI Events
-
-As explained xref:userguide:ROOT:overview.adoc#title-and-icon-and-css-class[earlier], to allow the end-user to distinguish one domain object from another, it is rendered with a title and an icon.
-
-xref:userguide:ROOT:ui.adoc#object-titles-and-icons[Normally] the code to return title and icon of an object is part of the domain object's implementation.
-However, UI events allow this title and icon to be provided instead by a subscriber.
-UI events have higher precedence than the other mechanisms of supplying a title.
-
-If annotated with xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#titleUiEvent[@DomainObjectLayout#titleUiEvent()], the appropriate (subclass of) `TitleUiEvent` will be emitted.
-Similarly for xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#iconUiEvent[#iconUiEvent()].
-In addition, it is possible to use events to obtain a CSS class to render with the domain object, using xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#cssClassUiEvent[#cssClassUiEvent()], and to select an alternate xref:userguide:ROOT:ui.adoc#object-layout[layout file] using xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#layoutUiEvent[#layoutUiEvent()].
-
-There are two use cases where this feature is useful:
-
-* the first is to override the title/icon/CSS class/layout of 3rd party library code, for example as provided by the xref:security:secman:about.adoc[SecMan] extension.
-
-* the second is for xref:userguide:ROOT:view-models.adoc#jaxb[JAXB-style view models] which are code generated from XSDs and so cannot have any dependencies on the rest of the Apache Causeway framework.
-
-In this second case a subscriber on the default events can provide a title and icon for such an object, with the behaviour provided using xref:userguide:ROOT:overview.adoc#mixins[mixins].
-
-[#lifecycle-events]
-== Lifecycle Events
-
-Lifecycle events allow domain object subscribers to listen for changes to the persistence state of domain entities, and act accordingly.
-
-[WARNING]
-====
-Lifecycle events are _not_ fired for view models.
-====
-
-The lifecycle events supported are:
-
-* *created*
-+
-Entity has just been instantiated.
-Note that this requires that the object is instantiated using the framework, see xref:userguide:ROOT:domain-services.adoc#instantiating[here] for further discussion.
-
-* *loaded*
-+
-Entity has just retrieved/rehydrated from the database
-
-* *persisting*
-+
-Entity is about to be inserted/saved (ie for the first time) into the database
-
-* *persisted*
-+
-Entity has just been inserted/saved (ie for the first time) into the database
-
-* *updating*
-+
-The (already persistent) entity about to be flushed in the database
-
-* *updated*
-+
-The (already persistent) entity has just been flushed to the database
-
-* *removing*
-+
-The (already persistent) entity is about to be deleted from the database
-
-For example, if annotated with xref:refguide:applib:index/annotation/DomainObject.adoc#updatingLifecycleEvent[@DomainObjectLayout#updatingLifecycleEvent], the appropriate (subclass of) `ObjectUpdatingEvent` will be emitted.
-
-There is no lifecycle event for "entity creating" because (obviously) the framework doesn't know about newly created objects until they have been created.
-Similarly, there is no lifecycle event for entities that have been removed because it is not valid to "touch" a domain entity once deleted.
-
-
 
+include::partial$events/_intro.adoc[leveloffset=+1]
+include::partial$events/progmodel.adoc[leveloffset=+1]
diff --git a/antora/components/userguide/modules/ROOT/pages/meta-annotations.adoc b/antora/components/userguide/modules/ROOT/pages/meta-annotations.adoc
index b0da2b1dd9..e038544acb 100644
--- a/antora/components/userguide/modules/ROOT/pages/meta-annotations.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/meta-annotations.adoc
@@ -4,107 +4,4 @@
 :page-partial:
 
 
-Apache Causeway supports the use of meta-annotations, as does Spring Boot and the ORM (xref:pjdo:ROOT:about.adoc[JDO/DataNucleus] from DN 5.x, xref:pjpa:ROOT:about.adoc[JPA/EclipseLink] from Eclipse 2.7 supporting JPA 2.2).
-
-This allows you to define custom annotations that are more descriptive of your domain model, and meta-annotate your custom annotations with those of Apache Causeway, Spring or the ORM.
-In your domain model, you can just use the custom annotation.
-
-The most common use case is to define meta-annotations for scalar types that are used in properties or parameters.
-
-The xref:docs:starters:simpleapp.adoc[SimpleApp] starter app illustrates the usage with its `@Name` meta-annotation:
-
-[source,java]
-.Name.java
-----
-@Column(length = Name.MAX_LEN, allowsNull = "false")                // <.>
-@Property(mustSatisfy = Name.Specification.class,                   // <.>
-          maxLength = Name.MAX_LEN)
-@Parameter(mustSatisfy = Name.Specification.class,                  // <.>
-           maxLength = Name.MAX_LEN)
-@ParameterLayout(named = "Name")                                    // <.>
-@Target({ ElementType.METHOD, ElementType.FIELD,                    // <.>
-          ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
-@Retention(RetentionPolicy.RUNTIME)                                 // <.>
-public @interface Name {
-
-    int MAX_LEN = 40;
-
-    // ...
-
-    class Specification extends AbstractSpecification2<String> {
-        // ...
-    }
-}
-----
-<.> ORM annotation
-<.> Annotations that are inferred if annotated on a property. +
-The xref:refguide:applib:index/annotation/Property.adoc#mustSatisfy[@Property#mustSatisfy()] allows arbitrary validation rules to be specified for the property.
-
-<.> Annotations that are inferred if annotated on a parameter +
-The xref:refguide:applib:index/annotation/Parameter.adoc#mustSatisfy[@Parameter#mustSatisfy()] allows the same validation rules to be specified for the parameter.
-
-<.> Layout annotations that are inferred if annotated on a parameter
-
-<.> The locations that the meta annotation can be applied
-
-<.> Required boilerplate to ensure that the meta annotation is part of the bytecode introspected at runtime
-
-This annotation is used in the `SimpleObject` entity, for the "name" property and the "name" parameter of the "updateName()" action:
-
-[source,java]
-.SimpleObject.java
-----
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-import lombok.ToString.Include;
-
-@ToString(onlyExplicitlyIncluded = true)
-public class SimpleObject implements Comparable<SimpleObject> {
-
-    // ...
-
-    @Name
-    @Getter @Setter @ToString.Include
-    private String name;
-
-    // ...
-    public SimpleObject updateName(
-            @Name final String name) {
-        setName(name);
-        return this;
-    }
-    // ...
-}
-----
-
-
-[#properties-vs-parameters]
-== Properties vs Parameters
-
-Since the ORM handles persistence, its annotations can only be specified for properties, not for action parameters.
-Using meta-annotations is a good way to unify the annotations that are required for properties vs parameters.
-
-The table below summarises the equivalence of some of the most common cases.
-
-.Comparing annotations of Properties vs Action Parameters
-[cols="2,3,3",options="header"]
-|===
-|value type/semantic
-|(JDO) property +
-`javax.jdo.annotations`
-|action parameter
-
-|string (length)
-|`@Column(length=50)`
-|`@Parameter(maxLength=50)`
-
-|big decimal (precision)
-|`@Column(scale=2)`
-|`@javax.validation.constraints.Digits(fraction=2)`
-
-|optionality
-|`@Column(allowsNull="true")`
-|`@javax.annotation.Nullable` or `ParameterLayout(optionality=OPTIONAL`)
-|===
 
diff --git a/antora/components/userguide/modules/ROOT/pages/mixins.adoc b/antora/components/userguide/modules/ROOT/pages/mixins.adoc
index 4da5ebcbfb..1012eabb03 100644
--- a/antora/components/userguide/modules/ROOT/pages/mixins.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/mixins.adoc
@@ -6,237 +6,5 @@
 
 
 
-[#mixins]
-== Mixins
-
-The next type of domain object is the *mixin*.
-These are similar to traits or extension methods in other programming languages.
-A mixin object allows one class to contribute behaviour - actions, (derived) properties and (derived) collections - to another domain object, either a domain entity or view model.
-Or rather, the mixin _appears_ to contribute the behaviour/state in the UI; the underlying domain class being "decorated" does not know this is happening.
-
-This is therefore a key technique to allow the app to stay decoupled, so that it doesn't degrade into the proverbial link:http://www.laputan.org/mud/mud.html#BigBallOfMud["big ball of mud"].
-There's a lot more discussion on this topic in xref:overview.adoc#modules[modules], below.
-
-
-Mixins are also a convenient mechanism for grouping functionality even for a concrete type, helping to rationalize about the dependency between the data and the behaviour.
-Each mixin is in effect a single behavioural "responsibility" of the domain object.
-
-In fact, we find mixins nicely balance inside-out vs outside-in ways of thinking about a system:
-
-* inside-out tends to focus on the structure, the nouns that make up the domain.
-
-* outside-in tends to focus on the behaviour, that is the functionality that the system provides to automate the business processes; the verbs, in other words.
-
-So, while Apache Causeway allows you to put behaviour onto the underlying domain entities, it can often be better to treat the domain entities as immutable.
-Instead, use mixins to implement behaviour.
-When using an agile development methodology, it's common for a user story to correspond to a new mixin.
-
-There are also practical reasons for moving behaviour out of entities even within the same module, because structuring your application this way helps support hot-reloading of Java classes (so that you can modify and recompile your application without having to restart it).
-This can provide substantial productivity gains.
-
-The Hotspot JVM has limited support for hot reloading; generally you can change method implementations but you cannot introduce new methods.
-However, the link:https://dcevm.github.io/[DCEVM] open source project will patch the JVM to support much more complete hot reloading support.
-There are also commercial products such as JRebel.
-
-[TIP]
-====
-Details on how to actually write a mixin (the programming model for mixins) is
-xref:userguide:ROOT:mixins.adoc[here].
-====
-
-.DCI Architecture
-****
-Mixins are an implementation of the link:http://www.artima.com/articles/dci_vision.html[DCI architecture] architecture, as formulated and described by link:https://en.wikipedia.org/wiki/Trygve_Reenskaug[Trygve Reenskaug] and link:https://en.wikipedia.org/wiki/Jim_Coplien[Jim Coplien].
-Reenskaug was the inventor of the MVC pattern (and also the external examiner for Richard Pawson's link:../../../../../ug/fun/_attachments/core-concepts/Pawson-Naked-Objects-thesis.pdf[PhD thesis]), while Coplien has a long history in object-orientation, C++ and patterns.
-
-DCI stands for Data-Context-Interaction and is presented as an evolution of object-oriented programming, but one where behaviour is bound to objects dynamically rather than statically in some context or other.
-The mixin pattern is Apache Causeway' straightforward take on the same basic concept.
-****
-
-
-
-As described in the xref:userguide:ROOT:overview.adoc#mixins[overview], a mixin acts like a trait or extension method, allowing one module to contribute behaviour or derived state to another object.
-This is a *very powerful feature*, but at the same time is  easy to use.
-
-Under the covers, all mixins are basically actions that use the mixee as one of their arguments.
-
-* if the mixin action takes some arguments (apart from the mixee), then it will only ever be rendered as an action
-* if the mixin action is not query-only, then again it will only ever be rendered as an action
-* if the mixin action is query-only and takes no arguments, then it can either be rendered as an action or as some derived state
-** if as an action, then it's basically a query to find and return some related info (eg "most recent order", "last 5 orders")
-** if as derived state, then it will be rendered as a property (if it returns a scalar), or as a collection (if it returns a vector).
-
-
-Accordingly, a mixin can be defined using the xref:refguide:applib:index/annotation/Action.adoc[@Action], xref:refguide:applib:index/annotation/Property.adoc[@Property] or xref:refguide:applib:index/annotation/Collection.adoc[@Collection] annotations, but defined at the domain class level rather than at the method level.
-
-The mixin is expected to follow the naming convention `SomeType_mixinName`, with the name of the contributed member being contributed is inferred from the name of the class itself, being everything after the last '_'.
-For example, if the mixin is called `DocumentHolder_documents`, then the mixee is `DocumentHolder` interface, and `documents` is the name of the contributed collection.
-
-This example also hints at why mixins are so powerful: the mixee's type is usually an interface, not a concrete class.
-Thus, any class that implements `DocumentHolder`, eg `Customer` or `Order`, will be contributed to.
-
-== Contributed Collection
-
-The example below shows in more detail how to contribute a collection, using xref:refguide:applib:index/annotation/Collection.adoc[@Collection].
-The method is expected to be called "coll":
-
-[source,java]
-----
-@Collection                                     // <.>
-@RequiredArgsConstructor                        // <.>
-public class DocumentHolder_documents {
-
-    private final DocumentHolder holder;
-
-    public List<Document> coll() { /* ... */ }  // <.>
-        ...
-    }
-    public boolean hideColl() { /* ... */ }     // <.>
-}
-----
-<.> indicates this is a collection mixin (that contributes its method as an association) with method name 'coll'
-<.> mixee is injected into the mixin
-<.> method (which must be called "coll") must accept no arguments, be query-only, and return a collection
-<.> supporting methods (discussed in xref:business-rules.adoc[business rules]) follow the usual naming conventions
-(Because the collection is derived/read-only, the only supporting method that is relevant is `hideColl()`).
-
-The above will result in a contributed collection "documents" for all types that implement/extend from `DocumentHolder`.
-
-
-
-== Contributed Property
-
-Contributed properties can likewise be defined using xref:refguide:applib:index/annotation/Property.adoc[@Property]; this implies a method called "prop":
-
-[source,java]
-----
-@Property                                                       // <.>
-@RequiredArgsConstructor                                        // <.>
-public class DocumentHolder_mostRecentDocument {
-
-    private final DocumentHolder holder;
-
-    public Document prop() {                                    // <.>
-        ...
-    }
-    public boolean hiderProp() { /* ... */ }                    // <.>
-}
-----
-<.> indicates this is a property mixin (that contributes its method as an association) with method name 'prop'
-<.> mixee is injected into the mixin
-<.> method (which must be called "prop") must accept no arguments, be query-only, and return a scalar value
-<.> supporting methods (discussed in xref:business-rules.adoc[business rules]) follow the usual naming conventions
-(Because the property is derived/read-only, the only supporting method that is relevant is `hideProp()`).
-
-This contributes a property called "mostRecentDocument".
-
-
-[#contributed-action]
-== Contributed Action
-
-Contributed actions are defined similarly, using xref:refguide:applib:index/annotation/Action.adoc[@Action]; this implies a method called "act".
-For example:
-
-[source,java]
-----
-@Action                                                         // <.>
-@RequiredArgsConstructor                                        // <.>
-public class DocumentHolder_addDocument {
-
-    private final DocumentHolder holder;
-
-    public Document> act(Document doc) {                        // <.>
-        ...
-    }
-    public boolean hideAct() { /* ... */ }                      // <.>
-}
-----
-<.> indicates this is an action mixin (that contributes its method as an action) with method name 'act'
-<.> mixee is injected into the mixin
-<.> method must be called "act"
-Unlike contributed properties and collections, contributed actions can accept parameters, and have any semantics.
-<.> xref:business-rules.adoc[supporting methods] follow the usual naming conventions
-
-This contributes an action called "addDocument".
-
-
-[[mixins-as-nested-classes]]
-== As Nested Classes
-
-While mixins primary use case is as a means of allowing contributions from one module to the types of another module, they are also a convenient mechanism for grouping functionality/behaviour against a concrete type.
-All the methods and xref:business-rules.adoc[supporting methods] end up in a single construct, and the dependency between that functionality and the rest of the object is made more explicit.
-
-We might therefore want to use a mixin within the same module as the mixee; indeed even within the same package or class as the mixee.
-In other words, we could define the mixin as nested static class of the mixee it contributes to.
-
-In the previous examples the "_" is used as a separator between the mixin type and mixin name.
-However, to support mixins as nested classes, the character "$" is also recognized as a separator.
-
-For example, the following refactors the "updateName" action -- of the `SimpleObject` class in xref:docs:starters:simpleapp.adoc[SimpleApp] start app -- into a nested mixin:
-
-[source,java]
-----
-public class SimpleObject /* ... */ {
-
-    // ...
-
-    @Action(semantics = IDEMPOTENT,
-            commandPublishing = Publishing.ENABLED,
-            executionPublishing = Publishing.ENABLED,
-            associateWith = "name",
-            domainEvent = updateName.DomainEvent.class)                 // <2>
-    public class updateName {                                           // <.>
-
-        public class DomainEvent extends
-            SimpleModule.ActionDomainEvent<SimpleObject.updateName> {}  // <.>
-
-        public SimpleObject act(@Name final String name) {
-            setName(name);                                              // <.>
-            return SimpleObject.this;
-        }
-        public String default0Act() {                                   // <.>
-            return getName();                                           // <.>
-        }
-    }
-    // ...
-}
-----
-<.> Mixin class is not `static`, so that the containing object is implicitly available.
-+
-Its name can be either "camelCase" or "PascalCase", either will work.
-
-<.> Domain event can be declared within the mixin, again, not `static`.
-+
-Note that it is genericised on the mixin, not on the mixee
-
-<.> Acts on the owning instance.
-<.> xref:business-rules.adoc[Supporting methods] follow the same naming convention.
-<.> Acts on the owning instance.
-
-
-== Programmatic usage
-
-When a domain object is rendered, the framework will automatically instantiate all required mixins and delegate to them dynamically.
-If writing integration tests or fixtures, or (sometimes) just regular domain logic, then you may need to instantiate mixins directly.
-
-For this you can use the xref:refguide:applib:index/services/factory/FactoryService.adoc[FactoryService#mixin(...)] method.
-
-For example:
-
-[source,java]
-----
-DocumentHolder_documents mixin =
-    factoryService.mixin(DocumentHolder_documents.class, customer);
-----
-
-Alternatively, you can use xref:refguide:applib:index/services/inject/ServiceInjector.adoc[ServiceInjector] to inject domain services after the mixin has been instantiated.
-You'll need to use this method if using nested non-`static` mixins:
-
-
-[source,java]
-----
-SimpleObject.updateName mixin =
-    serviceInjector.injectServicesInto( simpleObject.new updateName() );
-----
-
-
+include::partial$mixins/_intro.adoc[leveloffset=+1]
+include::partial$mixins/progmodel.adoc[leveloffset=+1]
diff --git a/antora/components/userguide/modules/ROOT/pages/view-models.adoc b/antora/components/userguide/modules/ROOT/pages/view-models.adoc
index e4615704c9..abd69f79e1 100644
--- a/antora/components/userguide/modules/ROOT/pages/view-models.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/view-models.adoc
@@ -7,460 +7,7 @@
 
 
 
-[#view-models]
-== View Models
-
-View models are similar to entities in that (unlike domain services) there can be many instances of any given type.
-End users interact with view models in the same way as a domain entity, indeed they are unlikely to distinguish one from the other.
-
-However, whereas domain entities are mapped to a datastore, view models are not.
-Instead they are recreated dynamically by serializing their state, ultimately into the URL itself (meaning their state it is in effect implicitly managed by the client browser).
-You will notice that the URL for view models (as shown in xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)] or xref:vro:ROOT:about.adoc[RestfulObjects viewer]) tends to be quite long.
-
-This capability opens up a number of more advanced use cases:
-
-* In the same way that an (RDBMS) database view can aggregate and abstract from multiple underlying database tables, a view model sits on top of one or many underlying entities.
-
-* A view model could also be used as a proxy for some externally managed entity, accessed over a web service or REST API; it could even be a representation of state held in-memory (such as user preferences, for example).
-
-* view models can also be used to support a particular use case.
-An example that comes to mind is to expose a list of scanned PDFs to be processed as an "intray", showing the list of PDFs on one side of the page, and the current PDF being viewed on the other.
-Such view models are part of the application layer, not part of the domain layer (where entities live).
-
-We explore these use cases in more detail below.
-
-[TIP]
-====
-Details on how to actually write a view model (the programming model for view models) can be found xref:userguide:ROOT:view-models.adoc[here].
-====
-
-=== Externally-managed entities
-
-Sometimes the entities that make up your application are persisted not in the local database but reside in some other system, for example accessible only through a SOAP web service.
-Logically that data might still be considered a domain entity and we might want to associate behaviour with it, however it cannot be modelled as a domain entity if only because JDO/DataNucleus doesn't know about the entity nor how to retrieve or update it.
-
-There are a couple of ways around this: we could either replicate the data somehow from the external system into the Causeway-managed database (in which case it is once again just another domain entity), or we could set up a stub/proxy for the externally managed entity.
-This proxy would hold the reference to the externally-managed domain entity (eg an external id), as well as the "smarts" to know how to interact with that entity (by making SOAP web service calls etc).
-
-The stub/proxy is a type of view model: a view -- if you like -- onto the domain entity managed by the external system.
-
-=== In-memory entities
-
-As a variation on the above, sometimes there are domain objects that are, conceptually at least entities, but whose state is not actually persisted anywhere, merely held in-memory (eg in a hash).
-
-A simple example is read-only configuration data that is read from a config file (eg log4j appender definitions) but thereafter is presented in the UI just like any other entity.
-
-=== Application-layer view models
-
-Domain entities (whether locally persisted or managed externally) are the bread-and-butter of Apache Causeway applications: the focus after all, should be on the business domain concepts and ensuring that they are solid.
-Generally those domain entities will make sense to the business domain experts: they form the _ubiquitous language_ of the domain.
-These domain entities are part of the domain layer.
-
-When developing an Apache Causeway application you will most likely start off with the persistent domain entities: `Customer`, `Order`, `Product`, and so on.
-For some applications this may well suffice.
-
-That said, it may not always be practical to expect end-users of the application to interact solely with those domain entities.
-If the application needs to integrate with other systems, or if the application needs to support reasonably complex business processes, then you may need to look beyond just domain entities; view models are the tool of choice.
-
-One such use case for view models is to help co-ordinate complex business processes; for example to perform a quarterly invoicing run, or to upload annual interest rates from an Excel spreadsheet, or prepare payment batches from incoming invoices, to be uploaded to an external payment system.
-In these cases the view model managing the business process might have some state of its own, but in most cases that state does not need to be persisted between user sessions.
-Many of the actions will be queries but in some cases such view model actions might also modify state of underlying domain entities.
-Either way, ultimately these actions just delegate down to the domain-layer.
-
-.Desire Lines
-****
-One way to think of application view models is that they model the "desire line": the commonly-trod path that end-users must follow to get from point A to point B as quickly as possible.
-
-To explain: there are link:http://ask.metafilter.com/62599/Where-the-sidewalk-ends[documented] link:https://sivers.org/walkways[examples] link:http://www.softpanorama.org/People/Wall/larry_wall_articles_and_interviews.shtml[that] architects of university campus will only add in paths some while after the campus buildings are complete: let the pedestrians figure out the routes they want to take.
-One name for this idea is "desire lines".
-
-What that means is you should add view models _after_ having built up the domain layer, rather than before.
-These view models pave that commonly-trod path, automating the steps that the end-user would otherwise have to do by hand.
-
-However, you _shouldn't_ try to build out a domain layer that could support every conceivable use case before starting to think about view models.
-Instead, iterate.
-Identify the use case/story/end-user objective that will deliver value to the business.
-Build out the minimum domain entities to support that use case.
-Then, introduce view models to simplify high-volume end-user interactions with the system (perhaps automating several related use cases together).
-****
-
-Another common requirement is to show a dashboard of the most significant data in the system to a user, often pulling in and aggregating information from multiple points of the app.
-Obtaining this information by hand (by querying the respective services/repositories) would be tedious and slow; far better to have a dashboard do the job for the end user.
-
-A dashboard object is a model of the most relevant state to the end-user, in other words it is (quite literally) a view model.
-It is not a persisted entity, instead it belongs to the application layer.
-
-=== DTOs
-
-DTOs (data transfer objects) are simple classes that (according to link:https://en.wikipedia.org/wiki/Data_transfer_object[wikipedia]) "carry data between processes".
-
-If those two processes are parts of the same overall application (the same team builds and deploys both server and client) then there's generally no need to define a DTO; just access the entities using Apache Causeway' xref:vro:ROOT:about.adoc[RestfulObjects viewer].
-
-On the other hand, if the client consuming the DTO is a different application -- by which we mean developed/deployed by a different (possible third-party) team -- then the DTOs act as a formal contract between the provider and the consumer.
-In such cases, exposing domain entities over xref:vro:ROOT:about.adoc[RestfulObjects] would be "A Bad Thing"(TM) because the consumer would in effect have access to implementation details that could then not be easily changed by the producer.
-There's plenty of discussion on this topic (eg link:https://stackoverflow.com/questions/36174516/rest-api-dtos-or-not[here] and link:https://juristr.com/blog/2012/10/lessions-learned-dont-expose-ef-entities-to-the-client-directly/[here]).
-Almost all of these recommend exposing only DTOs (which is to say view models), not domain entities, in REST APIs.
-
-To support this use case, a view model can be defined such that it can act as a DTO.
-This is done by annotating the class using JAXB annotations; this allows the consumer to obtain the DTO in XML format along with a corresponding XSD schema describing the structure of that XML.
-
-
-These DTOs are still usable as "regular" view models; they will render in the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)] just like any other.
-In fact (as the xref:userguide:ROOT:view-models.adoc#jaxb[programming model] section below makes clear), these JAXB-annotated view models are in many regards the most powerful of all the alternative ways of writing view models.
-
-It's also worth noting that it is also possible to download the XML (or XSD) straight from the UI, useful during development.
-The view model simply needs to implement the xref:refguide:applib:index/mixins/dto/Dto.adoc[Dto] marker interface; the framework has xref:refguide:applib-classes:mixees-and-mixins.adoc#Dto[mixins] that contribute the download actions to the view model.
-
-=== For REST Clients
-
-The xref:vro:ROOT:about.adoc[Restful Objects] viewer automatically provides a REST API for both domain entities.
-Or, you can use it to only expose view models, taking care to map the state of the domain entity/ies into a view model.
-The question to consider is whether the REST API is a public API or an internal private API:
-
-* If it's a public API, which is to say that there are third-party clients out over which you have no control, then view models are the way to go.
-+
-In this case view models provide an isolation layer which allow you to modify the structure of the underlying domain entities without breaking this API.
-
-* If it's a private API, which is to say that the only clients of the REST API are under your control, then view models are an unnecessary overhead.
-+
-In this case, just expose domain entities directly.
-
-The caveat to the "private API" option is that private APIs have a habit of becoming public APIs.
-Even if the REST API is only exposed within your organisation's intranet, other teams may "discover" your REST API and start writing applications that consume it.
-If that REST API is exposing domain entities, you could easily break those other teams' clients if you refactor.
-
-[NOTE]
-====
-The link:https://projects.spring.io/spring-data-rest/[Spring Data REST] subproject has a similar capability of being able to expose domain entities as REST resources.
-This link:https://stackoverflow.com/questions/38874746/is-it-problematic-that-spring-data-rest-exposes-entities-via-rest-resources-with[SO question], which debates the pros-and-cons, is also worth a read.
-====
-
-If your REST API is intended to be public (or you can't be sure that it will remain private), then exposing view models will entail a lot of marshalling of state from domain entities into view models.
-There are numerous open source tools that can help with that, for example link:http://modelmapper.org/[Model Mapper], link:http://dozer.sourceforge.net/[Dozer] and link:https://github.com/orika-mapper/orika[Orika].
-
-Or, rather than marshalling state, the view model could hold a reference to the underlying domain entity/ies and dynamically read from it (ie, all the view model's properties are derived from the entity's).
-
-A third option is to define an RDBMS view, and then map a "non-durable" entity to that view.
-The RDBMS view then becomes the public API that must be preserved.
-ORMs such as DataNucleus link:http://www.datanucleus.org:15080/products/accessplatform_5_1/jdo/mapping.html#schema_rdbms_views[support this].
-
-
-
-
-
-As described in the xref:userguide:ROOT:overview.adoc#view-models[overview], view models are generally domain objects concerned with facilitating or optimising a particular business process.
-As such, they bring together the domain entities involved in that process and provide actions to be performed against or on those domain entities.
-
-Such view models are generally considered to reside in the application layer, and -- unlike domain entities -- their state is _not_ persisted to a database.
-Instead, it is serialized into its identifier (in effect, its URL).
-The framework unpacks this URL to infer/recreate the view model's state with each interaction.
-
-
-The framework provides three ways to implement a view model:
-
-* Annotating the class using JAXB annotations; this allows the state of the object's properties and also its collections.
-+
-The serialized form of these view models is therefore XML, which also enables these view models to act as DTO (useful for various integration scenarios).
-
-* Using Apache Causeway specific annotations.
-+
-This is more concise, but less powerful: only the state of the object's properties is serialized -- collections are ignored -- and not every datatype is recognized.
-+
-On the other hand, they are more likely to perform better.
-
-* Implementing the xref:refguide:applib:index/ViewModel.adoc[ViewModel] interface.
-+
-With this option you take full control of the marshalling and unmarshalling of the object's state to/from a string.
-
-
-These options are discussed in more detail in the sections below.
-
-[[jaxb]]
-== JAXB View Models
-
-Here's a typical example of a JAXB view model, to allow (certain properties of) two ``Customer``s to be compared:
-
-[source,java]
-.CompareCustomers.java, using JAXB
-----
-@XmlRootElement(name = "compareCustomers")          // <.>
-@XmlType(
-        propOrder = {                               // <.>
-            "customer1",
-            "customer2"
-        }
-)
-@XmlAccessorType(XmlAccessType.FIELD)               // <.>
-public class CompareCustomers {
-
-    @XmlElement(required = true)                    // <.>
-    @Getter @Setter
-    Customer customer1;
-
-    @XmlElement(required = true)                    // <.>
-    @Getter @Setter
-    Customer customer2;
-
-    @XmlTransient                                   // <.>
-    public String getCustomer1Name() {
-        return getCustomer1().getName();
-    }
-
-    @XmlTransient                                   // <.>
-    public String getCustomer2Name() {
-        return getCustomer2().getName();
-    }
-
-    ...
-}
-----
-
-<.> The JAXB xref:refguide:applib-ant:XmlRootElement.adoc[@XmlRootElement] annotation indicates this is a view model to Apache Causeway, which then uses JAXB to serialize the state of the view model between interactions
-<.> Optionally, the properties of the view model can be listed using the `XmlType#propOrder` attribute. +
-This is an all-or-nothing affair: either all properties must be listed, or else the annotation omitted.
-<.> Specifying field accessor type allows the Lombok `@Getter` and `@Setter` annotations to be used.
-<.> The `@XmlElement` indicates the property is part of the view model's state.
-For collections, the `@XmlElementWrapper` would also typically be used.
-<.> The `@XmlTransient` indicates that the property is derived and should be ignored by JAXB. +
-The derived properties could also have been implemented using xref:userguide:ROOT:overview.adoc#mixins[mixins].
-
-[IMPORTANT]
-====
-Be aware that all the state will ultimately converted into a URL-safe form (by way of the xref:refguide:applib:index/services/urlencoding/UrlEncodingService.adoc[UrlEncodingService]).
-
-There are limits to the lengths of URLs, however.
-If the URL does exceed limits or contains invalid characters, then provide a custom implementation of xref:refguide:applib:index/services/urlencoding/UrlEncodingService.adoc[UrlEncodingService] to handle the memento string in some other fashion (eg substituting it with a GUID, with the memento cached somehow on the server).
-====
-
-
-
-[#referencing-domain-entities]
-=== Referencing Domain Entities
-
-It's quite common for view models to be "backed by" (be projections of) some underlying domain entity.
-For example, the `CompareCustomers` view model described xref:userguide:ROOT:view-models.adoc#jaxb[above] actually references two underlying ``Customer`` entities.
-
-It wouldn't make sense to serialize out the state of a persistent entity.
-However, the identity of the underlying entity is well defined; Apache Causeway defines the xref:refguide:schema:common.adoc[common schema] which defines the `<oid-dto>` element (and corresponding `OidDto` class): the object's type and its identifier.
-This is basically a formal XML equivalent to the `Bookmark` object obtained from the xref:refguide:applib:index/services/bookmark/BookmarkService.adoc[BookmarkService].
-
-There is only one requirement to make this work: every referenced domain entity must be annotated with xref:refguide:applib-ant:XmlJavaTypeAdapter.adoc[@XmlJavaTypeAdapter], specifying the framework-provided `PersistentEntityAdapter`.
-And this class is similar to the xref:refguide:applib:index/services/bookmark/BookmarkService.adoc[BookmarkService]: it knows how to create an `OidDto` from an object reference.
-
-Thus, in our view model we can legitimately write:
-
-[source,java]
-----
-public class CompareCustomers {
-
-    @XmlElement(required = true)
-    @Getter @Setter
-    Customer customer1;
-    ...
-}
-----
-
-All we need to do is remember to add that `@XmlJavaTypeAdapter` annotation to the referenced entity:
-
-[source,java]
-----
-@XmlJavaTypeAdapter(PersistentEntityAdapter.class)
-public class Customer ...  {
-    ...
-}
-----
-
-It's also possible for a DTO view models to hold collections of objects.
-These can be of any type, either simple properties, or references to other objects.
-The only bit of boilerplate that is required is the `@XmlElementWrapper` annotation.
-This instructs JAXB to create an XML element (based on the field name) to contain each of the elements.
-(If this is omitted then the contents of the collection are at the same level as the properties; almost certainly not what is required).
-
-For example, we could perhaps generalize the view model to hold a set of ``Customer``s to be compared:
-
-[source,java]
-----
-public class CompareCustomers {
-    ...
-    @XmlElementWrapper
-    @XmlElement(name = "customers")
-    @Getter @Setter
-    protected List<Customer> customersToCompare = Lists.newArrayList();
-}
-----
-
-
-[[joda-datatypes]]
-=== JODA Time Datatypes
-
-If your JAXB view model contains fields using the JODA datatypes (`LocalDate` and so on), then `@XmlJavaTypeAdapter` additional annotations in order to "teach" JAXB how to serialize out the state.
-
-The Apache Causeway applib provides a number of adapters to use out-of-the-box.
-For example:
-
-[source,java]
-----
-@XmlRootElement(name = "categorizeIncomingInvoice")
-@XmlType(
-        propOrder = {
-                ...
-                "dateReceived",
-                ...
-        }
-)
-@XmlAccessorType(XmlAccessType.FIELD)
-public class IncomingInvoiceViewModel extends IncomingOrderAndInvoiceViewModel {
-
-    @XmlJavaTypeAdapter(JodaLocalDateStringAdapter.ForJaxb.class)
-    private LocalDate dateReceived;
-
-    ...
-}
-----
-
-The full list of adapter classes are:
-
-.JAXB adapters
-[cols="1a,2a",options="header"]
-|===
-
-| JODA datatype
-| Adapter
-
-.2+| `org.joda.time.DateTime`
-| `JodaDateTimeStringAdapter.ForJaxb`
-| `JodaDateTimeXMLGregorianCalendarAdapter.ForJaxb`
-
-.2+| `org.joda.time.LocalDate`
-| `JodaLocalDateStringAdapter.ForJaxb`
-| `JodaLocalDateXMLGregorianCalendarAdapter.ForJaxb`
-
-.2+| `org.joda.time.LocalDateTime`
-| `JodaLocalDateTimeStringAdapter.ForJaxb`
-| `JodaLocalDateTimeXMLGregorianCalendarAdapter.ForJaxb`
-
-
-.2+| `org.joda.time.LocalTime`
-| `JodaLocalTimeStringAdapter.ForJaxb`
-| `JodaLocalTimeXMLGregorianCalendarAdapter.ForJaxb`
-
-| `java.sql.Timestamp`
-| `JavaSqlTimestampXmlGregorianCalendarAdapter.ForJaxb`
-
-
-|===
-
-[TIP]
-====
-If you want use other Joda data types, check out link:http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html[this blog post].
-====
-
-
-
-[[non-jaxb]]
-== Non-JAXB View Models
-
-Instead of using JAXB to specify a view model, it is also possible to use the xref:refguide:applib:index/annotation/DomainObject.adoc[@DomainObject] with a xref:refguide:applib:index/annotation/DomainObject.adoc#nature[nature] of xref:refguide:applib:index/annotation/Nature.adoc#VIEW_MODEL[VIEW_MODEL].
-
-This approach is not as powerful as using the JAXB-style of view models, because only the state of properties -- not collections -- is serialized, and moreover only certain data types are recognised.
-On the plus side, it takes less effort.
-
-For example:
-
-[source,java]
-.CompareCustomers.java, using @DomainObject(nature = VIEW_MODEL)
-----
-@DomainObject(nature = Nature.VIEW_MODEL)           // <.>
-public class CompareCustomers {
-
-    @Property                                       // <.>
-    @Getter @Setter
-    Customer customer1;
-
-    @Property                                       // <2>
-    @Getter @Setter
-    Customer customer2;
-
-    public String getCustomer1Name() {
-        return getCustomer1().getName();
-    }
-
-    public String getCustomer2Name() {
-        return getCustomer2().getName();
-    }
-
-    ...
-}
-----
-
-<.> declares the domain object as a view model
-<.> fields must be annotated with `@Property` so that they are part of the metamodel.
-+
-Note that they do _not_ need to be visible, however.
-
-
-
-[[view-model-interface]]
-== `ViewModel` interface
-
-The most flexible approach to implement a view model is to implement the xref:refguide:applib:index/ViewModel.adoc[ViewModel] interface.
-
-For example:
-
-[source,java]
-.CompareCustomers.java, using ViewModel interface
-----
-@DomainObject
-public class CompareCustomers implements ViewModel {    // <.>
-
-    public String viewModelMemento() {                  // <.>
-        return getCustomer1().getRef() + ":"
-             + getCustomer2().getRef();
-    }
-    public void viewModelInit(String memento) {         // <.>
-        val ref1 = memento.split[":"](0);
-        customer1 = customerRepository.findByRef(ref1));
-        val ref2 = memento.split[":"](1);
-        customer2 = customerRepository.findByRef(ref2);
-    }
-
-    @Getter @Setter
-    Customer customer1;
-
-    @Getter @Setter
-    Customer customer2;
-
-    public String getCustomer1Name() {
-        return getCustomer1().getName();
-    }
-
-    public String getCustomer2Name() {
-        return getCustomer2().getName();
-    }
-
-    @Inject
-    CustomerRepository customerRepository;
-
-    ...
-}
-----
-
-
-
-== Self-persisting Domain entities
-
-Sometimes we may have domain entities whose persistence is _not_ managed by JDO or JPA mechanism, in other words they take responsibility for their own persistence.
-
-We can characterise these as:
-
-* external entities
-+
-For example the application may interact synchronously with state exposed on another system through a REST or SOAP API.
-In this case the entity with your Apache Causeway application is a proxy or a facade for the state on the external system
-
-* internal entities
-+
-For example the entity might include a data structure that is best persisted in a custom datastore, for example a graph database such as neo4j.
 
-Because such entities are responsible for their own state management, they can be implemented as view models.
 
+include::partial$view-models/_intro.adoc[leveloffset=+1]
+include::partial$view-models/progmodel.adoc[leveloffset=+1]
diff --git a/antora/components/userguide/modules/ROOT/partials/domain-services/_intro.adoc b/antora/components/userguide/modules/ROOT/partials/domain-services/_intro.adoc
index eff5a87226..ba9356bdf7 100644
--- a/antora/components/userguide/modules/ROOT/partials/domain-services/_intro.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/domain-services/_intro.adoc
@@ -39,334 +39,3 @@ Said another way: it is the means by which Apache Causeway helps you avoid the a
 Domain services are instantiated once and once only by the framework, and are used to centralize any domain logic that does not logically belong in a domain entity or value.
 
 
-== Programming Model
-
-=== Declaring a Domain Service
-
-Domain services that are visible in the UI or REST API are annotated with xref:refguide:applib:index/annotation/DomainService.adoc[@DomainService()], while services that are _programmatic_ in nature should be simply annotated using Spring's link:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Component.html[@Component] or one of its specializations, eg
-link:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Service.html[@Service]
-or link:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Repository.html[@Repository].
-
-Apache Causeway runs on top of Spring Boot, and relies on Spring Boot for dependency injection using `@javax.inject.Inject`.
-The `@javax.annotation.Priority` annotation is used to prioritize multiple service implementations.
-*This allows any framework-provided domain service to be replaced by a user-defined one if required*, simply by assigning it with an earlier precedence (= higher priority).
-
-
-=== Nature of Service
-
-Apache Causeway uses Spring Boot to instantiate and manage the dependency injection of domain services.
-The vast majority of these are singleton (application) scoped; a smaller number are request scoped (using the xref:refguide:applib:index/annotation/InteractionScope.adoc[@InteractionScope] annotation).
-
-Accordingly, all domain services are annotated or meta-annotated using Spring's link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Component.html[@Component] annotation.
-
-For domain services to be visible in the Apache Causeway UI, they must be annotated with xref:refguide:applib:index/annotation/DomainService.adoc[@DomainService].
-Its xref:refguide:applib:index/annotation/DomainService.adoc#nature[#nature()] attribute is either:
-
-* `VIEW` (the default if not specified)
-+
-which indicates that the actions should appear on the menu of the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)], and as top-level actions for the REST API provided by the xref:vro:ROOT:about.adoc[REST API (Restful Objects viewer)].
-
-* `REST`
-+
-which indicates that the actions should appear in the REST API provided by the xref:vro:ROOT:about.adoc[REST API (Restful Objects viewer)], but not rendered by the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)].
-
-It's also possible to define a "programmatic" domain service, meaning one that is instantiated and injected by Spring Boot, but is not visible in the UI or REST API.
-Such programmatic services are usually annotated with Spring's link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Service.html[@Service] annotation or link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Repository.html[@Repository].
-
-== Repository and Factory
-
-The repository/factory uses an injected xref:refguide:applib:index/services/repository/RepositoryService.adoc[RepositoryService] to both instantiate new objects and to query the database for existing objects of a given entity type.
-Generally these services are not visible in UI, and so are annotated with link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Repository.html[@Repository]
-
-For example:
-
-[source,java]
-----
-import org.springframework.stereotype.Repository;
-import lombok.RequiredArgsConstructor;
-
-@Repository                                                     // <.>
-@RequiredArgsConstructor(onConstructor_ = {@Inject} )           // <.>
-public CustomerRepository {
-
-    private final RepositoryService repositoryService;
-
-    public List<Customer> findByName(String name) {
-        return repositoryService.allMatches(                    // <.>
-                Query.named(Customer.class, "findByName")
-                    .withParameter("name", name));
-    }
-
-    public Customer newCustomer(...) {
-        Customer Customer =
-            repositoryService.detachedEntity(Customer.class);   // <.>
-        ...
-        return repositoryService.persistAndFlush(Customer);     // <.>
-    }
-
-    public List<Customer> allCustomers() {                      // <.>
-        return repositoryService.allInstances(Customer.class);
-    }
-}
-----
-<.> Detected and managed by Spring Boot.
-<.> Lombok annotation for dependency injection of xref:refguide:applib:index/services/repository/RepositoryService.adoc[RepositoryService] through generated constructor.
-<.> uses injected `RepositoryService` to query via JDOQL.
-<.> uses injected `RepositoryService` to instantiate a not-yet-persisted domain entity ...
-<.> \... and then save into the database a new `Customer` instance.
-<.> Returns all instances (useful for prototyping, probably not for production).
-
-There is no need to annotate the actions; they are implicitly hidden because of the domain service's nature.
-
-[TIP]
-====
-JDO/Datanucleus also supports link:http://www.datanucleus.org:15080/products/accessplatform_5_2/jdo/query.html#jdoql_typed[type-safe queries].
-These can be executed through the xref:refguide:persistence:index/jdo/applib/services/JdoSupportService.adoc[JdoSupportService] domain service.
-====
-
-== Menu
-
-Menu services provide actions to be rendered on the menu.
-
-For the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)], each service's actions appear as a collection of menu items of a named menu, and this menu is on one of the three menu bars provided by the Wicket viewer.
-Although these can be organised using annotations, it's usually easier to use a xref:userguide:ROOT:ui.adoc#file-based-menus[file-based layout file] (`menubars.layout.xml`).
-
-For the xref:vro:ROOT:about.adoc[REST API (Restful Objects viewer)], all menu services are shown in the services representation.
-
-[source,java]
-----
-import lombok.RequiredArgsConstructor;
-
-@DomainService(nature = NatureOfService.VIEW)                   // <.>
-@RequiredArgsConstructor(onConstructor_ = {@Inject} )           // <.>
-public class Customers {
-
-    @Inject
-    protected final CustomerRepository customerRepository;      // <.>
-
-    @Action(semantics = SemanticsOf.SAFE)
-    public List<Customer> findByName(                           // <.>
-            @ParameterLayout(named="Name")                      // <.>
-            final String name ) {
-        return customerRepository.findByName(name);             // <.>
-    }
-
-    @Action(semantics = SemanticsOf.NON_IDEMPOTENT)
-    public Customer newCustomer(...) {
-        return customerRepository.newCustomer(...);
-    }
-
-    @Action( semantics = SemanticsOf.SAFE,
-             restrictTo = RestrictTo.PROTOTYPING )              // <.>
-    public List<Customer> listAll() {
-        return customerRepository.listAll();
-    }
-}
-----
-
-<.> Identify the class as a domain service, to render in the menu.
-
-<.> Rendered in the UI as a "Find By Name" menu item underneath the "Customers" menu.
-
-<.> The xref:refguide:applib:index/annotation/ParameterLayout.adoc[@ParameterLayout] provides metadata for the parameter itself, in this case its name.
-+
-[TIP]
-====
-There's no need to name the action parameters if the code is compiled using the `-parameters` flag to the Java compiler (javac); the framework will then use the name of the parameter itself.
-====
-
-<.> the action implementation delegates to an injected repository.
-The framework can inject into not just other domain services but will also automatically into domain entities and view models.
-There is further discussion of service injection xref:userguide:ROOT:domain-services.adoc#injecting-services[below].
-
-<.> xref:refguide:applib:index/annotation/Action.adoc#restrictTo[Prototype] actions are rendered only in prototyping mode.
-A "list all" action such as this can be useful when exploring the domain with a small dataset.
-
-<.> Menu services typically delegate to an underlying repository/ies specific to the domain (rather than use `RepositoryService` directly, for example).
-
-Whether you separate out menu services from repository services is to some extent a matter of style.
-One perspective is that these two closely related domain services nevertheless have different responsibilities, and so could be kept separate.
-An alternative perspective is that the duplication is just unnecessary boilerplate, and conflicts with the naked objects philosophy.
-
-== Event Subscribers
-
-Domain services acting as event subscribers can subscribe to xref:userguide:ROOT:overview.adoc#lifecycle-events[lifecycle] events, influencing the rendering and behaviour of other objects.
-Behind the scenes this uses Spring's (in-memory) link:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#context-functionality-events-annotation[event bus].
-
-[source,java]
-----
-import org.springframework.stereotype.Service;
-import org.springframework.context.event.EventListener;
-import lombok.RequiredArgsConstructor;
-
-@Service                                                        // <.>
-@lombok.RequiredArgsConstructor(onConstructor_ = {@Inject} )
-public class OnCustomerDeletedCascadeDeleteOrders {
-
-    private final OrderRepository orderRepository;
-
-    @EventListener(Customer.DeletedEvent.class)                 // <.>
-    public void on(final Customer.DeletedEvent ev) {            // <.>
-        Customer customer = ev.getSource();
-        orderRepository.delete(customer);
-    }
-}
-----
-<.> subscriptions do not appear in the UI at all
-
-<.> use Spring Framework's link:https://javadoc.io/doc/org.springframework/spring-context/latest/org/springframework/context/event/EventListener.html[@EventListener]
-
-<.> the parameter type of the method corresponds to the event emitted on the event bus.
-The actual method name does not matter (though it must have `public` visibility).
-
-== Scoped services
-
-By default all domain services are application-scoped, in other words singletons.
-Such domain services are required to be thread-safe, usually satisfied by being intrinsically stateless.
-
-Sometimes though a service's lifetime is applicable only to a single (http) request.
-The framework has a number of such services, including a xref:refguide:applib:index/services/scratchpad/Scratchpad.adoc[Scratchpad] service (to share adhoc data between methods), and xref:refguide:applib:index/services/queryresultscache/QueryResultsCache.adoc[QueryResultsCache], which as its name suggests will cache query results.
-Such services _do_ hold state, but that state is scoped per (possibly concurrent) request and should be removed afterwards.
-
-The requirement for request-scoped services is supported using Apache Causeway' own xref:refguide:applib:index/annotation/InteractionScope.adoc[@InteractionScope] annotation (named because a short-lived `CausewaySession` is created for each request).
-This is used by the framework services and can also be used for user-defined services.
-
-For example:
-
-[source,java]
-----
-@Service
-@InteractionScope
-public class MyService {
-    ...
-    public void doSomething() { ... }
-}
-----
-
-Unlike application-scoped services, these request-scoped services must be injected using a slightly different idiom (borrowed from CDI), using a `javax.inject.Provider`.
-For example:
-
-[source,java]
-----
-import javax.inject.Provider;
-
-public class SomeClient {
-    ...
-    @Inject
-    Provider<MyService> myServiceProvider;  // <.>
-
-    public void someMethod() {
-        myServiceProvider.get()             // <.>
-                         .doSomething();
-}
-----
-<.> Inject using `Provider`
-<.> Obtain an instance using `Provider#get()`
-
-
-== Configuration
-
-Spring provides numerous mechanisms to configure domain services, both in terms of binding or passing in the configuration property to the service, and in terms of setting the value within some sort of configuration file.
-
-The mechanism prefered by Apache Causeway itself, and which you are free to use for your own services, is the type-safe link:https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/properties/ConfigurationProperties.html[ConfigurationProperties], whereby the configuration properties are expressed in a series of nested static classes.
-
-The xref:docs:starters:simpleapp.adoc[simpleapp starter app] includes an example:
-
-[source,java]
-----
-import org.springframework.validation.annotation.Validated;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-@ConfigurationProperties("app.simple-module")
-@lombok.Data
-@Validated
-public static class Configuration {
-    private final Types types = new Types();
-    @lombok.Data
-    public static class Types {
-        private final Name name = new Name();
-        @lombok.Data
-        public static class Name {
-            private final Validation validation = new Validation();
-            @lombok.Data
-            public static class Validation {
-                private char[] prohibitedCharacters =
-                    "!&%$".toCharArray();
-                private String message =
-                    "Character '{character}' is not allowed";
-            }
-        }
-    }
-}
-----
-
-This configuration property can be injected, like any other component, and makes the configuration value available in a type-safe fashion:
-
-[source,java]
-----
-val prohibitedCharacters =
-  config.getTypes().getName().getValidation().getProhibitedCharacters();
-----
-
-For this configuration property service to be discovered and managed by Spring, we need to use the link:https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/properties/EnableConfigurationProperties.html[EnableConfigurationProperties] annotation.
-This normally would reside on the owning module (discussed in more detail xref:modules.adoc[later]):
-
-[source,java]
-.SimpleModule.java
-----
-import org.springframework.context.annotation.Configuration
-
-@Configuration
-// ...
-@EnableConfigurationProperties({
-        SimpleModule.Configuration.class,
-})
-public class SimpleModule /* ... */ {
-    // ...
-}
-----
-
-
-These configuration properties can then be specified using either Spring's `application.yml` or `application.properties`.
-For example:
-
-[source,yml]
-.application.yml
-----
-app:
-  simple-module:
-    types:
-      name:
-        validation:
-          message: "'{character}' is invalid."
-          prohibited-characters: "&%$"
-----
-
-Moreover, Spring is able to configure the IDE so that these configuration values can be specified using code completion.
-All that is required is this dependency:
-
-[source,xml]
-.pom.xml
-----
-<!-- IDE support  -->
-<dependency>
-    <groupId>org.springframework.boot</groupId>
-    <artifactId>spring-boot-configuration-processor</artifactId>
-    <optional>true</optional>
-</dependency>
-----
-
-== Initialization
-
-Sometimes a domain service needs to perform initialization logic before it is ready to be used.
-
-In many cases, such initialization can be performed within the constructor.
-If the initialization has dependencies, then these can be injected using standard link:https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-spring-beans-and-dependency-injection[constructor injection].
-
-Alternatively, initialization can be moved into a `@PostConstruct` link:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-postconstruct-and-predestroy-annotations[lifecycle callback].
-Shutdown is similar; the framework will call any method annotated with `javax.annotation.PreDestroy`.
-
-
-
-
-
diff --git a/antora/components/userguide/modules/ROOT/partials/domain-services/progmodel.adoc b/antora/components/userguide/modules/ROOT/partials/domain-services/progmodel.adoc
index e1b8ff079a..477af08def 100644
--- a/antora/components/userguide/modules/ROOT/partials/domain-services/progmodel.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/domain-services/progmodel.adoc
@@ -4,3 +4,339 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+== Declaring a Domain Service
+
+Domain services that are visible in the UI or REST API are annotated with xref:refguide:applib:index/annotation/DomainService.adoc[@DomainService()], while services that are _programmatic_ in nature should be simply annotated using Spring's link:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Component.html[@Component] or one of its specializations, eg
+link:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Service.html[@Service]
+or link:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Repository.html[@Repository].
+
+Apache Causeway runs on top of Spring Boot, and relies on Spring Boot for dependency injection using `@javax.inject.Inject`.
+The `@javax.annotation.Priority` annotation is used to prioritize multiple service implementations.
+*This allows any framework-provided domain service to be replaced by a user-defined one if required*, simply by assigning it with an earlier precedence (= higher priority).
+
+
+== Nature of Service
+
+Apache Causeway uses Spring Boot to instantiate and manage the dependency injection of domain services.
+The vast majority of these are singleton (application) scoped; a smaller number are request scoped (using the xref:refguide:applib:index/annotation/InteractionScope.adoc[@InteractionScope] annotation).
+
+Accordingly, all domain services are annotated or meta-annotated using Spring's link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Component.html[@Component] annotation.
+
+For domain services to be visible in the Apache Causeway UI, they must be annotated with xref:refguide:applib:index/annotation/DomainService.adoc[@DomainService].
+Its xref:refguide:applib:index/annotation/DomainService.adoc#nature[#nature()] attribute is either:
+
+* `VIEW` (the default if not specified)
++
+which indicates that the actions should appear on the menu of the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)], and as top-level actions for the REST API provided by the xref:vro:ROOT:about.adoc[REST API (Restful Objects viewer)].
+
+* `REST`
++
+which indicates that the actions should appear in the REST API provided by the xref:vro:ROOT:about.adoc[REST API (Restful Objects viewer)], but not rendered by the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)].
+
+It's also possible to define a "programmatic" domain service, meaning one that is instantiated and injected by Spring Boot, but is not visible in the UI or REST API.
+Such programmatic services are usually annotated with Spring's link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Service.html[@Service] annotation or link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Repository.html[@Repository].
+
+
+== Repository and Factory
+
+The repository/factory uses an injected xref:refguide:applib:index/services/repository/RepositoryService.adoc[RepositoryService] to both instantiate new objects and to query the database for existing objects of a given entity type.
+Generally these services are not visible in UI, and so are annotated with link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Repository.html[@Repository]
+
+For example:
+
+[source,java]
+----
+import org.springframework.stereotype.Repository;
+import lombok.RequiredArgsConstructor;
+
+@Repository                                                     // <.>
+@RequiredArgsConstructor(onConstructor_ = {@Inject} )           // <.>
+public CustomerRepository {
+
+    private final RepositoryService repositoryService;
+
+    public List<Customer> findByName(String name) {
+        return repositoryService.allMatches(                    // <.>
+                Query.named(Customer.class, "findByName")
+                    .withParameter("name", name));
+    }
+
+    public Customer newCustomer(...) {
+        Customer Customer =
+            repositoryService.detachedEntity(Customer.class);   // <.>
+        ...
+        return repositoryService.persistAndFlush(Customer);     // <.>
+    }
+
+    public List<Customer> allCustomers() {                      // <.>
+        return repositoryService.allInstances(Customer.class);
+    }
+}
+----
+<.> Detected and managed by Spring Boot.
+<.> Lombok annotation for dependency injection of xref:refguide:applib:index/services/repository/RepositoryService.adoc[RepositoryService] through generated constructor.
+<.> uses injected `RepositoryService` to query via JDOQL.
+<.> uses injected `RepositoryService` to instantiate a not-yet-persisted domain entity ...
+<.> \... and then save into the database a new `Customer` instance.
+<.> Returns all instances (useful for prototyping, probably not for production).
+
+There is no need to annotate the actions; they are implicitly hidden because of the domain service's nature.
+
+[TIP]
+====
+JDO/Datanucleus also supports link:http://www.datanucleus.org:15080/products/accessplatform_5_2/jdo/query.html#jdoql_typed[type-safe queries].
+These can be executed through the xref:refguide:persistence:index/jdo/applib/services/JdoSupportService.adoc[JdoSupportService] domain service.
+====
+
+== Menu
+
+Menu services provide actions to be rendered on the menu.
+
+For the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)], each service's actions appear as a collection of menu items of a named menu, and this menu is on one of the three menu bars provided by the Wicket viewer.
+Although these can be organised using annotations, it's usually easier to use a xref:userguide:ROOT:ui.adoc#file-based-menus[file-based layout file] (`menubars.layout.xml`).
+
+For the xref:vro:ROOT:about.adoc[REST API (Restful Objects viewer)], all menu services are shown in the services representation.
+
+[source,java]
+----
+import lombok.RequiredArgsConstructor;
+
+@DomainService(nature = NatureOfService.VIEW)                   // <.>
+@RequiredArgsConstructor(onConstructor_ = {@Inject} )           // <.>
+public class Customers {
+
+    @Inject
+    protected final CustomerRepository customerRepository;      // <.>
+
+    @Action(semantics = SemanticsOf.SAFE)
+    public List<Customer> findByName(                           // <.>
+            @ParameterLayout(named="Name")                      // <.>
+            final String name ) {
+        return customerRepository.findByName(name);             // <.>
+    }
+
+    @Action(semantics = SemanticsOf.NON_IDEMPOTENT)
+    public Customer newCustomer(...) {
+        return customerRepository.newCustomer(...);
+    }
+
+    @Action( semantics = SemanticsOf.SAFE,
+             restrictTo = RestrictTo.PROTOTYPING )              // <.>
+    public List<Customer> listAll() {
+        return customerRepository.listAll();
+    }
+}
+----
+
+<.> Identify the class as a domain service, to render in the menu.
+
+<.> Rendered in the UI as a "Find By Name" menu item underneath the "Customers" menu.
+
+<.> The xref:refguide:applib:index/annotation/ParameterLayout.adoc[@ParameterLayout] provides metadata for the parameter itself, in this case its name.
++
+[TIP]
+====
+There's no need to name the action parameters if the code is compiled using the `-parameters` flag to the Java compiler (javac); the framework will then use the name of the parameter itself.
+====
+
+<.> the action implementation delegates to an injected repository.
+The framework can inject into not just other domain services but will also automatically into domain entities and view models.
+There is further discussion of service injection xref:userguide:ROOT:domain-services.adoc#injecting-services[below].
+
+<.> xref:refguide:applib:index/annotation/Action.adoc#restrictTo[Prototype] actions are rendered only in prototyping mode.
+A "list all" action such as this can be useful when exploring the domain with a small dataset.
+
+<.> Menu services typically delegate to an underlying repository/ies specific to the domain (rather than use `RepositoryService` directly, for example).
+
+Whether you separate out menu services from repository services is to some extent a matter of style.
+One perspective is that these two closely related domain services nevertheless have different responsibilities, and so could be kept separate.
+An alternative perspective is that the duplication is just unnecessary boilerplate, and conflicts with the naked objects philosophy.
+
+
+== Scoped services
+
+By default all domain services are application-scoped, in other words singletons.
+Such domain services are required to be thread-safe, usually satisfied by being intrinsically stateless.
+
+Sometimes though a service's lifetime is applicable only to a single (http) request.
+The framework has a number of such services, including a xref:refguide:applib:index/services/scratchpad/Scratchpad.adoc[Scratchpad] service (to share adhoc data between methods), and xref:refguide:applib:index/services/queryresultscache/QueryResultsCache.adoc[QueryResultsCache], which as its name suggests will cache query results.
+Such services _do_ hold state, but that state is scoped per (possibly concurrent) request and should be removed afterwards.
+
+The requirement for request-scoped services is supported using Apache Causeway' own xref:refguide:applib:index/annotation/InteractionScope.adoc[@InteractionScope] annotation (named because a short-lived `CausewaySession` is created for each request).
+This is used by the framework services and can also be used for user-defined services.
+
+For example:
+
+[source,java]
+----
+@Service
+@InteractionScope
+public class MyService {
+    ...
+    public void doSomething() { ... }
+}
+----
+
+Unlike application-scoped services, these request-scoped services must be injected using a slightly different idiom (borrowed from CDI), using a `javax.inject.Provider`.
+For example:
+
+[source,java]
+----
+import javax.inject.Provider;
+
+public class SomeClient {
+    ...
+    @Inject
+    Provider<MyService> myServiceProvider;  // <.>
+
+    public void someMethod() {
+        myServiceProvider.get()             // <.>
+                         .doSomething();
+}
+----
+<.> Inject using `Provider`
+<.> Obtain an instance using `Provider#get()`
+
+
+== Configuration
+
+Spring provides numerous mechanisms to configure domain services, both in terms of binding or passing in the configuration property to the service, and in terms of setting the value within some sort of configuration file.
+
+The mechanism prefered by Apache Causeway itself, and which you are free to use for your own services, is the type-safe link:https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/properties/ConfigurationProperties.html[ConfigurationProperties], whereby the configuration properties are expressed in a series of nested static classes.
+
+The xref:docs:starters:simpleapp.adoc[simpleapp starter app] includes an example:
+
+[source,java]
+----
+import org.springframework.validation.annotation.Validated;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("app.simple-module")
+@lombok.Data
+@Validated
+public static class Configuration {
+    private final Types types = new Types();
+    @lombok.Data
+    public static class Types {
+        private final Name name = new Name();
+        @lombok.Data
+        public static class Name {
+            private final Validation validation = new Validation();
+            @lombok.Data
+            public static class Validation {
+                private char[] prohibitedCharacters =
+                    "!&%$".toCharArray();
+                private String message =
+                    "Character '{character}' is not allowed";
+            }
+        }
+    }
+}
+----
+
+This configuration property can be injected, like any other component, and makes the configuration value available in a type-safe fashion:
+
+[source,java]
+----
+val prohibitedCharacters =
+  config.getTypes().getName().getValidation().getProhibitedCharacters();
+----
+
+For this configuration property service to be discovered and managed by Spring, we need to use the link:https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/properties/EnableConfigurationProperties.html[EnableConfigurationProperties] annotation.
+This normally would reside on the owning module (discussed in more detail xref:modules.adoc[later]):
+
+[source,java]
+.SimpleModule.java
+----
+import org.springframework.context.annotation.Configuration
+
+@Configuration
+// ...
+@EnableConfigurationProperties({
+        SimpleModule.Configuration.class,
+})
+public class SimpleModule /* ... */ {
+    // ...
+}
+----
+
+
+These configuration properties can then be specified using either Spring's `application.yml` or `application.properties`.
+For example:
+
+[source,yml]
+.application.yml
+----
+app:
+  simple-module:
+    types:
+      name:
+        validation:
+          message: "'{character}' is invalid."
+          prohibited-characters: "&%$"
+----
+
+Moreover, Spring is able to configure the IDE so that these configuration values can be specified using code completion.
+All that is required is this dependency:
+
+[source,xml]
+.pom.xml
+----
+<!-- IDE support  -->
+<dependency>
+    <groupId>org.springframework.boot</groupId>
+    <artifactId>spring-boot-configuration-processor</artifactId>
+    <optional>true</optional>
+</dependency>
+----
+
+== Initialization
+
+Sometimes a domain service needs to perform initialization logic before it is ready to be used.
+
+In many cases, such initialization can be performed within the constructor.
+If the initialization has dependencies, then these can be injected using standard link:https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-spring-beans-and-dependency-injection[constructor injection].
+
+Alternatively, initialization can be moved into a `@PostConstruct` link:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-postconstruct-and-predestroy-annotations[lifecycle callback].
+Shutdown is similar; the framework will call any method annotated with `javax.annotation.PreDestroy`.
+
+If a domain service needs to wait until the framework is fully initialized though, it should register for the `MetamodelEvent` that is emitted by the framework itself.
+One example is if the service is to seed some reference data:
+
+[source,java]
+.SimpleModule.java
+----
+import org.springframework.context.annotation.Configuration
+
+@Service
+@Priority(99)
+@RequiredConstructor
+public class SeedCountryRefData {
+
+    final InteractionService interactionService;
+    final TransactionService transactionService;
+    final FixtureScripts fixtureScripts;
+
+    @EventListener(MetamodelEvent.class)
+    public void init(MetamodelEvent ev) {                       // <.>
+        if (ev.isPostMetamodel()) {
+            interactionService.runAnonymous(() -> {
+                transactionService.runWithinCurrentTransactionElseCreateNew(
+                    () -> {
+                        fixtureScripts.runFixtureScript(
+                            new SeedCountryFixture(), null);    // <.>
+                });
+            });
+        }
+    }
+}
+----
+<.> subscribe to the framework-emitted `MetamodelEvent`
+<.> uses xref:refguide:testing:index/fixtures/applib/fixturescripts/FixtureScripts.adoc[] to seed the data using a fixture script.
+
+
+
+
+
+
diff --git a/antora/components/userguide/modules/ROOT/partials/drop-down-and-defaults/_intro.adoc b/antora/components/userguide/modules/ROOT/partials/drop-down-and-defaults/_intro.adoc
index b55cfe7faa..283924ed8c 100644
--- a/antora/components/userguide/modules/ROOT/partials/drop-down-and-defaults/_intro.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/drop-down-and-defaults/_intro.adoc
@@ -4,3 +4,17 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+Invoking an action whose parameters are primitives or values (int, date, string etc) is simple: the user can just type in or use a date picker.
+Invoking an action with a parameter of reference type (such as `Customer` or `Order`) requires the viewer to provide some mechanism by which the end-user can select the relevant instance.
+
+If the list of available options is fixed then the developer can provided a list a xref:refguide:applib-methods:prefixes.adoc#choices[choices...()] supporting method (for either and action parameter or when editing a property).
+These are rendered in a drop-down.
+
+If the list of available options is much larger, then the developer can use an xref:refguide:applib-methods:prefixes.adoc#autoComplete[autoComplete...()] supporting method.
+The user user enters a few characters and this is used to search for matching reference(s), again rendered in a drop-down.
+
+Similarly, when invoking an action, there may well be suitable defaults for the action arguments.
+For example, if placing an `Order` then -- even if the `Product` argument might not have a sensible default -- the quantity argument could reasonably be defaulted to 1.
+Or, the `Product` might indeed have a default, say the product previously placed by this user.
+The developer indicates this using a xref:refguide:applib-methods:prefixes.adoc#default[default...()] supporting method.
diff --git a/antora/components/userguide/modules/ROOT/partials/drop-down-and-defaults/progmodel.adoc b/antora/components/userguide/modules/ROOT/partials/drop-down-and-defaults/progmodel.adoc
index e1b8ff079a..8b5154639c 100644
--- a/antora/components/userguide/modules/ROOT/partials/drop-down-and-defaults/progmodel.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/drop-down-and-defaults/progmodel.adoc
@@ -4,3 +4,206 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+== Choices and Default
+
+For example, _choices_ for a property are specified using:
+
+[source,java]
+----
+import lombok.Getter;
+import lombok.Setter;
+
+@Property(editing = Editing.ENABLED)                    // <.>
+@Getter @Setter
+private String paymentMethod;
+
+public List<String> choicesPaymentMethod() {            // <.>
+    return Arrays.asList("Visa", "Mastercard", "Amex");
+}
+----
+<.> If required; properties are by default disabled globally.
+<.> Note the "choices" prefix and the suffix matching up with the getter.
+The method must return a collection of the same type as the property.
+
+For an action the _choices_ and a suitable _default_ method would be:
+
+[source,java]
+----
+public Order changePaymentMethod(String paymentMethod) {
+    setPaymentMethod(paymentMethod);
+    return this;
+}
+
+public List<String> choices0ChangePaymentMethod() {         // <.>
+    return Arrays.asList("Visa", "Mastercard", "Amex");
+}
+public String default0ChangePaymentMethod() {               // <.>
+    return getPaymentMethod();                              // <.>
+}
+----
+<.> "choices" prefix, N-th param, suffix matches up with the action's name. +
+Returns a collection of the same type as the parameter.
+<.> "default" prefix, N-th param, the suffix matches up with the action's name. +
+Returns object of same type as parameter.
+<.> Common idiom to return the current value of an property of the object.
+
+
+== AutoComplete
+
+The _autocomplete_ is similar to _choices_, but accepts a string parameter, to search for matching results.
+A property for example might have:
+
+[source,java]
+----
+@Property(editing = Editing.ENABLED)                        // <.>
+@Getter @Setter
+private Product product;
+
+public List<Product> autoCompleteProduct(                   // <.>
+                        @MinLength(2) String search) {      // <.>
+    return productRepository.findByReferenceOrName(search);
+}
+----
+<.> If required; properties are by default disabled globally.
+<.> "autoComplete" prefix, suffix matches property name. +
+Returns a collection of the property's type.
+<.> The `@MinLength(...)` annotation indicates the minimum number of characters that must be entered before a search is initiated.
+
+Actions are very similar:
+
+[source,java]
+----
+public Order changeProduct(Product product) {
+    setProduct(product);
+    return this;
+}
+
+public List<Product> autoComplete0Product(                  // <.>
+                        @MinLength(2) String search) {
+    return productRepository.findByReferenceOrName(search);
+}
+----
+<.> "autoComplete" prefix, N-th param, suffix matches action name. +
+Returns a collection of the parameters type.
+
+An _autoComplete_ method can be used in conjunction with a _default_ method, but it doesn't make sense to provide both an _autoComplete_ and a _choices_ method.
+
+
+
+== "Globally" defined drop-downs
+
+Very often the set of available choices depends on the data type of the property/action parameter, rather than the individual property/parameter itself.
+And similarly the algorithm to search for references again may well depend only on that reference type.
+
+In the case of _choices_, annotating a class as "bounded" (as in a "bounded" or fixed number of instances) means that a _choices_ drop-down will automatically be defined.
+For example:
+
+[source,java]
+----
+@DomainObject(
+    bounded = true
+)
+public class Product { /* ... */ }
+----
+
+For more on this, see xref:refguide:applib:index/annotation/DomainObject.adoc#bounding[@DomainObject#bounding].
+
+Or, if the data type is an enum, then a drop-down will be provided automatically.
+A payment method is a good example of this:
+
+
+[source,java]
+----
+public enum PaymentMethod {
+    VISA, MASTERCARD, AMEX;
+}
+----
+
+Something similar can be achieved for _autoComplete_.
+Here the domain object indicates a repository query to execute.
+For example:
+
+[source,java]
+----
+@DomainObject(
+    autoCompleteRepository = Customers.class,
+    autoCompleteMethod = "findByReferenceOrName"
+)
+public class Customer { /* ... */ }
+----
+
+with:
+
+[source,java]
+----
+@DomainService(nature = NatureOfService.VIEW)
+public class Customers {
+    @Action(semantics=SemanticsOf.SAFE)
+    public List<Customer> findByReferenceOrName(@MinLength(3) String refOrName) {
+        ...
+    }
+}
+----
+
+For more on this, see xref:refguide:applib:index/annotation/DomainObject.adoc#autoCompleteRepository[@DomainObject#autoCompleteRepository].
+
+[TIP]
+====
+There's no need for the nominated method to be an actual action; any method of any domain service will do, so long as it accepts a string and returns the correct list.
+====
+
+
+== Multi-select action parameters
+
+As well as scalar values, action parameters can also be collections.
+For this to be valid, a _choices_ or _autoComplete_ supporting method must be provided.
+
+For example, suppose we want to "tag" or "label" an object:
+
+[source,java]
+----
+public StoryCard tag(List<Tag> tags) {
+    getTags().addAll(tags);
+}
+
+public List<Tag> autoCompleteTag(@MinLength(1) search) {
+    return tagRepository.findByName(search);
+}
+----
+
+
+If the action has been associated with a collection, using xref:refguide:applib:index/annotation/Action.adoc#choicesFrom[@Action#choicesFrom()], then the collection can be used to provide a list of candidate values.
+
+The xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)] handles this by rendering checkboxes against the associated collection; the user can select/deselect these checkboxes and the selected items are taken as the values for the multi-select action.
+
+
+
+== Dependent choices for action parameters
+
+For action it is also possible (in a limited form) to define dependencies between parameters.
+Specifically, if one parameter is a drop-down choice, then other drop-down choices can be derived from it.
+
+A good example is a category/sub-category:
+
+[source,java]
+----
+public ToDoItem categorize(
+            Category category,
+            Subcategory subcategory) {
+    setCategory(category);
+    setSubcategory(subcategory);
+}
+
+public List<Category> choices0Categorize() {
+    return categoryRepository.allCategories();
+}
+public List<Subcategory> choices1Categorize(        // <.>
+                                Category category) {
+    return subcategoryRepository.findBy(category);
+}
+----
+<.> Returns a list of choices for the 2nd parameter based on the argument provided for the first.
+
+p
+
diff --git a/antora/components/userguide/modules/ROOT/partials/events/_intro.adoc b/antora/components/userguide/modules/ROOT/partials/events/_intro.adoc
index b55cfe7faa..ce67044e92 100644
--- a/antora/components/userguide/modules/ROOT/partials/events/_intro.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/events/_intro.adoc
@@ -4,3 +4,13 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+When the framework renders a domain object, and as the end-user interacts with the domain object, the framework it emits multiple events using the intra-process xref:refguide:applib:index/services/eventbus/EventBusService.adoc[event bus].
+These events enable other domain services (possibly in other modules) to influence how the domain object is rendered, or to perform side-effects or even veto an action invocation.
+
+The framework has several categories of events: domain events, UI events and lifecycle events.
+Learn more about these in xref:events.adoc[events] page.
+
+To receive the events, the domain service should subscribe to the xref:refguide:applib:index/services/eventbus/EventBusService.adoc[EventBusService], and implement an appropriately annotated method to receive the events.
+
+
diff --git a/antora/components/userguide/modules/ROOT/partials/events/progmodel.adoc b/antora/components/userguide/modules/ROOT/partials/events/progmodel.adoc
index e1b8ff079a..e422e0beb2 100644
--- a/antora/components/userguide/modules/ROOT/partials/events/progmodel.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/events/progmodel.adoc
@@ -4,3 +4,134 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+[#domain-events]
+== Domain Events
+
+Domain events are fired -- through the internal xref:refguide:applib:index/services/eventbus/EventBusService.adoc[event bus] -- for every user interaction with each object member (property, collection or action).
+
+By default, rendering a property causes a `PropertyDomainEvent` to be fired, though the xref:refguide:applib:index/annotation/Property.adoc#domainEvent[@Property#domainEvent()] attribute allows a custom subclass to be specified if necessary.
+Similarly, rendering a collection causes a `DomainEvent` to be fired, and rendering an action causes an `ActionDomainEvent` to be fired.
+
+In fact, each event can be fired up to five times, with the event's `getEventPhase()` method indicating to the subscriber the phase:
+
+* *hide* phase allows the subscriber to hide the member
+
+* *disable* phase allows the subscriber to disable the member.
++
+For a property this makes it read-only; for an action this makes it "greyed out".
+(Collections are implicitly read-only).
+
+* *validate* phase allows the subscriber to validate the proposed change.
++
+For a property this means validating the proposed new value of the property; for an action this means validating the action parameter arguments.
+For example, a referential integrity restrict could be implemented here.
+
+* *executing* phase is prior to the actual property edit/action invocation, allowing the subscriber to perform side-effects.
++
+For example, a cascade delete could be implemented here.
+
+* *executed* phase is after the actual property edit/action invocation.
++
+For example, a business audit event could be implemented here.
+
+For more details on the actual domain event classes, see the xref:refguide:applib-classes:events.adoc#domain-event-classes[domain event] section of the relevant reference guide.
+
+== UI Events
+
+As explained xref:userguide:ROOT:overview.adoc#title-and-icon-and-css-class[earlier], to allow the end-user to distinguish one domain object from another, it is rendered with a title and an icon.
+
+xref:userguide:ROOT:ui.adoc#object-titles-and-icons[Normally] the code to return title and icon of an object is part of the domain object's implementation.
+However, UI events allow this title and icon to be provided instead by a subscriber.
+UI events have higher precedence than the other mechanisms of supplying a title.
+
+If annotated with xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#titleUiEvent[@DomainObjectLayout#titleUiEvent()], the appropriate (subclass of) `TitleUiEvent` will be emitted.
+Similarly for xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#iconUiEvent[#iconUiEvent()].
+In addition, it is possible to use events to obtain a CSS class to render with the domain object, using xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#cssClassUiEvent[#cssClassUiEvent()], and to select an alternate xref:userguide:ROOT:ui.adoc#object-layout[layout file] using xref:refguide:applib:index/annotation/DomainObjectLayout.adoc#layoutUiEvent[#layoutUiEvent()].
+
+There are two use cases where this feature is useful:
+
+* the first is to override the title/icon/CSS class/layout of 3rd party library code, for example as provided by the xref:security:secman:about.adoc[SecMan] extension.
+
+* the second is for xref:userguide:ROOT:view-models.adoc#jaxb[JAXB-style view models] which are code generated from XSDs and so cannot have any dependencies on the rest of the Apache Causeway framework.
+
+In this second case a subscriber on the default events can provide a title and icon for such an object, with the behaviour provided using xref:userguide:ROOT:overview.adoc#mixins[mixins].
+
+[#lifecycle-events]
+== Lifecycle Events
+
+Lifecycle events allow domain object subscribers to listen for changes to the persistence state of domain entities, and act accordingly.
+
+[WARNING]
+====
+Lifecycle events are _not_ fired for view models.
+====
+
+The lifecycle events supported are:
+
+* *created*
++
+Entity has just been instantiated.
+Note that this requires that the object is instantiated using the framework, see xref:userguide:ROOT:domain-services.adoc#instantiating[here] for further discussion.
+
+* *loaded*
++
+Entity has just retrieved/rehydrated from the database
+
+* *persisting*
++
+Entity is about to be inserted/saved (ie for the first time) into the database
+
+* *persisted*
++
+Entity has just been inserted/saved (ie for the first time) into the database
+
+* *updating*
++
+The (already persistent) entity about to be flushed in the database
+
+* *updated*
++
+The (already persistent) entity has just been flushed to the database
+
+* *removing*
++
+The (already persistent) entity is about to be deleted from the database
+
+For example, if annotated with xref:refguide:applib:index/annotation/DomainObject.adoc#updatingLifecycleEvent[@DomainObjectLayout#updatingLifecycleEvent], the appropriate (subclass of) `ObjectUpdatingEvent` will be emitted.
+
+There is no lifecycle event for "entity creating" because (obviously) the framework doesn't know about newly created objects until they have been created.
+Similarly, there is no lifecycle event for entities that have been removed because it is not valid to "touch" a domain entity once deleted.
+
+
+
+== Event Subscribers
+
+Domain services acting as xref:events.adoc[event] subscribers can subscribe to xref:userguide:ROOT:events.adoc#lifecycle-events[lifecycle] events, influencing the rendering and behaviour of other objects.
+Behind the scenes this uses Spring's (in-memory) link:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#context-functionality-events-annotation[event bus].
+
+[source,java]
+----
+import org.springframework.stereotype.Service;
+import org.springframework.context.event.EventListener;
+import lombok.RequiredArgsConstructor;
+
+@Service                                                        // <.>
+@lombok.RequiredArgsConstructor(onConstructor_ = {@Inject} )
+public class OnCustomerDeletedCascadeDeleteOrders {
+
+    private final OrderRepository orderRepository;
+
+    @EventListener(Customer.DeletedEvent.class)                 // <.>
+    public void on(final Customer.DeletedEvent ev) {            // <.>
+        Customer customer = ev.getSource();
+        orderRepository.delete(customer);
+    }
+}
+----
+<.> subscriptions do not appear in the UI at all
+
+<.> use Spring Framework's link:https://javadoc.io/doc/org.springframework/spring-context/latest/org/springframework/context/event/EventListener.html[@EventListener]
+
+<.> the parameter type of the method corresponds to the event emitted on the event bus.
+The actual method name does not matter (though it must have `public` visibility).
diff --git a/antora/components/userguide/modules/ROOT/partials/meta-annotations/_intro.adoc b/antora/components/userguide/modules/ROOT/partials/meta-annotations/_intro.adoc
index b55cfe7faa..08fffaf834 100644
--- a/antora/components/userguide/modules/ROOT/partials/meta-annotations/_intro.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/meta-annotations/_intro.adoc
@@ -4,3 +4,9 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+
+Apache Causeway supports the use of meta-annotations, as does Spring Boot and the ORM (xref:pjpa:ROOT:about.adoc[JPA/EclipseLink] from Eclipse 2.7 supporting JPA 2.2, xref:pjdo:ROOT:about.adoc[JDO/DataNucleus] for DN 6.x).
+
+This allows you to define custom annotations that are more descriptive of your domain model, and meta-annotate your custom annotations with those of Apache Causeway, Spring or the ORM.
+In your domain model, you can just use the custom annotation.
diff --git a/antora/components/userguide/modules/ROOT/partials/meta-annotations/progmodel.adoc b/antora/components/userguide/modules/ROOT/partials/meta-annotations/progmodel.adoc
index e1b8ff079a..cd743a9175 100644
--- a/antora/components/userguide/modules/ROOT/partials/meta-annotations/progmodel.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/meta-annotations/progmodel.adoc
@@ -4,3 +4,105 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+The most common use case is to define meta-annotations for scalar types that are used in properties or parameters.
+
+The xref:docs:starters:simpleapp.adoc[SimpleApp] starter app illustrates the usage with its `@Name` meta-annotation:
+
+[source,java]
+.Name.java
+----
+@Column(length = Name.MAX_LEN, allowsNull = "false")                // <.>
+@Property(mustSatisfy = Name.Specification.class,                   // <.>
+          maxLength = Name.MAX_LEN)
+@Parameter(mustSatisfy = Name.Specification.class,                  // <.>
+           maxLength = Name.MAX_LEN)
+@ParameterLayout(named = "Name")                                    // <.>
+@Target({ ElementType.METHOD, ElementType.FIELD,                    // <.>
+          ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)                                 // <.>
+public @interface Name {
+
+    int MAX_LEN = 40;
+
+    // ...
+
+    class Specification extends AbstractSpecification2<String> {
+        // ...
+    }
+}
+----
+<.> ORM annotation
+<.> Annotations that are inferred if annotated on a property. +
+The xref:refguide:applib:index/annotation/Property.adoc#mustSatisfy[@Property#mustSatisfy()] allows arbitrary validation rules to be specified for the property.
+
+<.> Annotations that are inferred if annotated on a parameter +
+The xref:refguide:applib:index/annotation/Parameter.adoc#mustSatisfy[@Parameter#mustSatisfy()] allows the same validation rules to be specified for the parameter.
+
+<.> Layout annotations that are inferred if annotated on a parameter
+
+<.> The locations that the meta annotation can be applied
+
+<.> Required boilerplate to ensure that the meta annotation is part of the bytecode introspected at runtime
+
+This annotation is used in the `SimpleObject` entity, for the "name" property and the "name" parameter of the "updateName()" action:
+
+[source,java]
+.SimpleObject.java
+----
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.ToString.Include;
+
+@ToString(onlyExplicitlyIncluded = true)
+public class SimpleObject implements Comparable<SimpleObject> {
+
+    // ...
+
+    @Name
+    @Getter @Setter @ToString.Include
+    private String name;
+
+    // ...
+    public SimpleObject updateName(
+            @Name final String name) {
+        setName(name);
+        return this;
+    }
+    // ...
+}
+----
+
+
+== Aligning Semantics
+
+Since the ORM handles persistence, its annotations can only be specified for properties, not for action parameters.
+Using meta-annotations is a good way to unify the annotations that are required for properties vs parameters.
+
+The table below summarises the equivalence of some of the most common cases.
+
+.Comparing annotations of Causeway properties/parameters vs ORM
+[cols="2a,3m,5m",options="header"]
+|===
+|value type/semantic
+|Causeway-recognized annotation
+|ORM annotation
+
+|string (length)
+|@Property(maxLength=50) +
+@Parameter(maxLength=50)
+|
+@javax.jdo.annotations.Column(length=50)
+
+|big decimal (precision)
+|@javax.validation.constraints.Digits(fraction=2)
+|@javax.persistence.Column(scale=2)
+@javax.jdo.annotations.Column(scale=2)
+
+|optionality
+|`@javax.annotation.Nullable` or `ParameterLayout(optionality=OPTIONAL`)
+|`@javax.persistence.Column(nullable=true)`
+`@javax.jdo.annotations.Column(allowsNull="true")`
+|===
+
diff --git a/antora/components/userguide/modules/ROOT/partials/mixins/_intro.adoc b/antora/components/userguide/modules/ROOT/partials/mixins/_intro.adoc
index b55cfe7faa..f575592018 100644
--- a/antora/components/userguide/modules/ROOT/partials/mixins/_intro.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/mixins/_intro.adoc
@@ -4,3 +4,46 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+The next type of domain object is the *mixin*.
+These are similar to traits or extension methods in other programming languages.
+A mixin object allows one class to contribute behaviour - actions, (derived) properties and (derived) collections - to another domain object, either a domain entity or view model.
+Or rather, the mixin _appears_ to contribute the behaviour/state in the UI; the underlying domain class being "decorated" does not know this is happening.
+
+This is therefore a key technique to allow the app to stay decoupled, so that it doesn't degrade into the proverbial link:http://www.laputan.org/mud/mud.html#BigBallOfMud["big ball of mud"].
+There's a lot more discussion on this topic in xref:overview.adoc#modules[modules], below.
+
+
+Mixins are also a convenient mechanism for grouping functionality even for a concrete type, helping to rationalize about the dependency between the data and the behaviour.
+Each mixin is in effect a single behavioural "responsibility" of the domain object.
+
+In fact, we find mixins nicely balance inside-out vs outside-in ways of thinking about a system:
+
+* inside-out tends to focus on the structure, the nouns that make up the domain.
+
+* outside-in tends to focus on the behaviour, that is the functionality that the system provides to automate the business processes; the verbs, in other words.
+
+So, while Apache Causeway allows you to put behaviour onto the underlying domain entities, it can often be better to treat the domain entities as immutable.
+Instead, use mixins to implement behaviour.
+When using an agile development methodology, it's common for a user story to correspond to a new mixin.
+
+There are also practical reasons for moving behaviour out of entities even within the same module, because structuring your application this way helps support hot-reloading of Java classes (so that you can modify and recompile your application without having to restart it).
+This can provide substantial productivity gains.
+
+The Hotspot JVM has limited support for hot reloading; generally you can change method implementations but you cannot introduce new methods.
+However, the link:https://dcevm.github.io/[DCEVM] open source project will patch the JVM to support much more complete hot reloading support.
+There are also commercial products such as JRebel.
+
+[TIP]
+====
+Details on how to actually write a mixin (the programming model for mixins) is
+xref:userguide:ROOT:mixins.adoc[here].
+====
+
+.DCI Architecture
+****
+Mixins are an implementation of the link:http://www.artima.com/articles/dci_vision.html[DCI architecture] architecture, as formulated and described by link:https://en.wikipedia.org/wiki/Trygve_Reenskaug[Trygve Reenskaug] and link:https://en.wikipedia.org/wiki/Jim_Coplien[Jim Coplien].
+Reenskaug was the inventor of the MVC pattern (and also the external examiner for Richard Pawson's link:../../../../../ug/fun/_attachments/core-concepts/Pawson-Naked-Objects-thesis.pdf[PhD thesis]), while Coplien has a long history in object-orientation, C++ and patterns.
+
+DCI stands for Data-Context-Interaction and is presented as an evolution of object-oriented programming, but one where behaviour is bound to objects dynamically rather than statically in some context or other.
+The mixin pattern is Apache Causeway' straightforward take on the same basic concept.
+****
diff --git a/antora/components/userguide/modules/ROOT/partials/mixins/progmodel.adoc b/antora/components/userguide/modules/ROOT/partials/mixins/progmodel.adoc
index e1b8ff079a..cda6bfdcba 100644
--- a/antora/components/userguide/modules/ROOT/partials/mixins/progmodel.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/mixins/progmodel.adoc
@@ -4,3 +4,189 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+A mixin acts like a trait or extension method, allowing one module to contribute behaviour or derived state to another object.
+This is a *very powerful feature*, but at the same time is  easy to use.
+
+Under the covers, all mixins are basically actions that use the mixee as one of their arguments.
+
+* if the mixin action takes some arguments (apart from the mixee), then it will only ever be rendered as an action
+* if the mixin action is not query-only, then again it will only ever be rendered as an action
+* if the mixin action is query-only and takes no arguments, then it can either be rendered as an action or as some derived state
+** if as an action, then it's basically a query to find and return some related info (eg "most recent order", "last 5 orders")
+** if as derived state, then it will be rendered as a property (if it returns a scalar), or as a collection (if it returns a vector).
+
+
+Accordingly, a mixin can be defined using the xref:refguide:applib:index/annotation/Action.adoc[@Action], xref:refguide:applib:index/annotation/Property.adoc[@Property] or xref:refguide:applib:index/annotation/Collection.adoc[@Collection] annotations, but defined at the domain class level rather than at the method level.
+
+The mixin is expected to follow the naming convention `SomeType_mixinName`, with the name of the contributed member being contributed is inferred from the name of the class itself, being everything after the last '_'.
+For example, if the mixin is called `DocumentHolder_documents`, then the mixee is `DocumentHolder` interface, and `documents` is the name of the contributed collection.
+
+This example also hints at why mixins are so powerful: the mixee's type is usually an interface, not a concrete class.
+Thus, any class that implements `DocumentHolder`, eg `Customer` or `Order`, will be contributed to.
+
+== Contributed Collection
+
+The example below shows in more detail how to contribute a collection, using xref:refguide:applib:index/annotation/Collection.adoc[@Collection].
+The method is expected to be called "coll":
+
+[source,java]
+----
+@Collection                                     // <.>
+@RequiredArgsConstructor                        // <.>
+public class DocumentHolder_documents {
+
+    private final DocumentHolder holder;
+
+    public List<Document> coll() { /* ... */ }  // <.>
+        ...
+    }
+    public boolean hideColl() { /* ... */ }     // <.>
+}
+----
+<.> indicates this is a collection mixin (that contributes its method as an association) with method name 'coll'
+<.> mixee is injected into the mixin
+<.> method (which must be called "coll") must accept no arguments, be query-only, and return a collection
+<.> supporting methods (discussed in xref:business-rules.adoc[business rules]) follow the usual naming conventions
+(Because the collection is derived/read-only, the only supporting method that is relevant is `hideColl()`).
+
+The above will result in a contributed collection "documents" for all types that implement/extend from `DocumentHolder`.
+
+
+
+== Contributed Property
+
+Contributed properties can likewise be defined using xref:refguide:applib:index/annotation/Property.adoc[@Property]; this implies a method called "prop":
+
+[source,java]
+----
+@Property                                                       // <.>
+@RequiredArgsConstructor                                        // <.>
+public class DocumentHolder_mostRecentDocument {
+
+    private final DocumentHolder holder;
+
+    public Document prop() {                                    // <.>
+        ...
+    }
+    public boolean hiderProp() { /* ... */ }                    // <.>
+}
+----
+<.> indicates this is a property mixin (that contributes its method as an association) with method name 'prop'
+<.> mixee is injected into the mixin
+<.> method (which must be called "prop") must accept no arguments, be query-only, and return a scalar value
+<.> supporting methods (discussed in xref:business-rules.adoc[business rules]) follow the usual naming conventions
+(Because the property is derived/read-only, the only supporting method that is relevant is `hideProp()`).
+
+This contributes a property called "mostRecentDocument".
+
+
+[#contributed-action]
+== Contributed Action
+
+Contributed actions are defined similarly, using xref:refguide:applib:index/annotation/Action.adoc[@Action]; this implies a method called "act".
+For example:
+
+[source,java]
+----
+@Action                                                         // <.>
+@RequiredArgsConstructor                                        // <.>
+public class DocumentHolder_addDocument {
+
+    private final DocumentHolder holder;
+
+    public Document> act(Document doc) {                        // <.>
+        ...
+    }
+    public boolean hideAct() { /* ... */ }                      // <.>
+}
+----
+<.> indicates this is an action mixin (that contributes its method as an action) with method name 'act'
+<.> mixee is injected into the mixin
+<.> method must be called "act"
+Unlike contributed properties and collections, contributed actions can accept parameters, and have any semantics.
+<.> xref:business-rules.adoc[supporting methods] follow the usual naming conventions
+
+This contributes an action called "addDocument".
+
+
+[[mixins-as-nested-classes]]
+== As Nested Classes
+
+While mixins primary use case is as a means of allowing contributions from one module to the types of another module, they are also a convenient mechanism for grouping functionality/behaviour against a concrete type.
+All the methods and xref:business-rules.adoc[supporting methods] end up in a single construct, and the dependency between that functionality and the rest of the object is made more explicit.
+
+We might therefore want to use a mixin within the same module as the mixee; indeed even within the same package or class as the mixee.
+In other words, we could define the mixin as nested static class of the mixee it contributes to.
+
+In the previous examples the "_" is used as a separator between the mixin type and mixin name.
+However, to support mixins as nested classes, the character "$" is also recognized as a separator.
+
+For example, the following refactors the "updateName" action -- of the `SimpleObject` class in xref:docs:starters:simpleapp.adoc[SimpleApp] start app -- into a nested mixin:
+
+[source,java]
+----
+public class SimpleObject /* ... */ {
+
+    // ...
+
+    @Action(semantics = IDEMPOTENT,
+            commandPublishing = Publishing.ENABLED,
+            executionPublishing = Publishing.ENABLED,
+            associateWith = "name",
+            domainEvent = updateName.DomainEvent.class)                 // <2>
+    public class updateName {                                           // <.>
+
+        public class DomainEvent extends
+            SimpleModule.ActionDomainEvent<SimpleObject.updateName> {}  // <.>
+
+        public SimpleObject act(@Name final String name) {
+            setName(name);                                              // <.>
+            return SimpleObject.this;
+        }
+        public String default0Act() {                                   // <.>
+            return getName();                                           // <.>
+        }
+    }
+    // ...
+}
+----
+<.> Mixin class is not `static`, so that the containing object is implicitly available.
++
+Its name can be either "camelCase" or "PascalCase", either will work.
+
+<.> Domain event can be declared within the mixin, again, not `static`.
++
+Note that it is genericised on the mixin, not on the mixee
+
+<.> Acts on the owning instance.
+<.> xref:business-rules.adoc[Supporting methods] follow the same naming convention.
+<.> Acts on the owning instance.
+
+
+== Programmatic usage
+
+When a domain object is rendered, the framework will automatically instantiate all required mixins and delegate to them dynamically.
+If writing integration tests or fixtures, or (sometimes) just regular domain logic, then you may need to instantiate mixins directly.
+
+For this you can use the xref:refguide:applib:index/services/factory/FactoryService.adoc[FactoryService#mixin(...)] method.
+
+For example:
+
+[source,java]
+----
+DocumentHolder_documents mixin =
+    factoryService.mixin(DocumentHolder_documents.class, customer);
+----
+
+Alternatively, you can use xref:refguide:applib:index/services/inject/ServiceInjector.adoc[ServiceInjector] to inject domain services after the mixin has been instantiated.
+You'll need to use this method if using nested non-`static` mixins:
+
+
+[source,java]
+----
+SimpleObject.updateName mixin =
+    serviceInjector.injectServicesInto( simpleObject.new updateName() );
+----
+
+
diff --git a/antora/components/userguide/modules/ROOT/partials/module-nav.adoc b/antora/components/userguide/modules/ROOT/partials/module-nav.adoc
index 814afed919..fe52a54291 100644
--- a/antora/components/userguide/modules/ROOT/partials/module-nav.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/module-nav.adoc
@@ -8,8 +8,9 @@
 * xref:userguide::view-models.adoc[View Models]
 * xref:userguide::mixins.adoc[Mixins]
 * xref:userguide::value-types.adoc[Value types]
-* xref:userguide::modules.adoc[Modules]
 * xref:userguide::meta-annotations.adoc[Meta-annotations]
+* xref:userguide::modules.adoc[Modules]
+* xref:userguide::events.adoc[Events]
 * xref:userguide::background-context-and-theory.adoc[Background, Context & Theory]
 
 
diff --git a/antora/components/userguide/modules/ROOT/partials/view-models/_intro.adoc b/antora/components/userguide/modules/ROOT/partials/view-models/_intro.adoc
index b55cfe7faa..5c2e298825 100644
--- a/antora/components/userguide/modules/ROOT/partials/view-models/_intro.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/view-models/_intro.adoc
@@ -4,3 +4,139 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+View models are similar to entities in that (unlike domain services) there can be many instances of any given type.
+End users interact with view models in the same way as a domain entity, indeed they are unlikely to distinguish one from the other.
+
+However, whereas domain entities are mapped to a datastore, view models are not.
+Instead they are recreated dynamically by serializing their state, ultimately into the URL itself (meaning their state it is in effect implicitly managed by the client browser).
+You will notice that the URL for view models (as shown in xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)] or xref:vro:ROOT:about.adoc[RestfulObjects viewer]) tends to be quite long.
+
+This capability opens up a number of more advanced use cases:
+
+* In the same way that an (RDBMS) database view can aggregate and abstract from multiple underlying database tables, a view model sits on top of one or many underlying entities.
+
+* A view model could also be used as a proxy for some externally managed entity, accessed over a web service or REST API; it could even be a representation of state held in-memory (such as user preferences, for example).
+
+* view models can also be used to support a particular use case.
+An example that comes to mind is to expose a list of scanned PDFs to be processed as an "intray", showing the list of PDFs on one side of the page, and the current PDF being viewed on the other.
+Such view models are part of the application layer, not part of the domain layer (where entities live).
+
+We explore these use cases in more detail below.
+
+[TIP]
+====
+Details on how to actually write a view model (the programming model for view models) can be found xref:userguide:ROOT:view-models.adoc[here].
+====
+
+== Externally-managed entities
+
+Sometimes the entities that make up your application are persisted not in the local database but reside in some other system, for example accessible only through a SOAP web service.
+Logically that data might still be considered a domain entity and we might want to associate behaviour with it, however it cannot be modelled as a domain entity if only because JDO/DataNucleus doesn't know about the entity nor how to retrieve or update it.
+
+There are a couple of ways around this: we could either replicate the data somehow from the external system into the Causeway-managed database (in which case it is once again just another domain entity), or we could set up a stub/proxy for the externally managed entity.
+This proxy would hold the reference to the externally-managed domain entity (eg an external id), as well as the "smarts" to know how to interact with that entity (by making SOAP web service calls etc).
+
+The stub/proxy is a type of view model: a view -- if you like -- onto the domain entity managed by the external system.
+
+== In-memory entities
+
+As a variation on the above, sometimes there are domain objects that are, conceptually at least entities, but whose state is not actually persisted anywhere, merely held in-memory (eg in a hash).
+
+A simple example is read-only configuration data that is read from a config file (eg log4j appender definitions) but thereafter is presented in the UI just like any other entity.
+
+== Application-layer view models
+
+Domain entities (whether locally persisted or managed externally) are the bread-and-butter of Apache Causeway applications: the focus after all, should be on the business domain concepts and ensuring that they are solid.
+Generally those domain entities will make sense to the business domain experts: they form the _ubiquitous language_ of the domain.
+These domain entities are part of the domain layer.
+
+When developing an Apache Causeway application you will most likely start off with the persistent domain entities: `Customer`, `Order`, `Product`, and so on.
+For some applications this may well suffice.
+
+That said, it may not always be practical to expect end-users of the application to interact solely with those domain entities.
+If the application needs to integrate with other systems, or if the application needs to support reasonably complex business processes, then you may need to look beyond just domain entities; view models are the tool of choice.
+
+One such use case for view models is to help co-ordinate complex business processes; for example to perform a quarterly invoicing run, or to upload annual interest rates from an Excel spreadsheet, or prepare payment batches from incoming invoices, to be uploaded to an external payment system.
+In these cases the view model managing the business process might have some state of its own, but in most cases that state does not need to be persisted between user sessions.
+Many of the actions will be queries but in some cases such view model actions might also modify state of underlying domain entities.
+Either way, ultimately these actions just delegate down to the domain-layer.
+
+.Desire Lines
+****
+One way to think of application view models is that they model the "desire line": the commonly-trod path that end-users must follow to get from point A to point B as quickly as possible.
+
+To explain: there are link:http://ask.metafilter.com/62599/Where-the-sidewalk-ends[documented] link:https://sivers.org/walkways[examples] link:http://www.softpanorama.org/People/Wall/larry_wall_articles_and_interviews.shtml[that] architects of university campus will only add in paths some while after the campus buildings are complete: let the pedestrians figure out the routes they want to take.
+One name for this idea is "desire lines".
+
+What that means is you should add view models _after_ having built up the domain layer, rather than before.
+These view models pave that commonly-trod path, automating the steps that the end-user would otherwise have to do by hand.
+
+However, you _shouldn't_ try to build out a domain layer that could support every conceivable use case before starting to think about view models.
+Instead, iterate.
+Identify the use case/story/end-user objective that will deliver value to the business.
+Build out the minimum domain entities to support that use case.
+Then, introduce view models to simplify high-volume end-user interactions with the system (perhaps automating several related use cases together).
+****
+
+Another common requirement is to show a dashboard of the most significant data in the system to a user, often pulling in and aggregating information from multiple points of the app.
+Obtaining this information by hand (by querying the respective services/repositories) would be tedious and slow; far better to have a dashboard do the job for the end user.
+
+A dashboard object is a model of the most relevant state to the end-user, in other words it is (quite literally) a view model.
+It is not a persisted entity, instead it belongs to the application layer.
+
+== DTOs
+
+DTOs (data transfer objects) are simple classes that (according to link:https://en.wikipedia.org/wiki/Data_transfer_object[wikipedia]) "carry data between processes".
+
+If those two processes are parts of the same overall application (the same team builds and deploys both server and client) then there's generally no need to define a DTO; just access the entities using Apache Causeway' xref:vro:ROOT:about.adoc[RestfulObjects viewer].
+
+On the other hand, if the client consuming the DTO is a different application -- by which we mean developed/deployed by a different (possible third-party) team -- then the DTOs act as a formal contract between the provider and the consumer.
+In such cases, exposing domain entities over xref:vro:ROOT:about.adoc[RestfulObjects] would be "A Bad Thing"(TM) because the consumer would in effect have access to implementation details that could then not be easily changed by the producer.
+There's plenty of discussion on this topic (eg link:https://stackoverflow.com/questions/36174516/rest-api-dtos-or-not[here] and link:https://juristr.com/blog/2012/10/lessions-learned-dont-expose-ef-entities-to-the-client-directly/[here]).
+Almost all of these recommend exposing only DTOs (which is to say view models), not domain entities, in REST APIs.
+
+To support this use case, a view model can be defined such that it can act as a DTO.
+This is done by annotating the class using JAXB annotations; this allows the consumer to obtain the DTO in XML format along with a corresponding XSD schema describing the structure of that XML.
+
+
+These DTOs are still usable as "regular" view models; they will render in the xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)] just like any other.
+In fact (as the xref:userguide:ROOT:view-models.adoc#jaxb[programming model] section below makes clear), these JAXB-annotated view models are in many regards the most powerful of all the alternative ways of writing view models.
+
+It's also worth noting that it is also possible to download the XML (or XSD) straight from the UI, useful during development.
+The view model simply needs to implement the xref:refguide:applib:index/mixins/dto/Dto.adoc[Dto] marker interface; the framework has xref:refguide:applib-classes:mixees-and-mixins.adoc#Dto[mixins] that contribute the download actions to the view model.
+
+== For REST Clients
+
+The xref:vro:ROOT:about.adoc[Restful Objects] viewer automatically provides a REST API for both domain entities.
+Or, you can use it to only expose view models, taking care to map the state of the domain entity/ies into a view model.
+The question to consider is whether the REST API is a public API or an internal private API:
+
+* If it's a public API, which is to say that there are third-party clients out over which you have no control, then view models are the way to go.
++
+In this case view models provide an isolation layer which allow you to modify the structure of the underlying domain entities without breaking this API.
+
+* If it's a private API, which is to say that the only clients of the REST API are under your control, then view models are an unnecessary overhead.
++
+In this case, just expose domain entities directly.
+
+The caveat to the "private API" option is that private APIs have a habit of becoming public APIs.
+Even if the REST API is only exposed within your organisation's intranet, other teams may "discover" your REST API and start writing applications that consume it.
+If that REST API is exposing domain entities, you could easily break those other teams' clients if you refactor.
+
+[NOTE]
+====
+The link:https://projects.spring.io/spring-data-rest/[Spring Data REST] subproject has a similar capability of being able to expose domain entities as REST resources.
+This link:https://stackoverflow.com/questions/38874746/is-it-problematic-that-spring-data-rest-exposes-entities-via-rest-resources-with[SO question], which debates the pros-and-cons, is also worth a read.
+====
+
+If your REST API is intended to be public (or you can't be sure that it will remain private), then exposing view models will entail a lot of marshalling of state from domain entities into view models.
+There are numerous open source tools that can help with that, for example link:http://modelmapper.org/[Model Mapper], link:http://dozer.sourceforge.net/[Dozer] and link:https://github.com/orika-mapper/orika[Orika].
+
+Or, rather than marshalling state, the view model could hold a reference to the underlying domain entity/ies and dynamically read from it (ie, all the view model's properties are derived from the entity's).
+
+A third option is to define an RDBMS view, and then map a "non-durable" entity to that view.
+The RDBMS view then becomes the public API that must be preserved.
+ORMs such as DataNucleus link:http://www.datanucleus.org:15080/products/accessplatform_5_1/jdo/mapping.html#schema_rdbms_views[support this].
+
+
diff --git a/antora/components/userguide/modules/ROOT/partials/view-models/progmodel.adoc b/antora/components/userguide/modules/ROOT/partials/view-models/progmodel.adoc
index e1b8ff079a..364bd0db48 100644
--- a/antora/components/userguide/modules/ROOT/partials/view-models/progmodel.adoc
+++ b/antora/components/userguide/modules/ROOT/partials/view-models/progmodel.adoc
@@ -4,3 +4,317 @@
 :Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
 :page-partial:
 
+
+View models are generally considered to reside in the application layer, and -- unlike xref:domain-entities.adoc[domain entities] -- their state is _not_ persisted to a database.
+Instead, it is serialized into its identifier (in effect, its URL).
+The framework unpacks this URL to infer/recreate the view model's state with each interaction.
+
+
+The framework provides three ways to implement a view model:
+
+* Annotating the class using JAXB annotations; this allows the state of the object's properties and also its collections.
++
+The serialized form of these view models is therefore XML, which also enables these view models to act as DTO (useful for various integration scenarios).
+
+* Using Apache Causeway specific annotations.
++
+This is more concise, but less powerful: only the state of the object's properties is serialized -- collections are ignored -- and not every datatype is recognized.
++
+On the other hand, they are more likely to perform better.
+
+* Implementing the xref:refguide:applib:index/ViewModel.adoc[ViewModel] interface.
++
+With this option you take full control of the marshalling and unmarshalling of the object's state to/from a string.
+
+
+These options are discussed in more detail in the sections below.
+
+[[jaxb]]
+== JAXB View Models
+
+Here's a typical example of a JAXB view model, to allow (certain properties of) two ``Customer``s to be compared:
+
+[source,java]
+.CompareCustomers.java, using JAXB
+----
+@XmlRootElement(name = "compareCustomers")          // <.>
+@XmlType(
+        propOrder = {                               // <.>
+            "customer1",
+            "customer2"
+        }
+)
+@XmlAccessorType(XmlAccessType.FIELD)               // <.>
+public class CompareCustomers {
+
+    @XmlElement(required = true)                    // <.>
+    @Getter @Setter
+    Customer customer1;
+
+    @XmlElement(required = true)                    // <.>
+    @Getter @Setter
+    Customer customer2;
+
+    @XmlTransient                                   // <.>
+    public String getCustomer1Name() {
+        return getCustomer1().getName();
+    }
+
+    @XmlTransient                                   // <.>
+    public String getCustomer2Name() {
+        return getCustomer2().getName();
+    }
+
+    ...
+}
+----
+
+<.> The JAXB xref:refguide:applib-ant:XmlRootElement.adoc[@XmlRootElement] annotation indicates this is a view model to Apache Causeway, which then uses JAXB to serialize the state of the view model between interactions
+<.> Optionally, the properties of the view model can be listed using the `XmlType#propOrder` attribute. +
+This is an all-or-nothing affair: either all properties must be listed, or else the annotation omitted.
+<.> Specifying field accessor type allows the Lombok `@Getter` and `@Setter` annotations to be used.
+<.> The `@XmlElement` indicates the property is part of the view model's state.
+For collections, the `@XmlElementWrapper` would also typically be used.
+<.> The `@XmlTransient` indicates that the property is derived and should be ignored by JAXB. +
+The derived properties could also have been implemented using xref:userguide:ROOT:overview.adoc#mixins[mixins].
+
+[IMPORTANT]
+====
+Be aware that all the state will ultimately converted into a URL-safe form (by way of the xref:refguide:applib:index/services/urlencoding/UrlEncodingService.adoc[UrlEncodingService]).
+
+There are limits to the lengths of URLs, however.
+If the URL does exceed limits or contains invalid characters, then provide a custom implementation of xref:refguide:applib:index/services/urlencoding/UrlEncodingService.adoc[UrlEncodingService] to handle the memento string in some other fashion (eg substituting it with a GUID, with the memento cached somehow on the server).
+====
+
+
+
+[#referencing-domain-entities]
+=== Referencing Domain Entities
+
+It's quite common for view models to be "backed by" (be projections of) some underlying domain entity.
+For example, the `CompareCustomers` view model described xref:userguide:ROOT:view-models.adoc#jaxb[above] actually references two underlying ``Customer`` entities.
+
+It wouldn't make sense to serialize out the state of a persistent entity.
+However, the identity of the underlying entity is well defined; Apache Causeway defines the xref:refguide:schema:common.adoc[common schema] which defines the `<oid-dto>` element (and corresponding `OidDto` class): the object's type and its identifier.
+This is basically a formal XML equivalent to the `Bookmark` object obtained from the xref:refguide:applib:index/services/bookmark/BookmarkService.adoc[BookmarkService].
+
+There is only one requirement to make this work: every referenced domain entity must be annotated with xref:refguide:applib-ant:XmlJavaTypeAdapter.adoc[@XmlJavaTypeAdapter], specifying the framework-provided `PersistentEntityAdapter`.
+And this class is similar to the xref:refguide:applib:index/services/bookmark/BookmarkService.adoc[BookmarkService]: it knows how to create an `OidDto` from an object reference.
+
+Thus, in our view model we can legitimately write:
+
+[source,java]
+----
+public class CompareCustomers {
+
+    @XmlElement(required = true)
+    @Getter @Setter
+    Customer customer1;
+    ...
+}
+----
+
+All we need to do is remember to add that `@XmlJavaTypeAdapter` annotation to the referenced entity:
+
+[source,java]
+----
+@XmlJavaTypeAdapter(PersistentEntityAdapter.class)
+public class Customer ...  {
+    ...
+}
+----
+
+It's also possible for a DTO view models to hold collections of objects.
+These can be of any type, either simple properties, or references to other objects.
+The only bit of boilerplate that is required is the `@XmlElementWrapper` annotation.
+This instructs JAXB to create an XML element (based on the field name) to contain each of the elements.
+(If this is omitted then the contents of the collection are at the same level as the properties; almost certainly not what is required).
+
+For example, we could perhaps generalize the view model to hold a set of ``Customer``s to be compared:
+
+[source,java]
+----
+public class CompareCustomers {
+    ...
+    @XmlElementWrapper
+    @XmlElement(name = "customers")
+    @Getter @Setter
+    protected List<Customer> customersToCompare = Lists.newArrayList();
+}
+----
+
+
+[[joda-datatypes]]
+=== JODA Time Datatypes
+
+If your JAXB view model contains fields using the JODA datatypes (`LocalDate` and so on), then `@XmlJavaTypeAdapter` additional annotations in order to "teach" JAXB how to serialize out the state.
+
+The Apache Causeway applib provides a number of adapters to use out-of-the-box.
+For example:
+
+[source,java]
+----
+@XmlRootElement(name = "categorizeIncomingInvoice")
+@XmlType(
+        propOrder = {
+                ...
+                "dateReceived",
+                ...
+        }
+)
+@XmlAccessorType(XmlAccessType.FIELD)
+public class IncomingInvoiceViewModel extends IncomingOrderAndInvoiceViewModel {
+
+    @XmlJavaTypeAdapter(JodaLocalDateStringAdapter.ForJaxb.class)
+    private LocalDate dateReceived;
+
+    ...
+}
+----
+
+The full list of adapter classes are:
+
+.JAXB adapters
+[cols="1a,2a",options="header"]
+|===
+
+| JODA datatype
+| Adapter
+
+.2+| `org.joda.time.DateTime`
+| `JodaDateTimeStringAdapter.ForJaxb`
+| `JodaDateTimeXMLGregorianCalendarAdapter.ForJaxb`
+
+.2+| `org.joda.time.LocalDate`
+| `JodaLocalDateStringAdapter.ForJaxb`
+| `JodaLocalDateXMLGregorianCalendarAdapter.ForJaxb`
+
+.2+| `org.joda.time.LocalDateTime`
+| `JodaLocalDateTimeStringAdapter.ForJaxb`
+| `JodaLocalDateTimeXMLGregorianCalendarAdapter.ForJaxb`
+
+
+.2+| `org.joda.time.LocalTime`
+| `JodaLocalTimeStringAdapter.ForJaxb`
+| `JodaLocalTimeXMLGregorianCalendarAdapter.ForJaxb`
+
+| `java.sql.Timestamp`
+| `JavaSqlTimestampXmlGregorianCalendarAdapter.ForJaxb`
+
+
+|===
+
+[TIP]
+====
+If you want use other Joda data types, check out link:http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html[this blog post].
+====
+
+
+
+[[non-jaxb]]
+== Non-JAXB View Models
+
+Instead of using JAXB to specify a view model, it is also possible to use the xref:refguide:applib:index/annotation/DomainObject.adoc[@DomainObject] with a xref:refguide:applib:index/annotation/DomainObject.adoc#nature[nature] of xref:refguide:applib:index/annotation/Nature.adoc#VIEW_MODEL[VIEW_MODEL].
+
+This approach is not as powerful as using the JAXB-style of view models, because only the state of properties -- not collections -- is serialized, and moreover only certain data types are recognised.
+On the plus side, it takes less effort.
+
+For example:
+
+[source,java]
+.CompareCustomers.java, using @DomainObject(nature = VIEW_MODEL)
+----
+@DomainObject(nature = Nature.VIEW_MODEL)           // <.>
+public class CompareCustomers {
+
+    @Property                                       // <.>
+    @Getter @Setter
+    Customer customer1;
+
+    @Property                                       // <2>
+    @Getter @Setter
+    Customer customer2;
+
+    public String getCustomer1Name() {
+        return getCustomer1().getName();
+    }
+
+    public String getCustomer2Name() {
+        return getCustomer2().getName();
+    }
+
+    ...
+}
+----
+
+<.> declares the domain object as a view model
+<.> fields must be annotated with `@Property` so that they are part of the metamodel.
++
+Note that they do _not_ need to be visible, however.
+
+
+
+[[view-model-interface]]
+== `ViewModel` interface
+
+The most flexible approach to implement a view model is to implement the xref:refguide:applib:index/ViewModel.adoc[ViewModel] interface.
+
+For example:
+
+[source,java]
+.CompareCustomers.java, using ViewModel interface
+----
+@DomainObject
+public class CompareCustomers implements ViewModel {    // <.>
+
+    public String viewModelMemento() {                  // <.>
+        return getCustomer1().getRef() + ":"
+             + getCustomer2().getRef();
+    }
+    public void viewModelInit(String memento) {         // <.>
+        val ref1 = memento.split[":"](0);
+        customer1 = customerRepository.findByRef(ref1));
+        val ref2 = memento.split[":"](1);
+        customer2 = customerRepository.findByRef(ref2);
+    }
+
+    @Getter @Setter
+    Customer customer1;
+
+    @Getter @Setter
+    Customer customer2;
+
+    public String getCustomer1Name() {
+        return getCustomer1().getName();
+    }
+
+    public String getCustomer2Name() {
+        return getCustomer2().getName();
+    }
+
+    @Inject
+    CustomerRepository customerRepository;
+
+    ...
+}
+----
+
+
+
+== Self-persisting Domain entities
+
+Sometimes we may have domain entities whose persistence is _not_ managed by JDO or JPA mechanism, in other words they take responsibility for their own persistence.
+
+We can characterise these as:
+
+* external entities
++
+For example the application may interact synchronously with state exposed on another system through a REST or SOAP API.
+In this case the entity with your Apache Causeway application is a proxy or a facade for the state on the external system
+
+* internal entities
++
+For example the entity might include a data structure that is best persisted in a custom datastore, for example a graph database such as neo4j.
+
+Because such entities are responsible for their own state management, they can be implemented as view models.
+