You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/09/27 22:31:00 UTC

[isis] branch ISIS-2873-petclinic updated: ISIS-2873: cleans up 010-getting-started

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

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


The following commit(s) were added to refs/heads/ISIS-2873-petclinic by this push:
     new 7a7881a  ISIS-2873: cleans up 010-getting-started
7a7881a is described below

commit 7a7881aaec7dbb8e5c9a2c1cb90be5905ffa9ec2
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Sep 27 23:28:58 2021 +0100

    ISIS-2873: cleans up 010-getting-started
---
 .../components/tutorials/{modules => }/antora.yml  |   0
 .../tutorials/modules/ROOT/pages/about.adoc        |   4 +-
 .../tutorials/modules/petclinic/nav.adoc           | 115 ++++---
 .../petclinic/pages/010-getting-started.adoc       | 217 ++++++++++++
 ...mple-domain.adoc => 020-an-example-domain.adoc} |   0
 ...rplatex.adoc => 030-removing-boilerplatex.adoc} |   2 +-
 ...adoc => 040-fleshing-out-the-owner-entity.adoc} |  10 +-
 .../{prototyping.adoc => 050-prototyping.adoc}     |   8 +-
 ....adoc => 060-adding-the-remaining-classes.adoc} |  12 +-
 ...oc => 070-business-rules-and-unit-testing.adoc} |   4 +-
 .../pages/{modularity.adoc => 080-modularity.adoc} |   2 +-
 .../{view-models.adoc => 090-view-models.adoc}     |   2 +-
 ...n-testing.adoc => 100-integration-testing.adoc} |   0
 ...ng-further-business-logic-worked-examples.adoc} |  10 +-
 .../tutorials/modules/petclinic/pages/about.adoc   |  23 +-
 .../modules/petclinic/pages/getting-started.adoc   | 363 ---------------------
 antora/components/tutorials/nav.adoc               |  73 -----
 .../modules/fun/pages/ui/object-layout.adoc        |   1 +
 .../adoc/modules/starters/pages/helloworld.adoc    |   4 +-
 .../adoc/modules/starters/pages/simpleapp.adoc     |   9 +-
 20 files changed, 312 insertions(+), 547 deletions(-)

diff --git a/antora/components/tutorials/modules/antora.yml b/antora/components/tutorials/antora.yml
similarity index 100%
rename from antora/components/tutorials/modules/antora.yml
rename to antora/components/tutorials/antora.yml
diff --git a/antora/components/tutorials/modules/ROOT/pages/about.adoc b/antora/components/tutorials/modules/ROOT/pages/about.adoc
index 9728e9d..6605d17 100644
--- a/antora/components/tutorials/modules/ROOT/pages/about.adoc
+++ b/antora/components/tutorials/modules/ROOT/pages/about.adoc
@@ -6,6 +6,6 @@
 
 This page catalogues the tutorials available to help you learn Apache Isis.
 
-* xref:tutorials:petclinic:about.adoc[Petclinic]
+* xref:petclinic:about.adoc[Petclinic]
 +
-A take on the classic petclinc application
+A take on the classic petclinic application
diff --git a/antora/components/tutorials/modules/petclinic/nav.adoc b/antora/components/tutorials/modules/petclinic/nav.adoc
index e8f62c2..3c44f78 100644
--- a/antora/components/tutorials/modules/petclinic/nav.adoc
+++ b/antora/components/tutorials/modules/petclinic/nav.adoc
@@ -1,73 +1,72 @@
 
 * xref:about.adoc[Introduction]
 
-* xref:getting-started.adoc[Getting Started]
-** xref:getting-started.adoc#_prereqs[Prereqs]
-** xref:getting-started.adoc#_generate[Generate]
-** xref:getting-started.adoc#_explore_the_generated_app[Explore the generated app]
-** xref:getting-started.adoc#_pull_down_github_example_solution[image:hand.png[] *010*: Pull down the github example/solution]
-** xref:getting-started.adoc#_set_up_your_dev_env[Set up your dev env]
-** xref:getting-started.adoc#_naked_objects_pattern[Naked Objects pattern]
-** xref:getting-started.adoc#_ui_hints[UI Hints]
+* xref:010-getting-started.adoc[Getting Started]
+** xref:010-getting-started.adoc#_prereqs[Prereqs]
+** xref:010-getting-started.adoc#_generate[Generate]
+** xref:010-getting-started.adoc#_explore_the_generated_app[Explore the generated app]
+** xref:010-getting-started.adoc#_pull_down_github_example_solution[image:hand.png[] *010*: Pull down the github example/solution]
+** xref:010-getting-started.adoc#_set_up_your_dev_env[Set up your dev env]
+** xref:010-getting-started.adoc#_naked_objects_pattern[Naked Objects pattern]
+** xref:010-getting-started.adoc#_ui_hints[UI Hints]
 
-* xref:an-example-domain.adoc[An Example Domain]
-** xref:an-example-domain.adoc#_rename_code_helloworldobject_code_to_code_owner_code[image:hand.png[] *020*: Rename HelloWorldObject to Owner]
+* xref:020-an-example-domain.adoc[An Example Domain]
+** xref:020-an-example-domain.adoc#_rename_code_helloworldobject_code_to_code_owner_code[image:hand.png[] *020*: Rename HelloWorldObject to Owner]
 
-* xref:removing-boilerplatex.adoc[Removing Boilerplate]
-** xref:removing-boilerplatex.adoc#_removing_boilerplate_lombok[image:hand.png[] *030*: Lombok]
-** xref:removing-boilerplatex.adoc#_removing_boilerplate_parameter_names[image:hand.png[] *040*: Parameter Names]
-** xref:removing-boilerplatex.adoc#_disable_editing[image:hand.png[] *050*: Disable editing]
-** xref:removing-boilerplatex.adoc#_font_awesome_icons[Font awesome icons]
-** xref:removing-boilerplatex.adoc#_implicit_action_annotations[image:hand.png[] *060*: Implicit Action Annotations]
+* xref:030-removing-boilerplatex.adoc[Removing Boilerplate]
+** xref:030-removing-boilerplatex.adoc#_removing_boilerplate_lombok[image:hand.png[] *030*: Lombok]
+** xref:030-removing-boilerplatex.adoc#_removing_boilerplate_parameter_names[image:hand.png[] *040*: Parameter Names]
+** xref:030-removing-boilerplatex.adoc#_disable_editing[image:hand.png[] *050*: Disable editing]
+** xref:030-removing-boilerplatex.adoc#_font_awesome_icons[Font awesome icons]
+** xref:030-removing-boilerplatex.adoc#_implicit_action_annotations[image:hand.png[] *060*: Implicit Action Annotations]
 
-* xref:fleshing-out-the-owner-entity.adoc[Fleshing out the Owner entity]
-** xref:fleshing-out-the-owner-entity.adoc#_rework_code_owner_code_s_name_code_firstname_code_and_code_lastname_code[image:hand.png[] *070*: Rework Owner's name (firstName and lastName)]
-** xref:fleshing-out-the-owner-entity.adoc#_derived_name_property[image:hand.png[] *080*: Derived name property]
-** xref:fleshing-out-the-owner-entity.adoc#_digression_changing_the_app_name[image:hand.png[] *090*: Digression: Changing the App Name]
-** xref:fleshing-out-the-owner-entity.adoc#_changing_the_object_type_class_alias[image:hand.png[] *100*: Changing the "Object Type" Class Alias]
-** xref:fleshing-out-the-owner-entity.adoc#_add_other_properties_for_code_owner_code[image:hand.png[] *110*: Add other properties for Owner]
-** xref:fleshing-out-the-owner-entity.adoc#_using_specifications_to_encapsulate_business_rules[image:hand.png[] *120*: Using specifications to encapsulate business rules]
+* xref:040-fleshing-out-the-owner-entity.adoc[Fleshing out the Owner entity]
+** xref:040-fleshing-out-the-owner-entity.adoc#_rework_code_owner_code_s_name_code_firstname_code_and_code_lastname_code[image:hand.png[] *070*: Rework Owner's name (firstName and lastName)]
+** xref:040-fleshing-out-the-owner-entity.adoc#_derived_name_property[image:hand.png[] *080*: Derived name property]
+** xref:040-fleshing-out-the-owner-entity.adoc#_digression_changing_the_app_name[image:hand.png[] *090*: Digression: Changing the App Name]
+** xref:040-fleshing-out-the-owner-entity.adoc#_changing_the_object_type_class_alias[image:hand.png[] *100*: Changing the "Object Type" Class Alias]
+** xref:040-fleshing-out-the-owner-entity.adoc#_add_other_properties_for_code_owner_code[image:hand.png[] *110*: Add other properties for Owner]
+** xref:040-fleshing-out-the-owner-entity.adoc#_using_specifications_to_encapsulate_business_rules[image:hand.png[] *120*: Using specifications to encapsulate business rules]
 
-* xref:prototyping.adoc[Prototyping]
-** xref:prototyping.adoc#_fixture_scripts_for_owner[image:hand.png[] *130*: Fixture Scripts (for Owner)]
-** xref:prototyping.adoc#_run_with_a_different_manifest[image:hand.png[] *140*: Run with a different manifest]
+* xref:050-prototyping.adoc[Prototyping]
+** xref:050-prototyping.adoc#_fixture_scripts_for_owner[image:hand.png[] *130*: Fixture Scripts (for Owner)]
+** xref:050-prototyping.adoc#_run_with_a_different_manifest[image:hand.png[] *140*: Run with a different manifest]
 
-* xref:adding-the-remaining-classes.adoc[Adding the remaining classes]
-** xref:adding-the-remaining-classes.adoc#_newpet_action_and_code_pet_code_to_code_owner_code_association[image:hand.png[] *150*: `newPet` action, `Pet` to `Owner`]
-** xref:adding-the-remaining-classes.adoc#_collection_of_code_pet_code_s[image:hand.png[] *160*: Collection of Pets)]
-** xref:adding-the-remaining-classes.adoc#_extend_our_fixture[image:hand.png[] *170*: Extend our Fixtures]
-** xref:adding-the-remaining-classes.adoc#_adding_code_visit_code[image:hand.png[] *180*: Adding Visit]
+* xref:060-adding-the-remaining-classes.adoc[Adding the remaining classes]
+** xref:060-adding-the-remaining-classes.adoc#_newpet_action_and_code_pet_code_to_code_owner_code_association[image:hand.png[] *150*: `newPet` action, `Pet` to `Owner`]
+** xref:060-adding-the-remaining-classes.adoc#_collection_of_code_pet_code_s[image:hand.png[] *160*: Collection of Pets)]
+** xref:060-adding-the-remaining-classes.adoc#_extend_our_fixture[image:hand.png[] *170*: Extend our Fixtures]
+** xref:060-adding-the-remaining-classes.adoc#_adding_code_visit_code[image:hand.png[] *180*: Adding Visit]
 
-* xref:business-rules-and-unit-testing.adoc[Business Rules & (Unit) Testing]
-** xref:business-rules-and-unit-testing.adoc#_defaults_and_code_clockservice_code[image:hand.png[] *190*: Defaults, and ClockService]
-** xref:business-rules-and-unit-testing.adoc#_unit_tests[image:hand.png[] *200*: Unit Tests]
-** xref:business-rules-and-unit-testing.adoc#_validation[image:hand.png[] *210*: Validation]
+* xref:070-business-rules-and-unit-testing.adoc[Business Rules & (Unit) Testing]
+** xref:070-business-rules-and-unit-testing.adoc#_defaults_and_code_clockservice_code[image:hand.png[] *190*: Defaults, and ClockService]
+** xref:070-business-rules-and-unit-testing.adoc#_unit_tests[image:hand.png[] *200*: Unit Tests]
+** xref:070-business-rules-and-unit-testing.adoc#_validation[image:hand.png[] *210*: Validation]
 
-* xref:modularity.adoc[Modularity]
-** xref:modularity.adoc#_introducing_packages[image:hand.png[] *220*: Introducing Packages]
-** xref:modularity.adoc#_inverting_responsibilities_refactoring_the_code_pet_code_s_visits[image:hand.png[] *230*: Inverting responsibilities (Refactoring the Pet's visits)]
-** xref:modularity.adoc#_pet_s_visits_a_contributed_collection[image:hand.png[] *240*: Pet’s visits (a contributed collection)]
-** xref:modularity.adoc#_events[image:hand.png[] *250*: Events]
+* xref:080-modularity.adoc[Modularity]
+** xref:080-modularity.adoc#_introducing_packages[image:hand.png[] *220*: Introducing Packages]
+** xref:080-modularity.adoc#_inverting_responsibilities_refactoring_the_code_pet_code_s_visits[image:hand.png[] *230*: Inverting responsibilities (Refactoring the Pet's visits)]
+** xref:080-modularity.adoc#_pet_s_visits_a_contributed_collection[image:hand.png[] *240*: Pet’s visits (a contributed collection)]
+** xref:080-modularity.adoc#_events[image:hand.png[] *250*: Events]
 
-* xref:view-models.adoc[View Models]
-** xref:view-models.adoc#_dashboard[image:hand.png[] *260*: Dashboard]
+* xref:090-view-models.adoc[View Models]
+** xref:090-view-models.adoc#_dashboard[image:hand.png[] *260*: Dashboard]
 
-* xref:integration-testing.adoc[(Integration) Testing]
-** xref:integration-testing.adoc#_an_improved_fixture_script[image:hand.png[] *270*: An improved Fixture Script]
-** xref:integration-testing.adoc#_writing_integration_tests[image:hand.png[] *280*: Writing Integration Tests]
-** xref:integration-testing.adoc#_factor_out_abstract_integration_test[image:hand.png[] *290*: Factor out abstract integration test]
-** xref:integration-testing.adoc#_move_teardowns_to_modules[image:hand.png[] *300*: Move teardowns to modules]
-** xref:integration-testing.adoc#_fake_data_service[image:hand.png[] *310*: Fake Data Service]
-** xref:integration-testing.adoc#_extend_the_fixture_script_to_set_up_visits[image:hand.png[] *320*: Extend the Fixture script to set up visits]
+* xref:100-integration-testing.adoc[(Integration) Testing]
+** xref:100-integration-testing.adoc#_an_improved_fixture_script[image:hand.png[] *270*: An improved Fixture Script]
+** xref:100-integration-testing.adoc#_writing_integration_tests[image:hand.png[] *280*: Writing Integration Tests]
+** xref:100-integration-testing.adoc#_factor_out_abstract_integration_test[image:hand.png[] *290*: Factor out abstract integration test]
+** xref:100-integration-testing.adoc#_move_teardowns_to_modules[image:hand.png[] *300*: Move teardowns to modules]
+** xref:100-integration-testing.adoc#_fake_data_service[image:hand.png[] *310*: Fake Data Service]
+** xref:100-integration-testing.adoc#_extend_the_fixture_script_to_set_up_visits[image:hand.png[] *320*: Extend the Fixture script to set up visits]
 
-* xref:adding-further-business-logic-worked-examples.adoc[Further business logic]
-** xref:adding-further-business-logic-worked-examples.adoc#_enter_an_outcome[image:hand.png[] *330*: Enter an outcome]
-** xref:adding-further-business-logic-worked-examples.adoc#_pay_for_a_visit[image:hand.png[] *340*: Pay for a visit]
-** xref:adding-further-business-logic-worked-examples.adoc#_prevent_payment_for_a_visit_twice[image:hand.png[] *350*: Prevent payment for a visit twice ("see it? use it? do it?")]
-** xref:adding-further-business-logic-worked-examples.adoc#_find_code_visit_code_s_not_yet_paid_and_overdue[image:hand.png[] *360*: Find Visits not yet paid and overdue]
-** xref:adding-further-business-logic-worked-examples.adoc#_digression_hiding_columns_in_tables[image:hand.png[] *370*: Digression: Hiding Columns in Tables]
-** xref:adding-further-business-logic-worked-examples.adoc#_another_digression_icons_and_css[image:hand.png[] *380*: Another Digression: Icons and CSS]
-** xref:adding-further-business-logic-worked-examples.adoc#_delete_an_code_owner_code_provided_no_unpaid_code_visit_code_s[image:hand.png[] *390*: Delete an Owner provided no unpaid Visits]
+* xref:110-adding-further-business-logic-worked-examples.adoc[Further business logic]
+** xref:110-adding-further-business-logic-worked-examples.adoc#_enter_an_outcome[image:hand.png[] *330*: Enter an outcome]
+** xref:110-adding-further-business-logic-worked-examples.adoc#_pay_for_a_visit[image:hand.png[] *340*: Pay for a visit]
+** xref:110-adding-further-business-logic-worked-examples.adoc#_prevent_payment_for_a_visit_twice[image:hand.png[] *350*: Prevent payment for a visit twice ("see it? use it? do it?")]
+** xref:110-adding-further-business-logic-worked-examples.adoc#_find_code_visit_code_s_not_yet_paid_and_overdue[image:hand.png[] *360*: Find Visits not yet paid and overdue]
+** xref:110-adding-further-business-logic-worked-examples.adoc#_digression_hiding_columns_in_tables[image:hand.png[] *370*: Digression: Hiding Columns in Tables]
+** xref:110-adding-further-business-logic-worked-examples.adoc#_another_digression_icons_and_css[image:hand.png[] *380*: Another Digression: Icons and CSS]
+** xref:110-adding-further-business-logic-worked-examples.adoc#_delete_an_code_owner_code_provided_no_unpaid_code_visit_code_s[image:hand.png[] *390*: Delete an Owner provided no unpaid Visits]
 
-//* xref:incode-platform.adoc[Incode Platform]
 //* xref:i18n.adoc[i18n]
diff --git a/antora/components/tutorials/modules/petclinic/pages/010-getting-started.adoc b/antora/components/tutorials/modules/petclinic/pages/010-getting-started.adoc
new file mode 100644
index 0000000..432e1e3
--- /dev/null
+++ b/antora/components/tutorials/modules/petclinic/pages/010-getting-started.adoc
@@ -0,0 +1,217 @@
+= Getting Started
+
+: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 [...]
+
+
+== Prereqs
+
+You'll need:
+
+* Java 11
++
+Apache Isis should work up to at least Java 15, but stick with Java 11 for now.
+
+* Maven 3.6.x
++
+Maven 3.6.x or later is recommended.
+
+* git
++
+The tutorial includes worked example/solution, provided in a github-hosted repo.
+This has multiple tags for the various checkpoints so you can pick up the tutorial at any point.
+
+* a Java IDE with support for Maven.
++
+The Apache Isis website has detailed documentation for setting up to use xref:setupguide:intellij:about.adoc[IntelliJ] or xref:setupguide:eclipse:about.adoc[Eclipse].
++
+For this tutorial, make sure that your IDE is configured to support Lombok.
+
+
+
+== Exercise 1.1: Starter apps / clone the repo
+
+Apache Isis provides a two starter apps, xref:docs:starters:helloworld.adoc[HelloWorld] and xref:docs:starters:simpleapp.adoc[SimpleApp].
+These are identical in terms of functionality, but the simpleapp provides more structure and includes example tests.
+
+NOTE: there are JPA and JDO variants of the starter apps.
+This tutorial uses JPA as it is more commonly used.
+
+We start the tutorial simply with a copy of xref:docs:starters:simpleapp.adoc[SimpleApp]; in subsequent exercises we'll refactor and build upon it to morph it into the petclinic domain.
+
+
+* Clone the repo:
++
+[source,bash]
+----
+git clone https://github.com/apache/isis-app-demo
+----
+
+* Checkout the first tag, and build:
++
+[source,bash]
+----
+git checkout tags/01-01-starter-app
+mvn clean install
+----
+
+* run the app:
++
+[source,bash]
+----
+mvn -pl webapp spring-boot:run
+----
+
+
+== Exercise 1.2: Explore the Simple App
+
+Although we'll be refactoring the codebase in the next exercise, take a few minutes to familiarize yourself with the functionality of the simpleapp.
+
+Check your understanding by using the app to:
+
+* create new objects
+* search by name
+* list all objects
+
+* Use the menu:Prototyping[Fixture Scripts] menu to run in the "DomainAppDemo" fixture script.
++
+This will create some sample data.
+
+
+== Exercise 1.3: Running from the IDE
+
+Running from the command line isn't ideal, so
+
+* load the project into your IDE as a Maven project, build and run.
+
+* The app is a Spring boot application, so locate the class with a main, and run.
+
+* alternatively, your IDE might also have specialised support for Spring Boot apps, so run the app that way if you wish.
+
+* with the IDE load
+
+If you want to go deeper, open up the xref:docs:starters:simpleapp.adoc[page describing the SimpleApp] and start to explore the xref:docs:starters:simpleapp.adoc#structure-of-the-app[structure of the app] files.
+
+
+
+== Naked Objects pattern
+
+Apache Isis is an implementation of the _naked objects pattern_, which means that entities (and later, as we'll see view models) are automatically exposed in the UI.
+
+[TIP]
+====
+An ORM such as JPA (EclipseLink or Hibernate) maps domain objects into an RDBMS or other datastore.
+You can think of Apache Isis (and naked objects) similarly, but it's an OIM - an object _interface_ mapper.
+It maps to the UI layer rather than the persistence layer.
+
+Common to both ORMs and OIMs is an internal metamodel; this is where much of the power comes from.
+====
+
+We can explore this by looking at the classes provided by the starter app:
+
+* locate the `SimpleObjects` domain service, and notice the methods annotated with `@Action`.
++
+Map these to the "Simple Objects" menu.
+
+
+* locate the `SimpleObject` entity, and notice the methods annotated with `@Property` and `@Action`.
++
+Map these onto the fields of the "simple object" entity, and the action buttons (eg to "change name").
+
+
+It's common for each entity (or more precisely, aggregate root) to have a corresponding domain service, acting as its repository.
+This abstraction hides the details of interacting with the persistence data store.
+Domain services are automatically injected wherever they are required, using `@javax.inject.Inject`.
+
+Apache Isis applications therefore generally follow the _hexagonal architecture_ (aka the _ports and adapters_ architecture).
+
+As well as writing our own domain services, there are also many framework-provided domain services, for example `RepositoryService` (to persist objects).
+See the xref:refguide:applib-svc:about.adoc[Reference Guide: Domain Services] docs for the full list.
+
+
+== UI Hints
+
+The framework derives as much of the UI as possible from the domain objects' intrinsic structure and behaviour, but there are some supporting structures and conventions that are there primarily to improve the UI.
+
+
+=== Titles
+
+A title is the identifier of a domain object for the end-user.
+
+For `SimpleObject`, this is defined declaratively:
+
+[source,java]
+----
+@Title
+// ... other annotations omitted ...
+private String name;
+----
+
+It can also be specified imperatively using either the `title()` or `toString()` method.
+
+Each domain object is also associated with an icon.
+Typically this is static and in the same package as the class; see `SimpleObject.png`.
+
+
+*Mini-Exercise*:
+
+(No solution is provided for this exercise).
+
+* replace the `@Title` annotation with a `title()` method:
++
+[source,java]
+----
+public String title() {
+    return getName();
+}
+----
+
+You can learn more about UI Hint Methods in the reference guide, xref:refguide:applib-methods:ui-hints.adoc[here].
+
+
+
+=== Object layout
+
+Frameworks that implement the _naked objects pattern_ automatically provide a default representation of domain objects.
+In many cases the details of that representation can be inferred directly from the domain members.
+For example the label of a field for an object's property (eg `SimpleObject#name`) can be derived directly from the name of the object property itself (`getName()`).
+
+In the absence of other metadata, Apache Isis will render a domain object with its properties to the left-hand side and its collections (if any) to the right.
+The order of these properties and collections can be specified using the `@PropertyLayout` annotation and the `@CollectionLayout` annotation.
+There are other annotations to group properties together and to associate action buttons with either properties or collections.
+
+The downside of using annotations is that changing the layout requires that the application be restarted, and certain more complex UIs, such as multi-columns or tab groups are difficult or impossible to express.
+
+Therefore Apache Isis also allows the layout of domain objects to be specified using a complementary layout file, eg `SimpleObject.layout.xml`.
+This is modelled upon bootstrap and so supports arbitrary rows and columns as well as tab groups and tabs.
+
+*Mini-Exercise*:
+
+* locate the `SimpleObject.layout.xml` file
+* compare the structure of the layout file to that of the rendered object
+* change the file, eg the relative widths of the columns
+* use the IDE to copy over the file to the classpath; the new version will be picked up automatically
+** for example, with IntelliJ use menu:Run[Reload Changed Classes].
+
+
+You can learn more about file-based layouts in the fundamentals guide describing at xref:userguide:fun:ui.adoc#object-layout[Object Layout]s.
+
+It's also possible to change the order of columns at runtime, using the `SimpleObject.columnOrder.txt` file.
+For more on this topic, see the section of the fundamentals guide describing xref:userguide:fun:ui.adoc#table-columns[Table Columns].
+
+
+
+=== menubars.layout.xml
+
+In a similar fashion, the actions of the various domain services are grouped into menus using the `menubars.layout.xml` file.
+
+*Mini-Exercise*:
+
+* locate the `menubars.layout.xml` file
+* compare the structure of the layout file to that of the rendered menu bar
+* change the file, eg reorder menu items or create new menus
+* again, use the IDE to copy over the file to the classpath
+** for example, with IntelliJ use menu:Run[Reload Changed Classes]/
+
+
+To learn more, see the section of the fundamentals guide describing xref:userguide:fun:ui.adoc#file-based-menus[file-based] menu bar layout.
+
diff --git a/antora/components/tutorials/modules/petclinic/pages/an-example-domain.adoc b/antora/components/tutorials/modules/petclinic/pages/020-an-example-domain.adoc
similarity index 100%
rename from antora/components/tutorials/modules/petclinic/pages/an-example-domain.adoc
rename to antora/components/tutorials/modules/petclinic/pages/020-an-example-domain.adoc
diff --git a/antora/components/tutorials/modules/petclinic/pages/removing-boilerplatex.adoc b/antora/components/tutorials/modules/petclinic/pages/030-removing-boilerplatex.adoc
similarity index 98%
rename from antora/components/tutorials/modules/petclinic/pages/removing-boilerplatex.adoc
rename to antora/components/tutorials/modules/petclinic/pages/030-removing-boilerplatex.adoc
index 4ed2aba..6a00b9a 100644
--- a/antora/components/tutorials/modules/petclinic/pages/removing-boilerplatex.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/030-removing-boilerplatex.adoc
@@ -89,7 +89,7 @@ mvn clean package jetty:run
 +
 For example, in IntelliJ:
 +
-image::{_imagesdir}/intellij-java-compiler-parameters.png[width="800px",link="_images/intellij-java-compiler-parameters.png"]
+image::intellij-java-compiler-parameters.png[width="800px",link="_images/intellij-java-compiler-parameters.png"]
 
 * in the `pom.xml`, add the following to `<dependencies>`:
 +
diff --git a/antora/components/tutorials/modules/petclinic/pages/fleshing-out-the-owner-entity.adoc b/antora/components/tutorials/modules/petclinic/pages/040-fleshing-out-the-owner-entity.adoc
similarity index 96%
rename from antora/components/tutorials/modules/petclinic/pages/fleshing-out-the-owner-entity.adoc
rename to antora/components/tutorials/modules/petclinic/pages/040-fleshing-out-the-owner-entity.adoc
index 12275d5..e46104c 100644
--- a/antora/components/tutorials/modules/petclinic/pages/fleshing-out-the-owner-entity.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/040-fleshing-out-the-owner-entity.adoc
@@ -159,15 +159,15 @@ public List<Owner> findByName(
 
 The ``Owner``'s `firstName` and `lastName` properties are updated using the `updateName` action, but when the action's button is invoked, it only "replaces" the `firstName` property:
 
-image::{_imagesdir}/Owner-updateName-prompt.png[width="400px",link="_images/Owner-updateName-prompt.png"]
+image::Owner-updateName-prompt.png[width="400px",link="_images/Owner-updateName-prompt.png"]
 
 We can improve this by introducing a derived `name` property and then hiding the `firstName` and `lastName`:
 
-image::{_imagesdir}/Owner-name.png[width="400px",link="_images/Owner-name.png"]
+image::Owner-name.png[width="400px",link="_images/Owner-name.png"]
 
 And, when `Owner#updateName` is invoked, the prompt we'll see is:
 
-image::{_imagesdir}/Owner-name-updated.png[width="400px",link="_images/Owner-name-updated.png"]
+image::Owner-name-updated.png[width="400px",link="_images/Owner-name-updated.png"]
 
 
 
@@ -381,7 +381,7 @@ While we are at it, we could move the `notes` property to its own tab:
 +
 resulting in:
 
-image::{_imagesdir}/Owner-with-contact-details.png[width="600px",link="_images/Owner-with-contact-details.png"]
+image::Owner-with-contact-details.png[width="600px",link="_images/Owner-with-contact-details.png"]
 
 
 == Using specifications to encapsulate business rules
@@ -441,7 +441,7 @@ private String phoneNumber;
 
 * extend the `Orders#create` action to also extend a `phoneNumber` parameter, and use the `PhoneNumberSpec` to implement the same business rule:
 +
-image::{_imagesdir}/Owners-create-with-phoneNumber.png[width="400px",link="_images/Owners-create-with-phoneNumber.png"]
+image::Owners-create-with-phoneNumber.png[width="400px",link="_images/Owners-create-with-phoneNumber.png"]
 +
 using this code:
 +
diff --git a/antora/components/tutorials/modules/petclinic/pages/prototyping.adoc b/antora/components/tutorials/modules/petclinic/pages/050-prototyping.adoc
similarity index 94%
rename from antora/components/tutorials/modules/petclinic/pages/prototyping.adoc
rename to antora/components/tutorials/modules/petclinic/pages/050-prototyping.adoc
index cdc8ca4..2652840 100644
--- a/antora/components/tutorials/modules/petclinic/pages/prototyping.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/050-prototyping.adoc
@@ -83,15 +83,15 @@ public class PetClinicFixtureScriptSpecProvider
 
 This now provides us with a _Run Fixture Script_ menu item under the _Prototyping_ menu:
 
-image::{_imagesdir}/run-fixture-script-menu-item.png[width="250px",link="_images/run-fixture-script-menu-item.png"]
+image::run-fixture-script-menu-item.png[width="250px",link="_images/run-fixture-script-menu-item.png"]
 
 from which we can select the _Order Fixture Script_:
 
-image::{_imagesdir}/run-fixture-script-prompt.png[width="400px",link="_images/run-fixture-script-prompt.png"]
+image::run-fixture-script-prompt.png[width="400px",link="_images/run-fixture-script-prompt.png"]
 
 When invoked this shows the three `Order` domain objects just created:
 
-image::{_imagesdir}/run-fixture-script-result.png[width="800px",link="_images/run-fixture-script-result.png"]
+image::run-fixture-script-result.png[width="800px",link="_images/run-fixture-script-result.png"]
 
 
 == Run with a different manifest
@@ -183,7 +183,7 @@ public class PetClinicAppManifestWithFixture
 +
 for example:
 +
-image::{_imagesdir}/extended-manifest-run-configuration.png[width="800px",link="_images/extended-manifest-run-configuration.png"]
+image::extended-manifest-run-configuration.png[width="800px",link="_images/extended-manifest-run-configuration.png"]
 
 When you run the application, the fixture should have run already and so there should be some ``Owner`` instances.
 
diff --git a/antora/components/tutorials/modules/petclinic/pages/adding-the-remaining-classes.adoc b/antora/components/tutorials/modules/petclinic/pages/060-adding-the-remaining-classes.adoc
similarity index 95%
rename from antora/components/tutorials/modules/petclinic/pages/adding-the-remaining-classes.adoc
rename to antora/components/tutorials/modules/petclinic/pages/060-adding-the-remaining-classes.adoc
index 5b061d0..e091cbd 100644
--- a/antora/components/tutorials/modules/petclinic/pages/adding-the-remaining-classes.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/060-adding-the-remaining-classes.adoc
@@ -13,15 +13,15 @@ Let's start by tackling one side of this, from `Pet` to `Owner`.
 
 * We'll add a new action to create a new `Pet` from an `Owner`:
 +
-image::{_imagesdir}/owner-newPet.png[width="400px",link="_images/owner-newPet.png"]
+image::owner-newPet.png[width="400px",link="_images/owner-newPet.png"]
 +
 which will prompt for the name and species of the `Pet`:
 +
-image::{_imagesdir}/owner-newPet-prompt.png[width="400px",link="_images/owner-newPet-prompt.png"]
+image::owner-newPet-prompt.png[width="400px",link="_images/owner-newPet-prompt.png"]
 
 * and, when the `Pet` is returned, it will be associated with the `Owner` that created it:
 +
-image::{_imagesdir}/Pet.png[width="600px",link="_images/Pet.png"]
+image::Pet.png[width="600px",link="_images/Pet.png"]
 
 === Solution
 
@@ -166,7 +166,7 @@ At this point in our app, although the `Pet` knows its `Owner`, the opposite isn
 
 Our design says we'd like this to be a bidirectional 1-to-many association:
 
-image::{_imagesdir}/Owner-pets.png[width="800px",link="_images/Owner-pets.png"]
+image::Owner-pets.png[width="800px",link="_images/Owner-pets.png"]
 
 Let's add in the `Owner#pets` collection:
 
@@ -322,11 +322,11 @@ protected void execute(final ExecutionContext ec) {
 Our final entity is `Visit`.
 Let's extend our app to allow ``Visit``s to be booked from an ``Owner``'s ``Pet``:
 
-image::{_imagesdir}/Pet-bookVisit-prompt.png[width="800px",link="_images/Pet-bookVisit-prompt.png"]
+image::Pet-bookVisit-prompt.png[width="800px",link="_images/Pet-bookVisit-prompt.png"]
 
 returning
 
-image::{_imagesdir}/Visit.png[width="800px",link="_images/Visit.png"]
+image::Visit.png[width="800px",link="_images/Visit.png"]
 
 
 
diff --git a/antora/components/tutorials/modules/petclinic/pages/business-rules-and-unit-testing.adoc b/antora/components/tutorials/modules/petclinic/pages/070-business-rules-and-unit-testing.adoc
similarity index 95%
rename from antora/components/tutorials/modules/petclinic/pages/business-rules-and-unit-testing.adoc
rename to antora/components/tutorials/modules/petclinic/pages/070-business-rules-and-unit-testing.adoc
index 1813a66..5914c4e 100644
--- a/antora/components/tutorials/modules/petclinic/pages/business-rules-and-unit-testing.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/070-business-rules-and-unit-testing.adoc
@@ -11,7 +11,7 @@ In this part of the tutorial we'll cover unit testing, later on we'll look at in
 By way of motivation, let's consider a small enhancement we could make to the app.
 For example, it would improve the usability if the app automatically suggesting a time for a new `Visit` that was in the future (say tomorrow, at 9am):
 
-image::{_imagesdir}/Pet-bookVisit-prompt-with-default.png[width="800px",link="_images/Pet-bookVisit-prompt-with-default.png"]
+image::Pet-bookVisit-prompt-with-default.png[width="800px",link="_images/Pet-bookVisit-prompt-with-default.png"]
 
 [NOTE]
 ====
@@ -124,7 +124,7 @@ All configured expectations are also automatically verified.
 It doesn't really make sense to book a visit in the past.
 Let's fix that with some validation:
 s
-image::{_imagesdir}/Pet-bookVisit-prompt-with-validate.png[width="800px",link="_images/Pet-bookVisit-prompt-with-validate.png"]
+image::Pet-bookVisit-prompt-with-validate.png[width="800px",link="_images/Pet-bookVisit-prompt-with-validate.png"]
 
 === Solution
 
diff --git a/antora/components/tutorials/modules/petclinic/pages/modularity.adoc b/antora/components/tutorials/modules/petclinic/pages/080-modularity.adoc
similarity index 99%
rename from antora/components/tutorials/modules/petclinic/pages/modularity.adoc
rename to antora/components/tutorials/modules/petclinic/pages/080-modularity.adoc
index 252aa12..ba29582 100644
--- a/antora/components/tutorials/modules/petclinic/pages/modularity.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/080-modularity.adoc
@@ -231,7 +231,7 @@ We also have the issue that we can't actually access the ``Visit``s once they ha
 An obvious place to see them would probably be from the `Pet`.
 Similar to the "bookVisit" contributed action, we can also contribute a "visits" collection:
 
-image::{_imagesdir}/Pet-visits-collection.png[width="800px",link="_images/Pet-visits-collection.png"]
+image::Pet-visits-collection.png[width="800px",link="_images/Pet-visits-collection.png"]
 
 === Solution
 
diff --git a/antora/components/tutorials/modules/petclinic/pages/view-models.adoc b/antora/components/tutorials/modules/petclinic/pages/090-view-models.adoc
similarity index 97%
rename from antora/components/tutorials/modules/petclinic/pages/view-models.adoc
rename to antora/components/tutorials/modules/petclinic/pages/090-view-models.adoc
index f0271e6..06a718c 100644
--- a/antora/components/tutorials/modules/petclinic/pages/view-models.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/090-view-models.adoc
@@ -17,7 +17,7 @@ Moreover, we'll make this the home page so that it is automatically shown when t
 
 Our end result is:
 
-image::{_imagesdir}/dashboard.png[width="800px",link="_images/dashboard.png"]
+image::dashboard.png[width="800px",link="_images/dashboard.png"]
 
 
 === Solution
diff --git a/antora/components/tutorials/modules/petclinic/pages/integration-testing.adoc b/antora/components/tutorials/modules/petclinic/pages/100-integration-testing.adoc
similarity index 100%
rename from antora/components/tutorials/modules/petclinic/pages/integration-testing.adoc
rename to antora/components/tutorials/modules/petclinic/pages/100-integration-testing.adoc
diff --git a/antora/components/tutorials/modules/petclinic/pages/adding-further-business-logic-worked-examples.adoc b/antora/components/tutorials/modules/petclinic/pages/110-adding-further-business-logic-worked-examples.adoc
similarity index 97%
rename from antora/components/tutorials/modules/petclinic/pages/adding-further-business-logic-worked-examples.adoc
rename to antora/components/tutorials/modules/petclinic/pages/110-adding-further-business-logic-worked-examples.adoc
index 218fefb..e2e0271 100644
--- a/antora/components/tutorials/modules/petclinic/pages/adding-further-business-logic-worked-examples.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/110-adding-further-business-logic-worked-examples.adoc
@@ -27,7 +27,7 @@ In this section we'll implement the missing functionality, along with unit or in
 
 An outcome for a `Visit` consists of a diagnosis, and also the cost to be paid by the ``Pet``'s `Owner`.
 
-image::{_imagesdir}/Visit-enterOutcome.png[width="800px",link="_images/Visit-enterOutcome.png"]
+image::Visit-enterOutcome.png[width="800px",link="_images/Visit-enterOutcome.png"]
 
 === Solution
 
@@ -326,7 +326,7 @@ git checkout tags/360-find-visits-not-yet-paid-and-overdue
 mvn clean package jetty:run
 ----
 
-image::{_imagesdir}/Dashboard-overdue.png[width="800px",link="_images/Dashboard-overdue.png"]
+image::Dashboard-overdue.png[width="800px",link="_images/Dashboard-overdue.png"]
 
 === Exercise
 
@@ -432,7 +432,7 @@ We could improve the dashboard a little.
 After all, in the "overdue" collection there's no point in showing the "paidOn"; the value will always be null.
 Also, the "reason" column is also somewhat superfluous (as, arguably, is the "diagnosis" column):
 
-image::{_imagesdir}/Dashboard-overdue-ui-hints.png[width="800px",link="_images/Dashboard-overdue-ui-hints.png"]
+image::Dashboard-overdue-ui-hints.png[width="800px",link="_images/Dashboard-overdue-ui-hints.png"]
 
 The framework offers two different ways to address this, so we'll show both.
 
@@ -497,11 +497,11 @@ This returns a simple string that is added as a CSS class wherever the object is
 
 In this exercise we'll use a different icon for the various species of `Pet`:
 
-image::{_imagesdir}/Pet-icons.png[width="800px",link="_images/Pet-icons.png"]
+image::Pet-icons.png[width="800px",link="_images/Pet-icons.png"]
 
 Let's also use a strike-through text for all ``Visit``s that are paid when rendered within a collection:
 
-image::{_imagesdir}/Visits-paid-strikethrough.png[width="800px",link="_images/Visits-paid-strikethrough.png"]
+image::Visits-paid-strikethrough.png[width="800px",link="_images/Visits-paid-strikethrough.png"]
 
 
 
diff --git a/antora/components/tutorials/modules/petclinic/pages/about.adoc b/antora/components/tutorials/modules/petclinic/pages/about.adoc
index 5950d22..4f003c8 100644
--- a/antora/components/tutorials/modules/petclinic/pages/about.adoc
+++ b/antora/components/tutorials/modules/petclinic/pages/about.adoc
@@ -1,26 +1,9 @@
-= Apache Isis - A Hands-on Tutorial
+= Petclinic
 
 :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 [...]
 
-This tutorial is an introduction to https://isis.apache.org[Apache Isis], and develops a variant of the venerable "pet clinic" demo app.
+This tutorial develops a variant of the venerable "pet clinic" demo app, broken up into a set of exercises.
 
-Each exercise has complete instructions to complete, and/or you can pull down the solutions from the companion github repo:
-
-[source,bash]
-----
-git clone https://github.com/apache/isis-app-demo
-----
-
-In the menu (on the left) you'll see that certain steps have a hand symbol and a number.
-These correspond to tags in the git repo, so you can pick up the tutorial wherever you wish.
-
-
-For more information:
-
-* link:http://isis.apache.org[isis.apache.org]
-
-* link:mailto:dan@haywood-associates.co.uk[dan@haywood-associates.co.uk]
-
-* twitter: @dkhaywood
+To guide you through those exercises, we've provided a git repo (https://github.com/apache/isis-app-demo) with a set of tags, each tag representing the solution to one exercise and the starting point to the next.
 
 
diff --git a/antora/components/tutorials/modules/petclinic/pages/getting-started.adoc b/antora/components/tutorials/modules/petclinic/pages/getting-started.adoc
deleted file mode 100644
index a763e20..0000000
--- a/antora/components/tutorials/modules/petclinic/pages/getting-started.adoc
+++ /dev/null
@@ -1,363 +0,0 @@
-= Getting Started
-
-: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 [...]
-
-== Prereqs
-
-You'll need:
-
-* Java 8
-+
-Apache Isis hasn't yet been tested for later versions of Java; the next major version of the framework will target Java 11.
-
-* Maven 3.x
-+
-Maven 3.5.0 or later is recommended (for its CI/CD support), but you'll get by okay with earlier versions.
-
-Also recommended:
-
-* a Java IDE with support for Maven.
-+
-The Apache Isis website has detailed documentation for using IntelliJ and Eclipse.
-
-* git
-+
-The tutorial includes worked example/solution, provided in a github-hosted repo.
-This has multiple tags for the various checkpoints.
-If you want to work from the solution, you'll need git to pull down the version.
-
-
-== Generate
-
-Use the "Hello World" Maven archetype to generate a running app (adjust `groupId` and `artifactId` as necessary):
-
-[source,bash]
-----
-mvn archetype:generate  \
-    -D archetypeGroupId=org.apache.isis.archetype \
-    -D archetypeArtifactId=helloworld-archetype \
-    -D archetypeVersion=1.16.2 \
-    -D groupId=com.mycompany \
-    -D artifactId=myapp \
-    -D version=1.0-SNAPSHOT \
-    -B
-
-cd myapp
-----
-
-Build and run:
-
-[source,bash]
-----
-mvn clean package jetty:run
-----
-
-and then browse to http://localhost:8080[]:
-
-image::{_imagesdir}/index.png[width="800px",link="_images/index.png"]
-
-
-
-== Explore the Generated App
-
-Although there's the archetype doesn't generated much code, there's still plenty to explore.
-
-=== Wicket viewer
-
-Follow the "wicket" link and logon using `sven/pass`.
-
-image::{_imagesdir}/home-page.png[width="800px",link="_images/home-page.png"]
-
-Use the "Hello World Objects" menu:
-
-image::{_imagesdir}/hello-world-objects-menu.png[width="300px",link="_images/hello-world-objects-menu.png"]
-
-to:
-
-
-* create new objects
-* search by name
-* list all objects
-+
-Note that "list all" is in italics.
-That's because this is a so-called "prototype" action; it's only available when running in prototype (development) mode.
-
-On an object:
-
-image::{_imagesdir}/object-a.png[width="800px",link="_images/object-a.png"]
-
-perform the following:
-
-* update its name
-* delete the object
-* create two objects with the same name
-
-Use the "Prototyping" menu:
-
-image::{_imagesdir}/prototyping-menu.png[width="300px",link="_images/prototyping-menu.png"]
-
-to access various built-in functionality.
-For example, use "HSQL DB Manager" to launch a Swing UI to browse the backend database (we use HSQL DB running in-memory when prototyping).
-Note that we'll be exploring the Swagger UI/REST API) below.
-
-Use the tertiary menu (with the user's name) to view the application's configuration settings and to logout:
-
-image::{_imagesdir}/tertiary-menu.png[width="250px",link="_images/tertiary-menu.png"]
-
-
-Use the bookmarks page (top left strip or `alt-[` ) to access previously visited domain objects.
-Use the drop-down menu on the bottom footer as an alternative mechanism.
-
-
-=== Restful Objects (Swagger)
-
-Navigate back to http://localhost:8080[], and follow the "swagger-ui" link.
-Enter `sven/pass` and "prototyping", then reload:
-
-image::{_imagesdir}/swagger-ui.png[width="800px",link="_images/swagger-ui.png"]
-
-Use the Swagger UI to interact with the domain objects previously created in the Wicket viewer.
-
-For example, follow link:http://localhost:8080/swagger-ui/index.html#!/myapp/get_services_myapp_HelloWorldObjects_actions_listAll_invoke[/services/myapp.HelloWorldObjects/actions/listAll/invoke] to list all domain objects.
-
-Use the "Response Content Type" drop-down to obtain different representations of the list.
-
-
-== Pull down github example/solution
-
-This tutorial has an accompanying github repo with multiple tags for the various checkpoints; the first tag is immediately after creating the archetype.
-We recommend that you pull this down so that - even if you code up all the steps yourself - you can easily get back to a working application if needs be.
-
-To pull down the example:
-
-[source,bash]
-----
-git clone https://github.com/danhaywood/isis-petclinic-tutorial
-----
-
-and then checkout the first tag:
-
-[source,bash]
-----
-git checkout tags/010-pull-down-github-example-solution
-mvn clean package jetty:run
-----
-
-You can now run the app.
-If necessary, switch into the "myapp" directory:
-
-[source,bash]
-----
-cd myapp
-----
-
-and run using:
-
-[source,bash]
-----
-mvn clean package jetty:run
-----
-
-
-== Set up your dev env
-
-Rather than running and editing from the Maven command line, we recommend that you load the application into an IDE and run from there.
-
-[TIP]
-====
-Admittedly, there is 5 or 10 minutes of setup required here.
-
-If you are short of time then you might want to skip this and instead just run the solutions by checking out the various tags and run using "mvn clean package jetty:run".
-
-Do though use an an editor that lets you easily locate files in the filesystem.
-====
-
-
-=== Loading the project
-
-Assuming that you _are_ going to use a mainstream IDE, the first bit is to load the files into the IDE.
-All of the mainstream IDEs make this easy to do: generally open Maven projects just by navigating to the `pom.xml`.
-For example, here's the generated app loaded into IntelliJ:
-
-image::{_imagesdir}/project-loaded-into-intellij.png[width="300px",link="_images/project-loaded-into-intellij.png"]
-
-=== Compiling the app
-
-DataNucleus includes an annotation processor, so make sure that annotation processing is enabled on the Maven project.
-
-The other very important thing you do need to know is that Apache Isis leverages DataNucleus for its ORM, and this uses bytecode enhancement rather than runtime proxies (it being an implementation of the JDO API as well as JPA).
-The DataNucleus bytecode enhancer runs after the compiler but (of course) before the app runs.
-
-* When using the Maven command line, the `datanucleus-maven-plugin` is bound to the `postCompile` phase.
-So, a simple "mvn package jetty:run" suffices to build the code, run the enhancer and to run the actual app.
-
-* When compiling with IntelliJ, there's nothing specific we need to configure to ensure that domain entity clases are enhanced.
-Because IntelliJ "watches" the filesystem for external changes, we can simply leverage Maven to perform the enhancement just prior to running the app.
-More on this in the next section.
-
-* When building in Eclipse, it is necessary to hook into the compile phase to ensure that the enhancement occurs; this is done by installing an Eclipse plugin which runs the datanucleus enhancer.
-+
-This is necessary because Eclipse -- unlike IntelliJ -- isn't designed to continually watch the filesystem; any changes to the class files must be made through its "internal" processes.
-+
-Nevertheless, datanucleus's Eclipse plugin generally works as well as the Maven plugin.
-
-For more info, see the Apache Isis developers' guide for more detailed instructions when using http://isis.apache.org/guides/dg/dg.html#_dg_ide_intellij[IntelliJ] or
-http://localhost:4000/guides/dg/dg.html#_dg_ide_eclipse[Eclipse].
-
-=== Running the app
-
-When running from the IDE, it's easiest to run using framework-provided bootstrap class, namely `org.apache.isis.WebServer`.
-This is just a regular application class (with a `main(...)` method) that uses Jetty to run the app (similar to the way in which Spring Boot works, for example).
-
-If using IntelliJ, we've found it easiest to set up a _run configuration_ with the "Before launch, run Maven goal" property set up to run the datanucleus enhancer:
-
-image::{_imagesdir}/intellij-run-configuration.png[width="800px",link="_images/intellij-run-configuration.png"]
-
-with
-
-image::{_imagesdir}/intellij-run-configuration-before-launch-datanucleus-enhance.png[width="400px",link="_images/intellij-run-configuration-before-launch-datanucleus-enhance.png"]
-
-The command being run here (in the appropriate directory) is simply:
-
-[source,bash]
-----
-mvn datanucleus:enhance -o
-----
-
-
-
-Alternatively the app can be deployed to an app server such as Tomcat (8.x); all the usual files in `src/main/webapp` are there.
-
-
-
-== Naked Objects pattern
-
-Apache Isis is an implementation of the _naked objects pattern_.
-This means that there's a direct mapping from the domain object model into the UI.
-We can explore this by looking at the domain entity and domain service generated by the archetype.
-
-[TIP]
-====
-An ORM such as DataNucleus or Hibernate maps domain objects into an RDBMS or other datastore.
-You can think of Apache Isis (and naked objects) similarly, but it's an OIM - an object _interface_ mapper.
-It maps to the UI layer rather than the persistence layer.
-
-Common to both ORMs and OIMs is an internal metamodel; this is where much of the power comes from.
-====
-
-
-Navigate to `HelloWorldObjects`; this is a singleton domain service automatically instantiated by the framework.
-It corresponds to the "Hello World Objects" menu.
-
-The menu items correspond to the actions of this class:
-
-image::{_imagesdir}/HelloWorldObjects.png[width="800px",link="_images/HelloWorldObjects.png"]
-
-
-The actions of that service are used to create and persist instances of `HelloWorldObject`.
-The structure and behaviour of this domain entity are similarly reflected:
-
-image::{_imagesdir}/HelloWorldObject.png[width="800px",link="_images/HelloWorldObject.png"]
-
-It's common for each entity (or more precisely, aggregate root) to have a corresponding domain service, acting as its repository.
-This abstraction hides the details of interacting with the persistence data store.
-Domain services are automatically injected wherever they are required, using `@javax.inject.Inject`.
-
-Apache Isis applications therefore generally follow the _hexagonal architecture_ (aka the _ports and adapters_ architecture).
-
-As well as writing our own domain services, there are also many framework-provided domain services, including `RepositoryService` (to persist objects) and `IsisJdoSupport` (for type-safe queries against the database).
-But this is just the tip of the iceberg; see Apache Isis documentation (specifically: link:http://isis.apache.org/guides/rgsvc/rgsvc.html[Reference Guide: Domain Services]) for the full list.
-
-If there is more than one implementation available, declare a `List<SomeDomainService>` and all of the available services will be injected.
-They are sorted by `@DomainService#menuOrder` attribute.
-
-It's also possible to override/decorate any of the framework-provided services; just implement the same type with a lower `@DomainService#menuOrder`.
-
-Domain services also act as extension points for the framework.
-For example, custom auditing and event publishing can be provided by providing an implementation of the appropriate SPI interface.
-
-
-== UI Hints
-
-The framework derives as much of the UI as possible from the domain objects' intrinsic structure and behaviour, but there are some supporting structures and conventions that are there primarily to improve the UI.
-
-
-=== Titles
-
-A title is the identifier of a domain object for the end-user.
-
-For `HelloWorldObject`, this is defined declaratively:
-
-[source,java]
-----
-@Title(prepend = "Object: ")
-private String name;
-----
-
-It can also be specified imperatively using either the `title()` or `toString()` method.
-
-*Mini-Exercise*:
-
-(No solution is provided for this exercise).
-
-* replace the `@Title` annotation with a `title()` method:
-+
-[source,java]
-----
-public String title() {
-    return "Object: " + getName();
-}
-----
-
-
-
-See link:http://isis.apache.org/guides/rgcms/rgcms.html#_rgcms_methods_reserved_title[Reference Guide: Classes, Methods and Schema] for more details.
-
-=== Object layout
-
-Frameworks that implement the _naked objects pattern_ automatically provide a default representation of domain objects.
-In many cases the details of that representation can be inferred directly from the domain members.
-For example the label of a field for an object's property (eg `HelloWorldObject#name`) can be derived directly from the name of the object property itself (`getName()`).
-
-In the absence of other metadata, Apache Isis will render a domain object with the properties to the left-hand side and the collections (if any) to the right.
-The order of these properties can be specified using the `@MemberOrder` annotation, and there are other annotations to group properties together and to associate action buttons with either properties or collections.
-
-The downside of using annotations is that changing the layout requires that the application be restarted, and certain more complex UIs, such as multi-columns or tab groups are difficult or impossible to express.
-
-Therefore Apache Isis also allows the layout of domain objects to be specified using a complementary layout file, eg `HelloWorldObject.layout.xml`.
-This is modelled upon bootstrap and so supports arbitrary rows and columns as well as tab groups and tabs.
-
-*Mini-Exercise*:
-
-* locate the `HelloWorldObject.layout.xml` file
-* compare the structure of the layout file to that of the rendered object
-* change the file, eg the relative widths of the columns
-* use the IDE to copy over the file to the classpath; the new version will be picked up automatically
-** for example, with IntelliJ use `Run > Reload Changed Classes`
-
-To learn more, see the link:http://isis.apache.org/guides/ugvw/ugvw.html#_ugvw_layout_file-based[Wicket viewer guide] (file-based layout).
-
-
-=== Icons
-
-Each domain object is associated with an icon.
-Typically this is static and in the same package as the class; see `HelloWorldObject.png`.
-
-
-=== menubars.layout.xml
-
-In a similar fashion, the actions of the various domain services are grouped into menus using the `menubars.layout.xml` file.
-
-*Mini-Exercise*:
-
-* locate the `menubars.layout.xml` file
-* compare the structure of the layout file to that of the rendered menu bar
-* change the file, eg reorder menu items or create new menus
-* again, use the IDE to copy over the file to the classpath
-** for example, with IntelliJ use `Run > Reload Changed Classes`
-
-
-To learn more, see the link:http://localhost:4000/guides/ugvw/ugvw.html#_ugvw_menubars-layout[Wicket viewer guide] (menu bars layout).
-
-
-
diff --git a/antora/components/tutorials/nav.adoc b/antora/components/tutorials/nav.adoc
deleted file mode 100644
index c0acc1d..0000000
--- a/antora/components/tutorials/nav.adoc
+++ /dev/null
@@ -1,73 +0,0 @@
-
-* xref:intro.adoc[Introduction]
-
-* xref:getting-started.adoc[Getting Started]
-** xref:getting-started.adoc#_prereqs[Prereqs]
-** xref:getting-started.adoc#_generate[Generate]
-** xref:getting-started.adoc#_explore_the_generated_app[Explore the generated app]
-** xref:getting-started.adoc#_pull_down_github_example_solution[image:hand.png[] *010*: Pull down the github example/solution]
-** xref:getting-started.adoc#_set_up_your_dev_env[Set up your dev env]
-** xref:getting-started.adoc#_naked_objects_pattern[Naked Objects pattern]
-** xref:getting-started.adoc#_ui_hints[UI Hints]
-
-* xref:an-example-domain.adoc[An Example Domain]
-** xref:an-example-domain.adoc#_rename_code_helloworldobject_code_to_code_owner_code[image:hand.png[] *020*: Rename HelloWorldObject to Owner]
-
-* xref:removing-boilerplatex.adoc[Removing Boilerplate]
-** xref:removing-boilerplatex.adoc#_removing_boilerplate_lombok[image:hand.png[] *030*: Lombok]
-** xref:removing-boilerplatex.adoc#_removing_boilerplate_parameter_names[image:hand.png[] *040*: Parameter Names]
-** xref:removing-boilerplatex.adoc#_disable_editing[image:hand.png[] *050*: Disable editing]
-** xref:removing-boilerplatex.adoc#_font_awesome_icons[Font awesome icons]
-** xref:removing-boilerplatex.adoc#_implicit_action_annotations[image:hand.png[] *060*: Implicit Action Annotations]
-
-* xref:fleshing-out-the-owner-entity.adoc[Fleshing out the Owner entity]
-** xref:fleshing-out-the-owner-entity.adoc#_rework_code_owner_code_s_name_code_firstname_code_and_code_lastname_code[image:hand.png[] *070*: Rework Owner's name (firstName and lastName)]
-** xref:fleshing-out-the-owner-entity.adoc#_derived_name_property[image:hand.png[] *080*: Derived name property]
-** xref:fleshing-out-the-owner-entity.adoc#_digression_changing_the_app_name[image:hand.png[] *090*: Digression: Changing the App Name]
-** xref:fleshing-out-the-owner-entity.adoc#_changing_the_object_type_class_alias[image:hand.png[] *100*: Changing the "Object Type" Class Alias]
-** xref:fleshing-out-the-owner-entity.adoc#_add_other_properties_for_code_owner_code[image:hand.png[] *110*: Add other properties for Owner]
-** xref:fleshing-out-the-owner-entity.adoc#_using_specifications_to_encapsulate_business_rules[image:hand.png[] *120*: Using specifications to encapsulate business rules]
-
-* xref:prototyping.adoc[Prototyping]
-** xref:prototyping.adoc#_fixture_scripts_for_owner[image:hand.png[] *130*: Fixture Scripts (for Owner)]
-** xref:prototyping.adoc#_run_with_a_different_manifest[image:hand.png[] *140*: Run with a different manifest]
-
-* xref:adding-the-remaining-classes.adoc[Adding the remaining classes]
-** xref:adding-the-remaining-classes.adoc#_newpet_action_and_code_pet_code_to_code_owner_code_association[image:hand.png[] *150*: `newPet` action, `Pet` to `Owner`]
-** xref:adding-the-remaining-classes.adoc#_collection_of_code_pet_code_s[image:hand.png[] *160*: Collection of Pets)]
-** xref:adding-the-remaining-classes.adoc#_extend_our_fixture[image:hand.png[] *170*: Extend our Fixtures]
-** xref:adding-the-remaining-classes.adoc#_adding_code_visit_code[image:hand.png[] *180*: Adding Visit]
-
-* xref:business-rules-and-unit-testing.adoc[Business Rules & (Unit) Testing]
-** xref:business-rules-and-unit-testing.adoc#_defaults_and_code_clockservice_code[image:hand.png[] *190*: Defaults, and ClockService]
-** xref:business-rules-and-unit-testing.adoc#_unit_tests[image:hand.png[] *200*: Unit Tests]
-** xref:business-rules-and-unit-testing.adoc#_validation[image:hand.png[] *210*: Validation]
-
-* xref:modularity.adoc[Modularity]
-** xref:modularity.adoc#_introducing_packages[image:hand.png[] *220*: Introducing Packages]
-** xref:modularity.adoc#_inverting_responsibilities_refactoring_the_code_pet_code_s_visits[image:hand.png[] *230*: Inverting responsibilities (Refactoring the Pet's visits)]
-** xref:modularity.adoc#_pet_s_visits_a_contributed_collection[image:hand.png[] *240*: Pet’s visits (a contributed collection)]
-** xref:modularity.adoc#_events[image:hand.png[] *250*: Events]
-
-* xref:view-models.adoc[View Models]
-** xref:view-models.adoc#_dashboard[image:hand.png[] *260*: Dashboard]
-
-* xref:integration-testing.adoc[(Integration) Testing]
-** xref:integration-testing.adoc#_an_improved_fixture_script[image:hand.png[] *270*: An improved Fixture Script]
-** xref:integration-testing.adoc#_writing_integration_tests[image:hand.png[] *280*: Writing Integration Tests]
-** xref:integration-testing.adoc#_factor_out_abstract_integration_test[image:hand.png[] *290*: Factor out abstract integration test]
-** xref:integration-testing.adoc#_move_teardowns_to_modules[image:hand.png[] *300*: Move teardowns to modules]
-** xref:integration-testing.adoc#_fake_data_service[image:hand.png[] *310*: Fake Data Service]
-** xref:integration-testing.adoc#_extend_the_fixture_script_to_set_up_visits[image:hand.png[] *320*: Extend the Fixture script to set up visits]
-
-* xref:adding-further-business-logic-worked-examples.adoc[Further business logic]
-** xref:adding-further-business-logic-worked-examples.adoc#_enter_an_outcome[image:hand.png[] *330*: Enter an outcome]
-** xref:adding-further-business-logic-worked-examples.adoc#_pay_for_a_visit[image:hand.png[] *340*: Pay for a visit]
-** xref:adding-further-business-logic-worked-examples.adoc#_prevent_payment_for_a_visit_twice[image:hand.png[] *350*: Prevent payment for a visit twice ("see it? use it? do it?")]
-** xref:adding-further-business-logic-worked-examples.adoc#_find_code_visit_code_s_not_yet_paid_and_overdue[image:hand.png[] *360*: Find Visits not yet paid and overdue]
-** xref:adding-further-business-logic-worked-examples.adoc#_digression_hiding_columns_in_tables[image:hand.png[] *370*: Digression: Hiding Columns in Tables]
-** xref:adding-further-business-logic-worked-examples.adoc#_another_digression_icons_and_css[image:hand.png[] *380*: Another Digression: Icons and CSS]
-** xref:adding-further-business-logic-worked-examples.adoc#_delete_an_code_owner_code_provided_no_unpaid_code_visit_code_s[image:hand.png[] *390*: Delete an Owner provided no unpaid Visits]
-
-//* xref:incode-platform.adoc[Incode Platform]
-//* xref:i18n.adoc[i18n]
diff --git a/antora/components/userguide/modules/fun/pages/ui/object-layout.adoc b/antora/components/userguide/modules/fun/pages/ui/object-layout.adoc
index 6b99ce5..314b657 100644
--- a/antora/components/userguide/modules/fun/pages/ui/object-layout.adoc
+++ b/antora/components/userguide/modules/fun/pages/ui/object-layout.adoc
@@ -251,6 +251,7 @@ All of the semantics in these layout annotations can also be specified in the `.
 In addition, xref:refguide:applib:index/annotation/ParameterLayout.adoc[@ParameterLayout] provides layout hints for action parameters.
 There is no way to specify these semantics in the `.layout.xml` file (action parameters are not enumerated in the file).
 
+[#layout-file-styles]
 === Layout file styles
 
 NOTE: TODO v2 - Style has been changed (WIP)
diff --git a/starters/adoc/modules/starters/pages/helloworld.adoc b/starters/adoc/modules/starters/pages/helloworld.adoc
index 321fcb2..d3cda92 100644
--- a/starters/adoc/modules/starters/pages/helloworld.adoc
+++ b/starters/adoc/modules/starters/pages/helloworld.adoc
@@ -505,13 +505,13 @@ This means:
 
 Most of the time you'll probably want to run the app from within your IDE.
 The mechanics of doing this will vary by IDE; see the xref:setupguide:ROOT:about.adoc[Setup Guide] for details of setting up Eclipse or IntelliJ IDEA.
-Basically, though, it amounts to running the `main()` method in the `HelloWorldApp`, but also (and this bit is important) ensuring that the xref:setupguide:hints-and-tips:about.adoc#datanucleus-enhancer[DataNucleus enhancer] has properly processed all domain entities.
+Basically, though, it amounts to running the `main()` method in the `HelloWorldApp` class.
 
 Here's what the setup looks like in IntelliJ IDEA:
 
 image::helloworld/helloworld.png[width="600px"]
 
-If using JDO as the ORM, then the DataNucleus enhancer must be run beforehand.
+If using JDO as the ORM (rather than JPA), then the DataNucleus enhancer must be run beforehand.
 With IntelliJ this can be done by setting up a different _Run Configuration_ to be executed beforehand:
 
 image::helloworld/helloworld-before-launch.png[width="600px"]
diff --git a/starters/adoc/modules/starters/pages/simpleapp.adoc b/starters/adoc/modules/starters/pages/simpleapp.adoc
index 38a9d9c..eead30d 100644
--- a/starters/adoc/modules/starters/pages/simpleapp.adoc
+++ b/starters/adoc/modules/starters/pages/simpleapp.adoc
@@ -54,7 +54,7 @@ To build the app from the latest stable release:
 +
 include::simpleapp-script-jdo.adoc[]
 
-if using JPA as the ORM:
+* if using JPA as the ORM:
 +
 include::simpleapp-script-jpa.adoc[]
 
@@ -64,6 +64,7 @@ This should only take a few seconds to download, compile and run.
 Then browse to link:http://localhost:8080[], and read on.
 
 
+[#using-the-app]
 == Using the App
 
 When you start the app, you'll be presented with a welcome page from which you can access the webapp using either the generic UI provided by xref:vw:ROOT:about.adoc[Web UI (Wicket viewer)] or use Swagger to access the xref:vro:ROOT:about.adoc[REST API (Restful Objects viewer)]:
@@ -652,7 +653,7 @@ Used for prototyping and also integration testing.
 
 <.> The `DomainAppDemo` is the fixture that was run xref:#fixtures[earlier on].
 
-<.> An implementation of the xref:refguide:applib:index/services/health/HealthCheckService.adoc[HealtCheckService].
+<.> An implementation of the xref:refguide:applib:index/services/health/HealthCheckService.adoc[HealthCheckService].
 This integrates with Spring Boot's link:https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/actuate/health/HealthIndicator.html[HealthIndicator] SPI, surfaced through the link:https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html[Spring Boot Actuator].
 
 <.> Annotated with xref:refguide:applib:index/annotation/HomePage.adoc[@HomePage] and so is shown automatically as the home page.
@@ -933,13 +934,13 @@ This means:
 
 Most of the time you'll probably want to run the app from within your IDE.
 The mechanics of doing this will vary by IDE; see the xref:setupguide:ROOT:about.adoc[Setup Guide] for details.
-Basically, though, it amounts to running the `main()` method in the `SimpleApp`, but also (and this bit is important) ensuring that the xref:setupguide:hints-and-tips:about.adoc#datanucleus-enhancer[DataNucleus enhancer] has properly processed all domain entities.
+Basically, though, it amounts to running the `main()` method in the `SimpleApp` class.
 
 Here's what the setup looks like in IntelliJ IDEA:
 
 image::simpleapp/simpleapp-webapp.png[width="600px"]
 
-If using JDO as the ORM, then the DataNucleus enhancer must be run beforehand.
+If using JDO as the ORM (rather than JPA), then the DataNucleus enhancer must be run beforehand.
 With IntelliJ this can be done by setting up a different _Run Configuration_ to be executed beforehand:
 
 image::simpleapp/simpleapp-webapp-before-launch.png[width="600px"]