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/28 05:33:39 UTC

[isis-app-demo] tag tags/01-01-starter-app created (now 0026087)

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

danhaywood pushed a change to tag tags/01-01-starter-app
in repository https://gitbox.apache.org/repos/asf/isis-app-demo.git.


      at 0026087  (commit)
This tag includes the following new commits:

     new 0026087  copies 2.0.0-M6 version of simpleapp

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[isis-app-demo] 01/01: copies 2.0.0-M6 version of simpleapp

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

danhaywood pushed a commit to tag tags/01-01-starter-app
in repository https://gitbox.apache.org/repos/asf/isis-app-demo.git

commit 0026087a655ff0865e532ea68adfbdadcf12c592
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Sep 28 06:28:50 2021 +0100

    copies 2.0.0-M6 version of simpleapp
---
 .gitattributes                                     |  51 +++++
 .github/workflows/build-and-test.yml               | 100 +++++++++
 .gitignore                                         |  45 ++++
 LICENSE                                            | 201 +++++++++++++++++
 README.adoc                                        | 244 +++++++++++++++++++++
 lib/README.txt                                     |   3 +
 lib/spring-instrument-5.3.8.jar                    | Bin 0 -> 7429 bytes
 module-simple-tests/.gitignore                     |   8 +
 module-simple-tests/log4j2-test.xml                |  60 +++++
 module-simple-tests/pom.xml                        |  69 ++++++
 .../modules/simple/dom/so/SimpleObject_Test.java   |  71 ++++++
 .../modules/simple/dom/so/SimpleObjects_Test.java  |  80 +++++++
 .../integtests/SimpleModuleIntegTestAbstract.java  |  50 +++++
 .../integtests/tests/SimpleObject_IntegTest.java   |  85 +++++++
 .../integtests/tests/SimpleObjects_IntegTest.java  |  95 ++++++++
 .../src/test/resources/application-test.yml        |   4 +
 module-simple/.gitignore                           |   8 +
 module-simple/pom.xml                              |  62 ++++++
 .../domainapp/modules/simple/SimpleModule.java     |  28 +++
 .../dom/so/SimpleObject#others.columnOrder.txt     |   2 +
 .../simple/dom/so/SimpleObject.columnOrder.txt     |   2 +
 .../modules/simple/dom/so/SimpleObject.java        | 140 ++++++++++++
 .../modules/simple/dom/so/SimpleObject.layout.xml  |  42 ++++
 .../modules/simple/dom/so/SimpleObject.png         | Bin 0 -> 653 bytes
 .../simple/dom/so/SimpleObjectRepository.java      |  13 ++
 .../modules/simple/dom/so/SimpleObjects.java       |  92 ++++++++
 .../simple/fixture/SimpleObjectBuilder.java        |  31 +++
 .../simple/fixture/SimpleObject_persona.java       |  47 ++++
 .../java/domainapp/modules/simple/types/Name.java  |  20 ++
 .../java/domainapp/modules/simple/types/Notes.java |  25 +++
 pom.xml                                            | 107 +++++++++
 update-parent.sh                                   |  15 ++
 webapp-tests/log4j2-test.xml                       |  70 ++++++
 webapp-tests/pom.xml                               |  54 +++++
 .../webapp/integtests/WebAppIntegTestAbstract.java |  55 +++++
 .../metamodel/SwaggerExport_IntegTest.java         |  34 +++
 .../metamodel/ValidateDomainModel_IntegTest.java   |  22 ++
 .../webapp/integtests/smoke/Smoke_IntegTest.java   |  92 ++++++++
 .../src/test/resources/application-test.yml        |   4 +
 .../src/test/resources/junit-platform.properties   |  17 ++
 webapp/pom.xml                                     | 196 +++++++++++++++++
 .../main/java/domainapp/webapp/AppManifest.java    |  47 ++++
 .../src/main/java/domainapp/webapp/SimpleApp.java  |  26 +++
 .../webapp/application/ApplicationModule.java      |  14 ++
 .../fixture/scenarios/DomainAppDemo.java           |  20 ++
 .../services/health/HealthCheckServiceImpl.java    |  33 +++
 .../services/homepage/HomePageViewModel.java       |  32 +++
 .../services/homepage/HomePageViewModel.layout.xml |  38 ++++
 .../services/homepage/HomePageViewModel.png        | Bin 0 -> 456 bytes
 .../java/domainapp/webapp/custom/CustomModule.java |  10 +
 .../webapp/custom/restapi/CustomController.java    |  47 ++++
 .../java/domainapp/webapp/quartz/QuartzModule.java |  41 ++++
 .../domainapp/webapp/quartz/job/SampleJob.java     |  55 +++++
 webapp/src/main/resources/application.yml          | 106 +++++++++
 webapp/src/main/resources/banner.txt               |   6 +
 .../config/application-SQLSERVER.properties        |  31 +++
 .../main/resources/config/application.properties   |  31 +++
 .../V2020.01.20.14.10__create_schema_simple.sql    |   8 +
 ....20.14.14__create_table_simple_SimpleObject.sql |  25 +++
 webapp/src/main/resources/log4j2-spring.xml        |  64 ++++++
 webapp/src/main/resources/menubars.layout.xml      |  82 +++++++
 webapp/src/main/resources/shiro.ini                |  53 +++++
 .../src/main/resources/static/css/application.css  |   0
 webapp/src/main/resources/static/css/page.css      |  27 +++
 .../static/images/apache-isis/logo-48x48.png       | Bin 0 -> 2622 bytes
 .../resources/static/images/apache-isis/logo.png   | Bin 0 -> 14160 bytes
 .../src/main/resources/static/images/favicon.png   | Bin 0 -> 2143 bytes
 webapp/src/main/resources/static/index.html        |  54 +++++
 .../main/resources/static/scripts/application.js   |   3 +
 webapp/src/main/resources/templates/error.html     |  20 ++
 webapp/translations.po                             |   0
 71 files changed, 3217 insertions(+)

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..b1eafb6
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,51 @@
+#
+#
+# text files are normalized (convert crlf => lf)
+# binary files are not normalized (binary is a macro for -text -diff)
+#
+#
+
+
+# Unless otherwise stated, assume text
+
+* text=auto
+
+
+*.java text diff=java
+*.html text diff=html
+*.xhtml text diff=html
+*.xml text
+*.txt text
+
+
+*.jar binary
+*.so binary
+*.dll binary
+
+# images
+*.jpg binary
+*.jpeg binary
+*.png binary
+*.pdn binary
+*.pdn binary
+
+
+*.cs     text diff=csharp
+
+*.sln    merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+*.doc	 diff=astextplain
+*.DOC	 diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF	 diff=astextplain
+*.rtf	 diff=astextplain
+*.RTF	 diff=astextplain
+
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
new file mode 100644
index 0000000..780b239
--- /dev/null
+++ b/.github/workflows/build-and-test.yml
@@ -0,0 +1,100 @@
+name: Maven Build and Test
+
+on:
+  workflow_dispatch:
+  push:
+    branches:
+      - jdo
+      - jpa
+      - jdo-SNAPSHOT
+      - jpa-SNAPSHOT
+  pull_request:
+    branches:
+      - jdo
+      - jpa
+      - jdo-SNAPSHOT
+      - jpa-SNAPSHOT
+
+jobs:
+  build:
+    name: Build, Test
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        java-version:
+          - 11
+          - 15
+
+    steps:
+    - name: Checkout repo
+      uses: actions/checkout@v2.3.3
+
+    - name: Set up JDK ${{ matrix.java-version }}
+      uses: actions/setup-java@v1
+      with:
+        java-version: ${{ matrix.java-version }}
+
+    - name: Print Maven Version
+      run: mvn --version
+
+    - name: Activate Cache for Maven Downloads
+      uses: actions/cache@v2
+      env:
+        cache-name: cache-maven-modules
+      with:
+        path: ~/.m2/repository
+        key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }}
+        restore-keys: |
+          ${{ runner.os }}-build-${{ env.cache-name }}-
+          ${{ runner.os }}-build-
+          ${{ runner.os }}-
+
+    - name: Calculate $REVISION
+      id: revision
+      shell: bash
+      run: |
+        if [[ $GITHUB_REF =~ .*-SNAPSHOT ]]
+        then
+          PARENT=$(curl -X GET "https://nexus.incode.work/service/rest/v1/search?sort=version&repository=nightly-builds&group=org.apache.isis.app&name=isis-app-starter-parent" -H "accept: application/json" -s | jq '.items[0].version' | sed 's/"//g')
+          echo "parentVersion = $PARENT"
+          mvn versions:update-parent -DparentVersion="[$PARENT]"
+        fi
+        BASELINE=$(cat pom.xml | grep "<version>" | head -1 | awk -F'>' '{print $2}' | awk -F'<' '{print $1}')
+        SHA_ID=$(echo $GITHUB_SHA | cut -c1-8)
+        REVISION=${BASELINE}.$(date +%Y%m%d-%H%M)-${SHA_ID}
+        echo "##[set-output name=revision;]${REVISION}"
+
+    - name: Share $REVISION as Environment Variable
+      run: echo "REVISION=${{ steps.revision.outputs.revision }}" >> $GITHUB_ENV
+
+    - name: Print $REVISION
+      shell: bash
+      run: |
+        echo ==============  ENV  =================
+        echo REVISION            \: $REVISION
+        echo ======================================
+
+    - name: Update Maven version
+      shell: bash
+      run: |
+        mvn versions:set -DnewVersion=$REVISION
+
+    - name: Print pom.xml
+      shell: bash
+      run: |
+        cat pom.xml
+
+    - name: Build with Maven
+      shell: bash
+      run: |
+        mvn install
+
+# uncomment and set environment variables to push to Docker registry
+#    - name: Build and Push Docker Image
+#      shell: bash
+#      run: |
+#        mvn -pl webapp -Ddocker jib:build
+#      env:
+#        DOCKER_REGISTRY_USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
+#        DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..568bbd5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,45 @@
+*~
+*.swp
+*.class
+bin/
+target/
+target-ide/
+logs/
+.settings/
+.project
+.classpath
+.idea
+*.iml
+
+build/
+
+JArchitectOut/
+*.jdproj
+
+neo4j_DB/
+
+# log files
+datanucleus.log
+isis.log
+i18n-po.log
+hs_err_pid*.log
+
+# Package Files #
+*.war
+*.ear
+
+dependency-reduced-pom.xml
+pom.xml.tag
+pom.xml.next
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+
+.clover/
+
+
+rebel.xml
+translations.pot
+
+.flattened-pom.xml
+
+create-db-schema.sql
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e6e77b0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 0000000..47808f9
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,244 @@
+= SimpleApp
+:toc:
+:toc-placement!:
+
+image:https://github.com/apache/isis-app-simpleapp/workflows/Build%20w/%20Maven%20+%20Jdk%208/badge.svg[]
+image:https://github.com/apache/isis-app-simpleapp/workflows/Build%20w/%20Maven%20+%20Jdk%2015/badge.svg[]
+
+This is a simple link:http://isis.apache.org[Apache Isis] application, structured so it can be used as a starting point for developing your own applications.
+
+[TIP]
+====
+If all you want is get a feel for what the framework is all about, then take a look at the link:https://github.com/apache/isis-app-helloworld[HelloWorld] starter app, which is even simpler.
+====
+
+toc::[]
+
+== Quick start
+
+* install prereqs:
+
+** Java 8 LTS (eg link:https://adoptopenjdk.net/[Adopt OpenJDK] distribution)
+** Maven 3.6 or later (http://maven.apache.org/download.cgi[download])
+* download and unzip
++
+[source,bash]
+----
+APP=simpleapp
+BRANCH=master
+
+REPO=isis-app-$APP
+curl "https://codeload.github.com/apache/$REPO/zip/$BRANCH" | jar xv
+mv $REPO-$BRANCH $REPO
+cd $REPO
+----
+
+* Build using Maven:
++
+[source,bash]
+----
+mvn clean install
+----
+
+* Run using Maven:
++
+[source,bash]
+----
+mvn -pl webapp spring-boot:run
+----
+
+* Browse to http://localhost:8080.
+
+* Login to using:
+
+** username: `sven`
+** password: `pass`
+
++
+The app runs with H2 running in-memory, with sample data set up using fixture scripts.
+
+* Build a Docker image
++
+[source,bash]
+----
+export REVISION=...                 #<.>
+export DOCKER_REGISTRY_USERNAME     #<.>
+export DOCKER_REGISTRY_PASSWORD     #<.>
+
+mvn -pl webapp jib:build
+----
+<.> used as the image tag
+<.> Docker Hub registry username
+<.> Docker Hub registry password
++
+To push to another container registry, change the `<image>` tag in the pom.xml
+
+== Application Structure
+
+The following table explains the contents of each of the directories:
+
+[width="100%",options="header,footer",stripes="none",cols="2a,4a"]
+|====================
+|Directory
+|Description
+
+|`module-simple`
+|Holds the "simple" module, consisting of the `SimpleObject` entity and supporting services.
+It also contains module-specific unit- and integration tests.
+
+[TIP]
+====
+Larger applications should consist of multiple modules; each such module can be copied from this starter module.
+====
+
+|`webapp`
+|Holds the bootstrapping classes, along with application-level scoped services and home page.
+It also contains application-wide integration tests/BDD specs, and lockdown tests.
+
+The `pom.xml` also provides goals to run the app from the command line, or to be assembled into a Docker image.
+
+|====================
+
+
+== Development
+
+Apache Isis uses DataNucleus as its ORM, which requires that any entities are "enhanced", a post-compile process.
+
+Normally this is done as part of a "mvn clean install", but the entities can also be enhanced explicity using:
+
+[source,bash]
+----
+mvn -pl module-simple datanucleus:enhance -o
+----
+
+This is useful to know if the application or integration test fails to bootstrap, complaining of "unenhanced entities".
+
+TIP: You can also use `enhance-all.sh`
+
+
+== Testing
+
+The application has three types of tests.
+Each of these generates its own set of tests, and each can be disabled through a system property.
+
+.Testing types
+[cols="5a,12a,6a,3a", options="header"]
+|===
+
+| Test type
+| Report
+| Phase
+| Skip using
+
+
+| Unit test
+| `target/surefire-unittest-reports`
+| `test`
+| `-DskipUTs`
+
+| Integ test
+| `target/surefire-integtest-reports`
+| `integration-test`
+| `-DskipITs`
+
+| BDD (Cucumber) specs
+| `target/surefire-integbddspecs-reports` and +
+`target/cucumber-html-reports/overview-features.html`
+
+| `integration-test`
+| `-DskipBS`
+
+
+|===
+
+
+These outputs can for example be processed within/published by a continuous pipeline pipeline.
+
+
+=== Lockdown Tests
+
+Lockdown tests capture the state of the system, so that changes to that system can be captured and confirmed.
+There are two lockdown tests:
+
+* the metamodel (serialized as XML)
++
+This is expected to change whenever the domain logic is changed, but generally should _not_ change when the Apache Isis framework is upgraded.
+
+* Maven dependencies (serialized as flat files)
++
+This should only change when dependencies are bumped or new dependencies added.
+
+The lockdown tests are implemented as link:https://approvaltests.com/[approval test]s, meaning that the current state is compared to an "approved" state.
+If there are no differences then the test passes.
+If there are differences then the test fails, and a diff is shown using any available `diff` tool.
+
+The lockdown tests are disabled by default, and are enabled with system properties.
+
+* to compare the current metamodel with the previously approved metamodel:
++
+[source,bash]
+----
+mvn integration-test -Dmetamodel.lockdown
+----
+
+* to compare the current Maven dependencies with the previously approved dependencies:
++
+[source,bash]
+----
+mvn test -Dmavendeps.lockdown
+----
+
+When there are differences, the current state is written to the `current` subdirectory.
+If the current state is approved, it should be copied over to the corresponding `approved` subdirectory.
+
+
+
+
+== Translations
+
+Apache Isis supports i18n using link:https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html[GNU .po file]s.
+The `WEB-INF/translations.po` is the fallback (an empty value means that the key is used "as-is"), while `WEB-INF/translations-XX.po` files provide translations for each "XX" locale.
+
+Translations are required for all domain classes and all members (actions, properties and collections) of all classes.
+This information is available from the metamodel, and so a new template `translations.po` is generated as a side-effect of running the integration tests (through a log4j2 logger).
+A good integration test to run is `ValidateDomainModel_IntegTest`.
+
+In addition, translations are required for any validation messages triggered by the test.
+Running an integration tests that trigger validations will result in these messages being captured as keys, for example `Smoke_IntegTest`.
+
+The generated file should be merged with any existing translations in `WEB-INF/translations.po`, and translations obtained for any new keys (there are numerous online services that support the format).
+
+
+== Flyway
+
+The application also demonstrates how to use Flyway to migrate the database schema.
+
+By default the app runs using an in-memory database.
+The Flyway example is activated using the "SQLSERVER" Spring Boot profile, eg:
+
+[source,bash]
+----
+mvn -Dspring.profiles.active=SQLSERVER -pl webapp install
+mvn -Dspring.profiles.active=SQLSERVER -pl webapp spring-boot:run
+----
+
+This causes the properties defined in  `config/application-SQLSERVER.properties` file to be used in preference to those in the default `config/application.properties` file.
+It defines the following:
+
+* `spring.flyway.url`, `spring.flyway.user` and `spring.flyway.password`
++
+The presence of these is enough to enable the Flyway integration
+
+* `spring.flyway.enabled`
++
+This is explicitly set to `true`, to override the value in the default `config/application.properties`.
+
+* `isis.persistence.jdo-datanucleus.impl.datanucleus.schema.autoCreateAll`
++
+This is set to `false`, also overriding the value in the default `config/application.properties`.
+It instructs the JDO/DataNucleus object store not to automatically create any tables etc.
+
+The Spring Boot profile is also used to add the dependency to the SQL Server driver is included (it is hidden behind a Maven profile).
+
+The prerequisites to try this out are a SQL Server database running on `localhost` and with the credentials as specified in `config/application-SQLSERVER.properties`; adjust as necessary.
+
diff --git a/lib/README.txt b/lib/README.txt
new file mode 100644
index 0000000..db3c441
--- /dev/null
+++ b/lib/README.txt
@@ -0,0 +1,3 @@
+If eclipselink.weaving=true, then run using JVM argument:
+
+ -javaagent:lib/spring-instrument-5.3.8.jar
diff --git a/lib/spring-instrument-5.3.8.jar b/lib/spring-instrument-5.3.8.jar
new file mode 100644
index 0000000..c0a0a9b
Binary files /dev/null and b/lib/spring-instrument-5.3.8.jar differ
diff --git a/module-simple-tests/.gitignore b/module-simple-tests/.gitignore
new file mode 100644
index 0000000..85cb54f
--- /dev/null
+++ b/module-simple-tests/.gitignore
@@ -0,0 +1,8 @@
+.gradle
+translations.pot
+*.jar
+gradle/wrapper
+!gradle-wrapper.jar
+/.apt_generated/
+/.factorypath
+/.apt_generated_tests/
diff --git a/module-simple-tests/log4j2-test.xml b/module-simple-tests/log4j2-test.xml
new file mode 100644
index 0000000..1d50129
--- /dev/null
+++ b/module-simple-tests/log4j2-test.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+    <Properties>
+        <Property name="PID">????</Property>
+        <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
+        <Property name="LOG_LEVEL_PATTERN">%5p</Property>
+        <Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
+        <Property name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+        <Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+    </Properties>
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT" follow="true">
+            <PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
+        </Console>
+    </Appenders>
+    <Loggers>
+        <Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
+        <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
+        <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
+        <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
+        <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
+        <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
+        <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
+        <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
+
+        <logger name="org.apache.directory" level="warn"/>
+        <logger name="org.apache.directory.api.ldap.model.entry.Value" level="off"/>
+
+        <logger name="DataNucleus.Persistence" level="info"/>
+        <logger name="DataNucleus.Transaction" level="info"/>
+        <logger name="DataNucleus.Datastore.Schema" level="info"/>
+        <logger name="DataNucleus.Datastore.Native" level="info"/>
+
+        <Root level="info">
+            <AppenderRef ref="Console" />
+        </Root>
+    </Loggers>
+</Configuration>
+
+<!-- # DataNucleus Logging Categories -->
+<!-- DataNucleus.Persistence - All messages relating to the persistence process -->
+<!-- DataNucleus.Transaction - All messages relating to transactions -->
+<!-- DataNucleus.Connection - All messages relating to Connections. -->
+<!-- DataNucleus.Query - All messages relating to queries -->
+<!-- DataNucleus.Cache - All messages relating to the DataNucleus Cache -->
+<!-- DataNucleus.MetaData - All messages relating to MetaData -->
+<!-- DataNucleus.Datastore - All general datastore messages -->
+<!-- DataNucleus.Datastore.Schema - All schema related datastore log messages -->
+<!-- DataNucleus.Datastore.Persist - All datastore persistence messages -->
+<!-- DataNucleus.Datastore.Retrieve - All datastore retrieval messages -->
+<!-- DataNucleus.Datastore.Native - Log of all 'native' statements sent to the datastore -->
+<!-- DataNucleus.General - All general operational messages -->
+<!-- DataNucleus.Lifecycle - All messages relating to object lifecycle changes -->
+<!-- DataNucleus.ValueGeneration - All messages relating to value generation -->
+<!-- DataNucleus.Enhancer - All messages from the DataNucleus Enhancer. -->
+<!-- DataNucleus.SchemaTool - All messages from DataNucleus SchemaTool -->
+<!-- DataNucleus.JDO - All messages general to JDO -->
+<!-- DataNucleus.JPA - All messages general to JPA -->
+<!-- DataNucleus.JCA - All messages relating to Connector JCA. -->
+<!-- DataNucleus.IDE - Messages from the DataNucleus IDE. -->
\ No newline at end of file
diff --git a/module-simple-tests/pom.xml b/module-simple-tests/pom.xml
new file mode 100644
index 0000000..93a3504
--- /dev/null
+++ b/module-simple-tests/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.isis.starters</groupId>
+        <artifactId>simpleapp-jpa</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simpleapp-jpa-module-simple-tests</artifactId>
+    <name>SimpleApp (JPA) - Simple Module (tests)</name>
+
+    <description>
+        Integ tests for 'simple module'
+    </description>
+
+    <build>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+            </testResource>
+            <testResource>
+                <filtering>false</filtering>
+                <directory>src/test/java</directory>
+                <includes>
+                    <include>**</include>
+                </includes>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+        </testResources>
+    </build>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>simpleapp-jpa-module-simple</artifactId>
+        </dependency>
+
+        <!-- TESTS -->
+
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-unittests</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+            	<exclusion>
+            		<groupId>org.jmock</groupId>
+            		<artifactId>jmock-junit4</artifactId>
+            	</exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-integtests</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObject_Test.java b/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObject_Test.java
new file mode 100644
index 0000000..922b077
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObject_Test.java
@@ -0,0 +1,71 @@
+package domainapp.modules.simple.dom.so;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.isis.applib.services.message.MessageService;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.applib.services.title.TitleService;
+
+@ExtendWith(MockitoExtension.class)
+class SimpleObject_Test {
+
+    @Mock TitleService mockTitleService;
+    @Mock MessageService mockMessageService;
+    @Mock RepositoryService mockRepositoryService;
+
+    SimpleObject object;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        object = SimpleObject.withName("Foo");
+        object.titleService = mockTitleService;
+        object.messageService = mockMessageService;
+        object.repositoryService = mockRepositoryService;
+    }
+
+    @Nested
+    public class updateName {
+
+        @Test
+        void happy_case() {
+            // given
+            assertThat(object.getName()).isEqualTo("Foo");
+
+            // when
+            object.updateName("Bar");
+
+            // then
+            assertThat(object.getName()).isEqualTo("Bar");
+        }
+
+    }
+    @Nested
+    class delete {
+
+        @Test
+        void happy_case() throws Exception {
+
+            // given
+            assertThat(object).isNotNull();
+
+            // expecting
+            when(mockTitleService.titleOf(object)).thenReturn("Foo");
+
+            // when
+            object.delete();
+
+            // then
+            verify(mockMessageService).informUser("'Foo' deleted");
+            verify(mockRepositoryService).removeAndFlush(object);
+        }
+    }
+}
\ No newline at end of file
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObjects_Test.java b/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObjects_Test.java
new file mode 100644
index 0000000..88eaeb0
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObjects_Test.java
@@ -0,0 +1,80 @@
+package domainapp.modules.simple.dom.so;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.stubbing.Answer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.when;
+
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.persistence.jpa.applib.services.JpaSupportService;
+
+@ExtendWith(MockitoExtension.class)
+class SimpleObjects_Test {
+
+    @Mock RepositoryService mockRepositoryService;
+    @Mock JpaSupportService mockJpaSupportService;
+    @Mock SimpleObjectRepository mockSimpleObjectRepository;
+
+    SimpleObjects objects;
+
+    @BeforeEach
+    public void setUp() {
+        objects = new SimpleObjects(mockRepositoryService, mockJpaSupportService, mockSimpleObjectRepository);
+    }
+
+    @Nested
+    class create {
+
+        @Test
+        void happyCase() {
+
+            // given
+            final String someName = "Foobar";
+
+            // expect
+            when(mockRepositoryService.persist(
+                    argThat((ArgumentMatcher<SimpleObject>) simpleObject -> Objects.equals(simpleObject.getName(), someName)))
+            ).then((Answer<SimpleObject>) invocation -> invocation.getArgument(0));
+
+            // when
+            final SimpleObject obj = objects.create(someName);
+
+            // then
+            assertThat(obj).isNotNull();
+            assertThat(obj.getName()).isEqualTo(someName);
+        }
+    }
+
+    @Nested
+    class ListAll {
+
+        @Test
+        void happyCase() {
+
+            // given
+            final List<SimpleObject> all = new ArrayList<>();
+
+            // expecting
+            when(mockSimpleObjectRepository.findAll())
+                .thenReturn(all);
+
+            // when
+            final List<SimpleObject> list = objects.listAll();
+
+            // then
+            assertThat(list).isEqualTo(all);
+        }
+    }
+}
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/SimpleModuleIntegTestAbstract.java b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/SimpleModuleIntegTestAbstract.java
new file mode 100644
index 0000000..a2b6476
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/SimpleModuleIntegTestAbstract.java
@@ -0,0 +1,50 @@
+package domainapp.modules.simple.integtests;
+
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.test.context.ActiveProfiles;
+
+import org.apache.isis.core.config.presets.IsisPresets;
+import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+import org.apache.isis.persistence.jpa.eclipselink.IsisModulePersistenceJpaEclipselink;
+import org.apache.isis.security.bypass.IsisModuleSecurityBypass;
+import org.apache.isis.testing.fixtures.applib.IsisIntegrationTestAbstractWithFixtures;
+import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
+
+import domainapp.modules.simple.SimpleModule;
+
+
+@SpringBootTest(
+        classes = SimpleModuleIntegTestAbstract.TestApp.class
+)
+@ActiveProfiles("test")
+public abstract class SimpleModuleIntegTestAbstract extends IsisIntegrationTestAbstractWithFixtures {
+
+    /**
+     * Compared to the production app manifest <code>domainapp.webapp.AppManifest</code>,
+     * here we in effect disable security checks, and we exclude any web/UI modules.
+     */
+    @SpringBootConfiguration
+    @EnableAutoConfiguration
+    @Import({
+
+            IsisModuleCoreRuntimeServices.class,
+            IsisModuleSecurityBypass.class,
+            IsisModulePersistenceJpaEclipselink.class,
+            IsisModuleTestingFixturesApplib.class,
+
+            SimpleModule.class
+    })
+    @PropertySources({
+            @PropertySource(IsisPresets.H2InMemory_withUniqueSchema),
+            @PropertySource(IsisPresets.UseLog4j2Test),
+    })
+    public static class TestApp {
+
+    }
+
+}
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObject_IntegTest.java b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObject_IntegTest.java
new file mode 100644
index 0000000..761bb86
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObject_IntegTest.java
@@ -0,0 +1,85 @@
+package domainapp.modules.simple.integtests.tests;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.isis.applib.services.wrapper.DisabledException;
+import org.apache.isis.applib.services.wrapper.InvalidException;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.fixture.SimpleObject_persona;
+import domainapp.modules.simple.integtests.SimpleModuleIntegTestAbstract;
+
+@Transactional
+public class SimpleObject_IntegTest extends SimpleModuleIntegTestAbstract {
+
+    SimpleObject simpleObject;
+
+    @BeforeEach
+    public void setUp() {
+        // given
+        simpleObject = fixtureScripts.runPersona(SimpleObject_persona.FOO);
+    }
+
+
+    @Nested
+    public static class name extends SimpleObject_IntegTest {
+
+        @Test
+        public void accessible() {
+            // when
+            final String name = wrap(simpleObject).getName();
+
+            // then
+            assertThat(name).isEqualTo(simpleObject.getName());
+        }
+
+        @Test
+        public void not_editable() {
+
+            // expect
+            assertThrows(DisabledException.class, ()->{
+
+                // when
+                wrap(simpleObject).setName("new name");
+            });
+        }
+
+    }
+
+    @Nested
+    public static class updateName extends SimpleObject_IntegTest {
+
+
+        @Test
+        public void can_be_updated_directly() {
+
+            // when
+            wrap(simpleObject).updateName("new name");
+            transactionService.flushTransaction();
+
+            // then
+            assertThat(wrap(simpleObject).getName()).isEqualTo("new name");
+        }
+
+        @Test
+        public void fails_validation() {
+
+            // expect
+            InvalidException cause = assertThrows(InvalidException.class, ()->{
+
+                // when
+                wrap(simpleObject).updateName("new name!");
+            });
+
+            // then
+            assertThat(cause.getMessage()).contains("Character '!' is not allowed");
+        }
+    }
+
+}
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObjects_IntegTest.java b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObjects_IntegTest.java
new file mode 100644
index 0000000..fdd26c2
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObjects_IntegTest.java
@@ -0,0 +1,95 @@
+package domainapp.modules.simple.integtests.tests;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.isis.testing.unittestsupport.applib.matchers.ThrowableMatchers;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+import domainapp.modules.simple.fixture.SimpleObject_persona;
+import domainapp.modules.simple.integtests.SimpleModuleIntegTestAbstract;
+
+@Transactional
+public class SimpleObjects_IntegTest extends SimpleModuleIntegTestAbstract {
+
+    @Inject
+    SimpleObjects menu;
+
+    @Nested
+    public static class listAll extends SimpleObjects_IntegTest {
+
+        @Test
+        public void happyCase() {
+
+            // given
+            fixtureScripts.run(new SimpleObject_persona.PersistAll());
+            transactionService.flushTransaction();
+
+            // when
+            final List<SimpleObject> all = wrap(menu).listAll();
+
+            // then
+            assertThat(all).hasSize(SimpleObject_persona.values().length);
+        }
+
+        @Test
+        public void whenNone() {
+
+            // when
+            final List<SimpleObject> all = wrap(menu).listAll();
+
+            // then
+            assertThat(all).hasSize(0);
+        }
+    }
+
+    @Nested
+    public static class create extends SimpleObjects_IntegTest {
+
+        @Test
+        public void happyCase() {
+
+            wrap(menu).create("Faz");
+
+            // then
+            final List<SimpleObject> all = wrap(menu).listAll();
+            assertThat(all).hasSize(1);
+        }
+
+        @Test
+        public void whenAlreadyExists() {
+
+            // given
+            fixtureScripts.runPersona(SimpleObject_persona.FIZZ);
+            transactionService.flushTransaction();
+
+            // expect
+            Throwable cause = assertThrows(Throwable.class, ()->{
+
+                // when
+                wrap(menu).create("Fizz");
+                transactionService.flushTransaction();
+
+            });
+
+            // also expect
+            MatcherAssert.assertThat(cause,
+                    ThrowableMatchers.causedBy(DuplicateKeyException.class));
+
+        }
+
+    }
+
+
+}
diff --git a/module-simple-tests/src/test/resources/application-test.yml b/module-simple-tests/src/test/resources/application-test.yml
new file mode 100644
index 0000000..3a68cae
--- /dev/null
+++ b/module-simple-tests/src/test/resources/application-test.yml
@@ -0,0 +1,4 @@
+isis:
+  persistence:
+    schema:
+      auto-create-schemas: "simple"
diff --git a/module-simple/.gitignore b/module-simple/.gitignore
new file mode 100644
index 0000000..85cb54f
--- /dev/null
+++ b/module-simple/.gitignore
@@ -0,0 +1,8 @@
+.gradle
+translations.pot
+*.jar
+gradle/wrapper
+!gradle-wrapper.jar
+/.apt_generated/
+/.factorypath
+/.apt_generated_tests/
diff --git a/module-simple/pom.xml b/module-simple/pom.xml
new file mode 100644
index 0000000..d295539
--- /dev/null
+++ b/module-simple/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.isis.starters</groupId>
+        <artifactId>simpleapp-jpa</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simpleapp-jpa-module-simple</artifactId>
+    <name>SimpleApp (JPA) - Simple Module</name>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <filtering>false</filtering>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**</include>
+                </includes>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </resource>
+        </resources>
+    </build>
+
+    <dependencies>
+
+        <!-- ISIS API -->
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-applib</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.persistence</groupId>
+            <artifactId>isis-persistence-jpa-eclipselink</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.testing</groupId>
+            <artifactId>isis-testing-fixtures-applib</artifactId>
+        </dependency>
+
+        <!-- IDE support (optional) -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/module-simple/src/main/java/domainapp/modules/simple/SimpleModule.java b/module-simple/src/main/java/domainapp/modules/simple/SimpleModule.java
new file mode 100644
index 0000000..e547259
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/SimpleModule.java
@@ -0,0 +1,28 @@
+package domainapp.modules.simple;
+
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixtures;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+
+@Configuration
+@ComponentScan
+@EnableJpaRepositories
+@EntityScan(basePackageClasses = {SimpleModule.class})
+public class SimpleModule implements ModuleWithFixtures {
+
+    @Override
+    public FixtureScript getTeardownFixture() {
+        return new FixtureScript() {
+            @Override
+            protected void execute(ExecutionContext executionContext) {
+                repositoryService.removeAll(SimpleObject.class);
+            }
+        };
+    }
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject#others.columnOrder.txt b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject#others.columnOrder.txt
new file mode 100644
index 0000000..ebc8999
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject#others.columnOrder.txt
@@ -0,0 +1,2 @@
+name
+id
\ No newline at end of file
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.columnOrder.txt b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.columnOrder.txt
new file mode 100644
index 0000000..dbf267a
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.columnOrder.txt
@@ -0,0 +1,2 @@
+name
+#version
\ No newline at end of file
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.java b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.java
new file mode 100644
index 0000000..094b99b
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.java
@@ -0,0 +1,140 @@
+package domainapp.modules.simple.dom.so;
+
+import java.util.Comparator;
+
+import javax.inject.Inject;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.annotation.PromptStyle;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Publishing;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
+import org.apache.isis.applib.services.message.MessageService;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.applib.services.title.TitleService;
+import org.apache.isis.persistence.jpa.applib.integration.IsisEntityListener;
+
+import static org.apache.isis.applib.annotation.SemanticsOf.IDEMPOTENT;
+import static org.apache.isis.applib.annotation.SemanticsOf.NON_IDEMPOTENT_ARE_YOU_SURE;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.val;
+
+import domainapp.modules.simple.types.Name;
+import domainapp.modules.simple.types.Notes;
+
+
+@javax.persistence.Entity
+@javax.persistence.Table(
+    schema="simple",
+    uniqueConstraints = {
+        @javax.persistence.UniqueConstraint(name = "SimpleObject__name__UNQ", columnNames = {"NAME"})
+    }
+)
+@javax.persistence.NamedQueries({
+        @javax.persistence.NamedQuery(
+                name = SimpleObject.NAMED_QUERY__FIND_BY_NAME_LIKE,
+                query = "SELECT so " +
+                        "FROM SimpleObject so " +
+                        "WHERE so.name LIKE :name"
+        )
+})
+@javax.persistence.EntityListeners(IsisEntityListener.class)
+@DomainObject(logicalTypeName = "simple.SimpleObject", entityChangePublishing = Publishing.ENABLED)
+@DomainObjectLayout()
+@NoArgsConstructor(access = AccessLevel.PUBLIC)
+@XmlJavaTypeAdapter(PersistentEntityAdapter.class)
+@ToString(onlyExplicitlyIncluded = true)
+public class SimpleObject implements Comparable<SimpleObject> {
+
+    static final String NAMED_QUERY__FIND_BY_NAME_LIKE = "SimpleObject.findByNameLike";
+
+    @javax.persistence.Id
+    @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
+    @javax.persistence.Column(name = "id", nullable = false)
+    private Long id;
+
+    @javax.persistence.Version
+    @javax.persistence.Column(name = "version", nullable = false)
+    @PropertyLayout(fieldSetId = "metadata", sequence = "999")
+    @Getter @Setter
+    private long version;
+
+    public static SimpleObject withName(String name) {
+        val simpleObject = new SimpleObject();
+        simpleObject.setName(name);
+        return simpleObject;
+    }
+
+    @Inject @javax.persistence.Transient RepositoryService repositoryService;
+    @Inject @javax.persistence.Transient TitleService titleService;
+    @Inject @javax.persistence.Transient MessageService messageService;
+
+
+
+    @Title
+    @Name
+    @javax.persistence.Column(length = Name.MAX_LEN, nullable = false)
+    @Getter @Setter @ToString.Include
+    @PropertyLayout(fieldSetId = "name", sequence = "1")
+    private String name;
+
+    @Notes
+    @javax.persistence.Column(length = Notes.MAX_LEN, nullable = true)
+    @Getter @Setter
+    @Property(commandPublishing = Publishing.ENABLED, executionPublishing = Publishing.ENABLED)
+    @PropertyLayout(fieldSetId = "name", sequence = "2")
+    private String notes;
+
+
+    @Action(semantics = IDEMPOTENT, commandPublishing = Publishing.ENABLED, executionPublishing = Publishing.ENABLED)
+    @ActionLayout(associateWith = "name", promptStyle = PromptStyle.INLINE)
+    public SimpleObject updateName(
+            @Name final String name) {
+        setName(name);
+        return this;
+    }
+    public String default0UpdateName() {
+        return getName();
+    }
+    public String validate0UpdateName(String newName) {
+        for (char prohibitedCharacter : "&%$!".toCharArray()) {
+            if( newName.contains(""+prohibitedCharacter)) {
+                return "Character '" + prohibitedCharacter + "' is not allowed.";
+            }
+        }
+        return null;
+    }
+
+
+    @Action(semantics = NON_IDEMPOTENT_ARE_YOU_SURE)
+    @ActionLayout(
+            associateWith = "name", position = ActionLayout.Position.PANEL,
+            describedAs = "Deletes this object from the persistent datastore")
+    public void delete() {
+        final String title = titleService.titleOf(this);
+        messageService.informUser(String.format("'%s' deleted", title));
+        repositoryService.removeAndFlush(this);
+    }
+
+
+
+    private final static Comparator<SimpleObject> comparator =
+            Comparator.comparing(SimpleObject::getName);
+
+    @Override
+    public int compareTo(final SimpleObject other) {
+        return comparator.compare(this, other);
+    }
+
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.layout.xml b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.layout.xml
new file mode 100644
index 0000000..46bd0ee
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.layout.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<bs3:grid xsi:schemaLocation="http://isis.apache.org/applib/layout/component http://isis.apache.org/applib/layout/component/component.xsd http://isis.apache.org/applib/layout/grid/bootstrap3 http://isis.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd" xmlns:c="http://isis.apache.org/applib/layout/component" xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+    <bs3:row>
+        <bs3:col span="12" unreferencedActions="true">
+            <c:domainObject bookmarking="AS_ROOT"/>
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="6">
+            <bs3:tabGroup>
+                <bs3:tab name="General">
+                    <bs3:row>
+                        <bs3:col span="12">
+                            <c:fieldSet id="name"/>
+                        </bs3:col>
+                    </bs3:row>
+                </bs3:tab>
+                <bs3:tab name="Metadata">
+                    <bs3:row>
+                        <bs3:col span="12">
+                            <c:fieldSet name="Metadata" id="metadata"/>
+                        </bs3:col>
+                    </bs3:row>
+                </bs3:tab>
+                <bs3:tab name="Other">
+                    <bs3:row>
+                        <bs3:col span="12">
+                            <c:fieldSet id="other" unreferencedProperties="true"/>
+                        </bs3:col>
+                    </bs3:row>
+                </bs3:tab>
+            </bs3:tabGroup>
+            <bs3:tabGroup>
+            </bs3:tabGroup>
+        </bs3:col>
+        <bs3:col span="6">
+            <bs3:tabGroup  unreferencedCollections="true">
+            </bs3:tabGroup>
+        </bs3:col>
+    </bs3:row>
+</bs3:grid>
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.png b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.png
new file mode 100644
index 0000000..0bd6f57
Binary files /dev/null and b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.png differ
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjectRepository.java b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjectRepository.java
new file mode 100644
index 0000000..77d34a1
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjectRepository.java
@@ -0,0 +1,13 @@
+package domainapp.modules.simple.dom.so;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface SimpleObjectRepository extends JpaRepository<SimpleObject, Long> {
+
+    List<SimpleObject> findByNameContaining(final String name);
+
+    SimpleObject findByName(final String name);
+
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjects.java b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjects.java
new file mode 100644
index 0000000..218d00b
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjects.java
@@ -0,0 +1,92 @@
+package domainapp.modules.simple.dom.so;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.persistence.TypedQuery;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.BookmarkPolicy;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.PromptStyle;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.query.Query;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.persistence.jpa.applib.services.JpaSupportService;
+
+import domainapp.modules.simple.types.Name;
+
+@DomainService(
+        nature = NatureOfService.VIEW,
+        logicalTypeName = "simple.SimpleObjects"
+)
+@javax.annotation.Priority(PriorityPrecedence.EARLY)
+@lombok.RequiredArgsConstructor(onConstructor_ = {@Inject} )
+public class SimpleObjects {
+
+    final RepositoryService repositoryService;
+    final JpaSupportService jpaSupportService;
+    final SimpleObjectRepository simpleObjectRepository;
+
+
+    @Action(semantics = SemanticsOf.NON_IDEMPOTENT)
+    @ActionLayout(promptStyle = PromptStyle.DIALOG_SIDEBAR)
+    public SimpleObject create(
+            @Name final String name) {
+        return repositoryService.persist(SimpleObject.withName(name));
+    }
+
+
+    @Action(semantics = SemanticsOf.NON_IDEMPOTENT)
+    @ActionLayout(promptStyle = PromptStyle.DIALOG_SIDEBAR)
+    public List<SimpleObject> findByNameLike(
+            @Name final String name) {
+        return repositoryService.allMatches(
+                Query.named(SimpleObject.class, SimpleObject.NAMED_QUERY__FIND_BY_NAME_LIKE)
+                     .withParameter("name", "%" + name + "%"));
+    }
+
+
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT, promptStyle = PromptStyle.DIALOG_SIDEBAR)
+    public List<SimpleObject> findByName(
+            @Name final String name
+            ) {
+        return simpleObjectRepository.findByNameContaining(name);
+    }
+
+
+    @Programmatic
+    public SimpleObject findByNameExact(final String name) {
+        return simpleObjectRepository.findByName(name);
+    }
+
+
+
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT)
+    public List<SimpleObject> listAll() {
+        return simpleObjectRepository.findAll();
+    }
+
+
+
+
+    @Programmatic
+    public void ping() {
+        jpaSupportService.getEntityManager(SimpleObject.class)
+            .ifSuccess(entityManager -> {
+                final TypedQuery<SimpleObject> q = entityManager.createQuery(
+                        "SELECT p FROM SimpleObject p ORDER BY p.name",
+                        SimpleObject.class)
+                    .setMaxResults(1);
+                q.getResultList();
+            });
+    }
+
+
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObjectBuilder.java b/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObjectBuilder.java
new file mode 100644
index 0000000..80223be
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObjectBuilder.java
@@ -0,0 +1,31 @@
+package domainapp.modules.simple.fixture;
+
+import javax.inject.Inject;
+
+import org.apache.isis.testing.fixtures.applib.personas.BuilderScriptWithResult;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+@Accessors(chain = true)
+public class SimpleObjectBuilder extends BuilderScriptWithResult<SimpleObject> {
+
+    @Getter @Setter
+    private String name;
+
+    @Override
+    protected SimpleObject buildResult(final ExecutionContext ec) {
+
+        checkParam("name", ec, String.class);
+
+        return wrap(simpleObjects).create(name);
+    }
+
+    // -- DEPENDENCIES
+
+    @Inject SimpleObjects simpleObjects;
+
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObject_persona.java b/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObject_persona.java
new file mode 100644
index 0000000..41a5aec
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObject_persona.java
@@ -0,0 +1,47 @@
+package domainapp.modules.simple.fixture;
+
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.testing.fixtures.applib.personas.PersonaWithBuilderScript;
+import org.apache.isis.testing.fixtures.applib.personas.PersonaWithFinder;
+import org.apache.isis.testing.fixtures.applib.setup.PersonaEnumPersistAll;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+public enum SimpleObject_persona
+implements PersonaWithBuilderScript<SimpleObjectBuilder>, PersonaWithFinder<SimpleObject> {
+
+    FOO("Foo"),
+    BAR("Bar"),
+    BAZ("Baz"),
+    FRODO("Frodo"),
+    FROYO("Froyo"),
+    FIZZ("Fizz"),
+    BIP("Bip"),
+    BOP("Bop"),
+    BANG("Bang"),
+    BOO("Boo");
+
+    private final String name;
+
+    @Override
+    public SimpleObjectBuilder builder() {
+        return new SimpleObjectBuilder().setName(name);
+    }
+
+    @Override
+    public SimpleObject findUsing(final ServiceRegistry serviceRegistry) {
+        SimpleObjects simpleObjects = serviceRegistry.lookupService(SimpleObjects.class).orElse(null);
+        return simpleObjects.findByNameExact(name);
+    }
+
+    public static class PersistAll
+    extends PersonaEnumPersistAll<SimpleObject_persona, SimpleObject> {
+
+        public PersistAll() {
+            super(SimpleObject_persona.class);
+        }
+    }
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/types/Name.java b/module-simple/src/main/java/domainapp/modules/simple/types/Name.java
new file mode 100644
index 0000000..8c3955c
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/types/Name.java
@@ -0,0 +1,20 @@
+package domainapp.modules.simple.types;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.Property;
+
+@Property(maxLength = Name.MAX_LEN)
+@Parameter(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;
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/types/Notes.java b/module-simple/src/main/java/domainapp/modules/simple/types/Notes.java
new file mode 100644
index 0000000..73525e5
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/types/Notes.java
@@ -0,0 +1,25 @@
+package domainapp.modules.simple.types;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Where;
+
+@Property(editing = Editing.ENABLED, maxLength = Notes.MAX_LEN)
+@PropertyLayout(named = "Notes", multiLine = 10, hidden = Where.ALL_TABLES)
+@Parameter(maxLength = Notes.MAX_LEN)
+@ParameterLayout(named = "Notes", multiLine = 10)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Notes {
+
+    int MAX_LEN = 4000;
+
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f35ab68
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.isis.app</groupId>
+        <artifactId>isis-app-starter-parent</artifactId>
+        <version>2.0.0-M6</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.apache.isis.starters</groupId>
+    <artifactId>simpleapp-jpa</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <name>SimpleApp (JPA) - Parent</name>
+
+    <packaging>pom</packaging>
+
+    <properties>
+        <isis.version>2.0.0-M6</isis.version>
+        <java.version>11</java.version>
+        <maven-cucumber-reporting.version>5.3.0</maven-cucumber-reporting.version>
+        <archunit.version>0.14.1</archunit.version>
+        <skipBDD>${skipTests}</skipBDD>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+
+            <!-- this project's own modules -->
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>simpleapp-jpa-module-simple</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>simpleapp-jpa-module-simple-tests</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>simpleapp-jpa-webapp</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>simpleapp-jpa-webapp-tests</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+                <type>test-jar</type>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <modules>
+        <module>module-simple</module>
+        <module>module-simple-tests</module>
+        <module>webapp</module>
+        <module>webapp-tests</module>
+    </modules>
+
+    <profiles>
+        <profile>
+            <id>staging</id>
+            <activation>
+                <property>
+                    <name>!skip.staging</name>
+                </property>
+            </activation>
+            <repositories>
+                <repository>
+                    <id>staging</id>
+                    <url>https://repository.apache.org/content/groups/staging/</url>
+                </repository>
+            </repositories>
+        </profile>
+        <profile>
+            <id>nightly</id>
+            <activation>
+                <property>
+                    <name>!skip.nightly</name>
+                </property>
+            </activation>
+            <repositories>
+                <repository>
+                    <id>nightly-builds</id>
+                    <url>https://nexus.incode.work/repository/nightly-builds/</url>
+                </repository>
+            </repositories>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/update-parent.sh b/update-parent.sh
new file mode 100644
index 0000000..de78ca7
--- /dev/null
+++ b/update-parent.sh
@@ -0,0 +1,15 @@
+
+if [ $# = 1 ]; then
+  PARENT=$1
+else
+  PARENT=$(curl -X GET "https://nexus.incode.work/service/rest/v1/search?sort=version&repository=nightly-builds&group=org.apache.isis.app&name=isis-app-starter-parent" -H "accept: application/json" -s | jq '.items[0].version' | sed 's/"//g')
+fi
+
+echo "parentVersion = $PARENT"
+mvn versions:update-parent -DparentVersion="[$PARENT]"
+
+CURR=$(grep "<isis.version>" pom.xml | head -1 | cut -d'>' -f2 | cut -d'<' -f1)
+sed -i "s|<isis.version>$CURR</isis.version>|<isis.version>$PARENT</isis.version>|g" pom.xml
+
+git add pom.xml
+git commit -m "updates parent pom to $PARENT"
diff --git a/webapp-tests/log4j2-test.xml b/webapp-tests/log4j2-test.xml
new file mode 100644
index 0000000..60e8974
--- /dev/null
+++ b/webapp-tests/log4j2-test.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+    <Properties>
+        <Property name="PID">????</Property>
+        <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
+        <Property name="LOG_LEVEL_PATTERN">%5p</Property>
+        <Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
+        <Property name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+        <Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+    </Properties>
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT" follow="true">
+            <PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
+        </Console>
+        <File name="TranslationsPoFile" fileName="translations.po" append="false" immediateFlush="true">
+            <PatternLayout>
+                <Pattern>%m%n</Pattern>
+            </PatternLayout>
+        </File>
+    </Appenders>
+    <Loggers>
+        <Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
+        <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
+        <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
+        <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
+        <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
+        <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
+        <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
+        <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
+
+        <logger name="org.apache.directory" level="warn"/>
+        <logger name="org.apache.directory.api.ldap.model.entry.Value" level="off"/>
+
+        <logger name="DataNucleus.Persistence" level="info"/>
+        <logger name="DataNucleus.Transaction" level="info"/>
+        <logger name="DataNucleus.Datastore.Schema" level="info"/>
+        <logger name="DataNucleus.Datastore.Native" level="info"/>
+
+        <Root level="info">
+            <AppenderRef ref="Console" />
+        </Root>
+
+        <logger name="org.apache.isis.core.runtimeservices.i18n.po.PoWriter" level="info">
+            <AppenderRef ref="TranslationsPoFile"/>
+        </logger>
+
+    </Loggers>
+</Configuration>
+
+<!-- # DataNucleus Logging Categories -->
+<!-- DataNucleus.Persistence - All messages relating to the persistence process -->
+<!-- DataNucleus.Transaction - All messages relating to transactions -->
+<!-- DataNucleus.Connection - All messages relating to Connections. -->
+<!-- DataNucleus.Query - All messages relating to queries -->
+<!-- DataNucleus.Cache - All messages relating to the DataNucleus Cache -->
+<!-- DataNucleus.MetaData - All messages relating to MetaData -->
+<!-- DataNucleus.Datastore - All general datastore messages -->
+<!-- DataNucleus.Datastore.Schema - All schema related datastore log messages -->
+<!-- DataNucleus.Datastore.Persist - All datastore persistence messages -->
+<!-- DataNucleus.Datastore.Retrieve - All datastore retrieval messages -->
+<!-- DataNucleus.Datastore.Native - Log of all 'native' statements sent to the datastore -->
+<!-- DataNucleus.General - All general operational messages -->
+<!-- DataNucleus.Lifecycle - All messages relating to object lifecycle changes -->
+<!-- DataNucleus.ValueGeneration - All messages relating to value generation -->
+<!-- DataNucleus.Enhancer - All messages from the DataNucleus Enhancer. -->
+<!-- DataNucleus.SchemaTool - All messages from DataNucleus SchemaTool -->
+<!-- DataNucleus.JDO - All messages general to JDO -->
+<!-- DataNucleus.JPA - All messages general to JPA -->
+<!-- DataNucleus.JCA - All messages relating to Connector JCA. -->
+<!-- DataNucleus.IDE - Messages from the DataNucleus IDE. -->
\ No newline at end of file
diff --git a/webapp-tests/pom.xml b/webapp-tests/pom.xml
new file mode 100644
index 0000000..6110fb2
--- /dev/null
+++ b/webapp-tests/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.isis.starters</groupId>
+        <artifactId>simpleapp-jpa</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simpleapp-jpa-webapp-tests</artifactId>
+    <name>SimpleApp (JPA) - Webapp (tests)</name>
+
+    <description>
+        Integ tests for entire app.
+    </description>
+
+    <packaging>jar</packaging>
+
+    <build>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+            </testResource>
+            <testResource>
+                <filtering>false</filtering>
+                <directory>src/test/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+        </testResources>
+    </build>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>simpleapp-jpa-webapp</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- TESTING -->
+
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-integtests</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/webapp-tests/src/test/java/domainapp/webapp/integtests/WebAppIntegTestAbstract.java b/webapp-tests/src/test/java/domainapp/webapp/integtests/WebAppIntegTestAbstract.java
new file mode 100644
index 0000000..5859b65
--- /dev/null
+++ b/webapp-tests/src/test/java/domainapp/webapp/integtests/WebAppIntegTestAbstract.java
@@ -0,0 +1,55 @@
+package domainapp.webapp.integtests;
+
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.test.context.ActiveProfiles;
+
+import org.apache.isis.core.config.presets.IsisPresets;
+import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+import org.apache.isis.persistence.jpa.eclipselink.IsisModulePersistenceJpaEclipselink;
+import org.apache.isis.security.bypass.IsisModuleSecurityBypass;
+import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
+import org.apache.isis.testing.integtestsupport.applib.IsisIntegrationTestAbstract;
+
+import domainapp.modules.simple.SimpleModule;
+import domainapp.webapp.application.ApplicationModule;
+
+@SpringBootTest(
+    classes = {
+            // we use a slightly different configuration compared to the production (AppManifest/webapp)
+            WebAppIntegTestAbstract.TestApp.class,
+            ApplicationModule.class,
+    }
+)
+@ActiveProfiles("test")
+public abstract class WebAppIntegTestAbstract extends IsisIntegrationTestAbstract {
+
+    /**
+     * Compared to the production app manifest <code>domainapp.webapp.AppManifest</code>,
+     * here we in effect disable security checks, and we exclude any web/UI modules.
+     */
+    @SpringBootConfiguration
+    @EnableAutoConfiguration
+    @EnableJpaRepositories
+    @Import({
+
+        IsisModuleCoreRuntimeServices.class,
+        IsisModuleSecurityBypass.class,
+        IsisModulePersistenceJpaEclipselink.class,
+        IsisModuleTestingFixturesApplib.class,
+
+        SimpleModule.class
+    })
+    @PropertySources({
+        @PropertySource(IsisPresets.H2InMemory_withUniqueSchema),
+        @PropertySource(IsisPresets.UseLog4j2Test),
+    })
+    public static class TestApp {
+
+    }
+}
diff --git a/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/SwaggerExport_IntegTest.java b/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/SwaggerExport_IntegTest.java
new file mode 100644
index 0000000..d2b345d
--- /dev/null
+++ b/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/SwaggerExport_IntegTest.java
@@ -0,0 +1,34 @@
+package domainapp.webapp.integtests.metamodel;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.annotation.DirtiesContext;
+
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.applib.services.swagger.Format;
+import org.apache.isis.applib.services.swagger.Visibility;
+import org.apache.isis.testing.integtestsupport.applib.swagger.SwaggerExporter;
+import org.apache.isis.viewer.restfulobjects.jaxrsresteasy4.IsisModuleViewerRestfulObjectsJaxrsResteasy4;
+
+import lombok.val;
+
+import domainapp.webapp.integtests.WebAppIntegTestAbstract;
+
+@Import({
+        IsisModuleViewerRestfulObjectsJaxrsResteasy4.class
+})
+@DirtiesContext
+class SwaggerExport_IntegTest extends WebAppIntegTestAbstract {
+
+    @Inject ServiceRegistry serviceRegistry;
+
+    @Test
+    void export() throws IOException {
+        val swaggerExporter = new SwaggerExporter(serviceRegistry);
+        swaggerExporter.export(Visibility.PRIVATE, Format.JSON);
+    }
+}
diff --git a/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/ValidateDomainModel_IntegTest.java b/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/ValidateDomainModel_IntegTest.java
new file mode 100644
index 0000000..84b9b0c
--- /dev/null
+++ b/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/ValidateDomainModel_IntegTest.java
@@ -0,0 +1,22 @@
+package domainapp.webapp.integtests.metamodel;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.testing.integtestsupport.applib.validate.DomainModelValidator;
+
+import domainapp.webapp.integtests.WebAppIntegTestAbstract;
+
+class ValidateDomainModel_IntegTest extends WebAppIntegTestAbstract {
+
+    @Inject ServiceRegistry serviceRegistry;
+
+    @Test
+    void validate() {
+        new DomainModelValidator(serviceRegistry).assertValid();
+    }
+
+
+}
diff --git a/webapp-tests/src/test/java/domainapp/webapp/integtests/smoke/Smoke_IntegTest.java b/webapp-tests/src/test/java/domainapp/webapp/integtests/smoke/Smoke_IntegTest.java
new file mode 100644
index 0000000..f8c5b56
--- /dev/null
+++ b/webapp-tests/src/test/java/domainapp/webapp/integtests/smoke/Smoke_IntegTest.java
@@ -0,0 +1,92 @@
+package domainapp.webapp.integtests.smoke;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.services.wrapper.InvalidException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.transaction.annotation.Transactional;
+
+import org.apache.isis.applib.services.xactn.TransactionService;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import domainapp.webapp.integtests.WebAppIntegTestAbstract;
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@Transactional
+class Smoke_IntegTest extends WebAppIntegTestAbstract {
+
+    @Inject SimpleObjects menu;
+    @Inject TransactionService transactionService;
+
+    @Test
+    void happy_case() {
+
+        // when
+        List<SimpleObject> all = wrap(menu).listAll();
+
+        // then
+        assertThat(all).isEmpty();
+
+
+        // when
+        final SimpleObject fred = wrap(menu).create("Fred");
+        transactionService.flushTransaction();
+
+        // then
+        all = wrap(menu).listAll();
+        assertThat(all).hasSize(1);
+        assertThat(all).contains(fred);
+
+
+        // when
+        final SimpleObject bill = wrap(menu).create("Bill");
+        transactionService.flushTransaction();
+
+        // then
+        all = wrap(menu).listAll();
+        assertThat(all).hasSize(2);
+        assertThat(all).contains(fred, bill);
+
+
+        // when
+        wrap(fred).updateName("Freddy");
+        transactionService.flushTransaction();
+
+        // then
+        assertThat(wrap(fred).getName()).isEqualTo("Freddy");
+
+
+        // when
+        wrap(fred).setNotes("These are some notes");
+        transactionService.flushTransaction();
+
+        // then
+        assertThat(wrap(fred).getNotes()).isEqualTo("These are some notes");
+
+
+        // when
+        Assertions.assertThrows(InvalidException.class, () -> {
+            wrap(fred).updateName("New name !!!");
+            transactionService.flushTransaction();
+        }, "Exclamation mark is not allowed");
+
+        // then
+        assertThat(wrap(fred).getNotes()).isEqualTo("These are some notes");
+
+
+        // when
+        wrap(fred).delete();
+        transactionService.flushTransaction();
+
+        // then
+        all = wrap(menu).listAll();
+        assertThat(all).hasSize(1);
+    }
+
+}
+
diff --git a/webapp-tests/src/test/resources/application-test.yml b/webapp-tests/src/test/resources/application-test.yml
new file mode 100644
index 0000000..3a68cae
--- /dev/null
+++ b/webapp-tests/src/test/resources/application-test.yml
@@ -0,0 +1,4 @@
+isis:
+  persistence:
+    schema:
+      auto-create-schemas: "simple"
diff --git a/webapp-tests/src/test/resources/junit-platform.properties b/webapp-tests/src/test/resources/junit-platform.properties
new file mode 100644
index 0000000..182483e
--- /dev/null
+++ b/webapp-tests/src/test/resources/junit-platform.properties
@@ -0,0 +1,17 @@
+# as per https://github.com/cucumber/cucumber-jvm/tree/master/junit-platform-engine#configuration-options
+cucumber.publish.quiet=true
+cucumber.filter.tags=not @backlog and not @ignore
+#we are using @Cucumber annotated classes and restrict classpath search to
+cucumber.glue=domainapp.webapp.bdd
+#we are using built-in reporting plugins
+cucumber.plugin=pretty, html:target/cucumber-reports/cucumber-report.html
+
+# WARNING:
+#
+# cucumber.plugin=..., json:target/cucumber-reports/cucumber-report.json, ...
+#
+# will cause an empty file to be created when running from mvn.
+#
+# this is why the maven configuration to execute cucumber using the CLI (antrun:run@cucumber-cli)
+# is configured to use --plugins json:target/cucumber-no-clobber.json
+#
\ No newline at end of file
diff --git a/webapp/pom.xml b/webapp/pom.xml
new file mode 100644
index 0000000..edc9b65
--- /dev/null
+++ b/webapp/pom.xml
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.isis.starters</groupId>
+        <artifactId>simpleapp-jpa</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simpleapp-jpa-webapp</artifactId>
+    <name>SimpleApp (JPA) - Webapp</name>
+
+    <description>
+    	Assembles and runs both the Wicket viewer and the Restfulobjects viewer
+    	in a single webapp configured to run using the JPA/EclipseLink object store.
+    </description>
+
+    <packaging>jar</packaging>
+
+    <build>
+        <resources>
+            <resource>
+                <filtering>true</filtering>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>application.yml</include>
+                </includes>
+            </resource>
+            <resource>
+                <filtering>false</filtering>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <filtering>false</filtering>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**</include>
+                </includes>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </resource>
+        </resources>
+        <plugins>
+            <!-- running: mvn spring-boot:run -->
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <classifier>exec</classifier>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>simpleapp-jpa-module-simple</artifactId>
+        </dependency>
+
+        <!-- isis -->
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-webapp</artifactId>
+            <type>pom</type>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.security</groupId>
+            <artifactId>isis-security-shiro</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.persistence</groupId>
+            <artifactId>isis-persistence-jpa-eclipselink</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-actuator-autoconfigure</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.testing</groupId>
+            <artifactId>isis-testing-h2console-ui</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.extensions</groupId>
+            <artifactId>isis-extensions-flyway-impl</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-quartz</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+
+        <profile>
+            <id>jdbc-sqlserver</id>
+            <activation>
+                <property>
+                    <name>spring.profiles.active</name>
+                    <value>SQLSERVER</value>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>com.microsoft.sqlserver</groupId>
+                    <artifactId>mssql-jdbc</artifactId>
+                    <version>7.4.1.jre8</version>
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <profile>
+            <id>jdbc-postgresql</id>
+            <activation>
+                <property>
+                    <name>spring.profiles.active</name>
+                    <value>POSTGRESQL</value>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.postgresql</groupId>
+		    		<artifactId>postgresql</artifactId>
+					<!-- <version>42.2.12</version> ... version already managed -->
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <profile>
+            <id>deploy-to-docker-io</id>
+            <activation>
+                <property>
+                    <name>docker</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>com.google.cloud.tools</groupId>
+                        <artifactId>jib-maven-plugin</artifactId>
+                        <configuration>
+                            <from>
+                                <image>adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.10_9</image>
+                            </from>
+                            <container>
+                                <jvmFlags>
+                                    <jvmFlag>-Xmx512m</jvmFlag>
+                                </jvmFlags>
+                                <mainClass>domainapp.webapp.SimpleApp</mainClass>
+                                <ports>
+                                    <port>8080</port>
+                                </ports>
+                            </container>
+                            <to>
+                                <image>docker.io/apacheisis/simpleapp</image>
+                                <tags>
+                                    <tag>${env.REVISION}</tag>
+                                </tags>
+                                <auth>
+                                    <username>${env.DOCKER_REGISTRY_USERNAME}</username>
+                                    <password>${env.DOCKER_REGISTRY_PASSWORD}</password>
+                                </auth>
+                            </to>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/webapp/src/main/java/domainapp/webapp/AppManifest.java b/webapp/src/main/java/domainapp/webapp/AppManifest.java
new file mode 100644
index 0000000..9dca47c
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/AppManifest.java
@@ -0,0 +1,47 @@
+package domainapp.webapp;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+
+import org.apache.isis.core.config.presets.IsisPresets;
+import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+import org.apache.isis.extensions.flyway.impl.IsisModuleExtFlywayImpl;
+import org.apache.isis.persistence.jpa.eclipselink.IsisModulePersistenceJpaEclipselink;
+import org.apache.isis.security.shiro.IsisModuleSecurityShiro;
+import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
+import org.apache.isis.testing.h2console.ui.IsisModuleTestingH2ConsoleUi;
+import org.apache.isis.viewer.restfulobjects.jaxrsresteasy4.IsisModuleViewerRestfulObjectsJaxrsResteasy4;
+import org.apache.isis.viewer.wicket.viewer.IsisModuleViewerWicketViewer;
+
+import domainapp.webapp.application.ApplicationModule;
+import domainapp.webapp.application.fixture.scenarios.DomainAppDemo;
+import domainapp.webapp.custom.CustomModule;
+import domainapp.webapp.quartz.QuartzModule;
+
+@Configuration
+@Import({
+        IsisModuleCoreRuntimeServices.class,
+        IsisModuleSecurityShiro.class,
+        IsisModulePersistenceJpaEclipselink.class,
+        IsisModuleViewerRestfulObjectsJaxrsResteasy4.class,
+        IsisModuleViewerWicketViewer.class,
+
+        IsisModuleTestingFixturesApplib.class,
+        IsisModuleTestingH2ConsoleUi.class,
+
+        IsisModuleExtFlywayImpl.class,
+
+        ApplicationModule.class,
+        CustomModule.class,
+        QuartzModule.class,
+
+        // discoverable fixtures
+        DomainAppDemo.class
+})
+@PropertySources({
+        @PropertySource(IsisPresets.DebugDiscovery),
+})
+public class AppManifest {
+}
diff --git a/webapp/src/main/java/domainapp/webapp/SimpleApp.java b/webapp/src/main/java/domainapp/webapp/SimpleApp.java
new file mode 100644
index 0000000..7dedb13
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/SimpleApp.java
@@ -0,0 +1,26 @@
+package domainapp.webapp;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Import;
+
+import org.apache.isis.core.config.presets.IsisPresets;
+
+@SpringBootApplication
+@Import({
+    AppManifest.class
+//    , XrayEnable.class
+})
+public class SimpleApp extends SpringBootServletInitializer {
+
+    /**
+     * @implNote this is to support the <em>Spring Boot Maven Plugin</em>, which auto-detects an
+     * entry point by searching for classes having a {@code main(...)}
+     */
+    public static void main(String[] args) {
+        IsisPresets.prototyping();
+        SpringApplication.run(new Class[] { SimpleApp.class }, args);
+    }
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/ApplicationModule.java b/webapp/src/main/java/domainapp/webapp/application/ApplicationModule.java
new file mode 100644
index 0000000..540c2e4
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/ApplicationModule.java
@@ -0,0 +1,14 @@
+package domainapp.webapp.application;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import domainapp.modules.simple.SimpleModule;
+
+@Configuration
+@Import(SimpleModule.class)
+@ComponentScan
+public class ApplicationModule {
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/fixture/scenarios/DomainAppDemo.java b/webapp/src/main/java/domainapp/webapp/application/fixture/scenarios/DomainAppDemo.java
new file mode 100644
index 0000000..3b18a98
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/fixture/scenarios/DomainAppDemo.java
@@ -0,0 +1,20 @@
+package domainapp.webapp.application.fixture.scenarios;
+
+import javax.inject.Inject;
+
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixturesService;
+
+import domainapp.modules.simple.fixture.SimpleObject_persona;
+
+public class DomainAppDemo extends FixtureScript {
+
+    @Override
+    protected void execute(final ExecutionContext ec) {
+        ec.executeChildren(this, moduleWithFixturesService.getTeardownFixture());
+        ec.executeChild(this, new SimpleObject_persona.PersistAll());
+    }
+
+    @Inject ModuleWithFixturesService moduleWithFixturesService;
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/services/health/HealthCheckServiceImpl.java b/webapp/src/main/java/domainapp/webapp/application/services/health/HealthCheckServiceImpl.java
new file mode 100644
index 0000000..4f16352
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/services/health/HealthCheckServiceImpl.java
@@ -0,0 +1,33 @@
+package domainapp.webapp.application.services.health;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.services.health.Health;
+import org.apache.isis.applib.services.health.HealthCheckService;
+
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@Service
+@Named("domainapp.HealthCheckServiceImpl")
+public class HealthCheckServiceImpl implements HealthCheckService {
+
+    private final SimpleObjects simpleObjects;
+
+    @Inject
+    public HealthCheckServiceImpl(SimpleObjects simpleObjects) {
+        this.simpleObjects = simpleObjects;
+    }
+
+    @Override
+    public Health check() {
+        try {
+            simpleObjects.ping();
+            return Health.ok();
+        } catch (Exception ex) {
+            return Health.error(ex);
+        }
+    }
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.java b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.java
new file mode 100644
index 0000000..bfb4dca
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.java
@@ -0,0 +1,32 @@
+package domainapp.webapp.application.services.homepage;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.annotation.HomePage;
+import org.apache.isis.applib.annotation.Nature;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@DomainObject(
+        nature = Nature.VIEW_MODEL,
+        logicalTypeName = "simple.HomePageViewModel"
+        )
+@HomePage
+@DomainObjectLayout()
+public class HomePageViewModel {
+
+    public String title() {
+        return getObjects().size() + " objects";
+    }
+
+    public List<SimpleObject> getObjects() {
+        return simpleObjects.listAll();
+    }
+
+    @Inject SimpleObjects simpleObjects;
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.layout.xml b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.layout.xml
new file mode 100644
index 0000000..72ba56e
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.layout.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<bs3:grid xsi:schemaLocation="http://isis.apache.org/applib/layout/component http://isis.apache.org/applib/layout/component/component.xsd http://isis.apache.org/applib/layout/grid/bootstrap3 http://isis.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd" xmlns="http://isis.apache.org/applib/layout/component" xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <bs3:row>
+        <bs3:col span="3">
+            <bs3:row>
+                <bs3:col span="12" unreferencedActions="true">
+                    <domainObject/>
+                    <action id="clearHints" hidden="EVERYWHERE"/>
+                    <action id="impersonate" hidden="EVERYWHERE"/>
+                    <action id="impersonateWithRoles" hidden="EVERYWHERE"/>
+                    <action id="stopImpersonating" hidden="EVERYWHERE"/>
+                    <action id="downloadLayoutXml" hidden="EVERYWHERE"/>
+                    <action id="inspectMetamodel" hidden="EVERYWHERE"/>
+                    <action id="rebuildMetamodel" hidden="EVERYWHERE"/>
+                    <action id="downloadMetamodelXml" hidden="EVERYWHERE"/>
+                    <action id="openRestApi" hidden="EVERYWHERE"/>
+                </bs3:col>
+            </bs3:row>
+        </bs3:col>
+        <bs3:col span="6" unreferencedCollections="true">
+            <bs3:row>
+                <bs3:col span="12">
+                    <collection id="objects" defaultView="table"/>
+                </bs3:col>
+            </bs3:row>
+        </bs3:col>
+        <bs3:col span="3">
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="0">
+            <fieldSet name="General" id="general" unreferencedProperties="true"/>
+        </bs3:col>
+    </bs3:row>
+</bs3:grid>
diff --git a/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.png b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.png
new file mode 100644
index 0000000..cb03785
Binary files /dev/null and b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.png differ
diff --git a/webapp/src/main/java/domainapp/webapp/custom/CustomModule.java b/webapp/src/main/java/domainapp/webapp/custom/CustomModule.java
new file mode 100644
index 0000000..9004096
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/custom/CustomModule.java
@@ -0,0 +1,10 @@
+package domainapp.webapp.custom;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ComponentScan
+public class CustomModule {
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/custom/restapi/CustomController.java b/webapp/src/main/java/domainapp/webapp/custom/restapi/CustomController.java
new file mode 100644
index 0000000..33c2ee5
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/custom/restapi/CustomController.java
@@ -0,0 +1,47 @@
+package domainapp.webapp.custom.restapi;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.applib.services.xactn.TransactionalProcessor;
+
+import lombok.RequiredArgsConstructor;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@RestController
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+class CustomController {
+
+    private final InteractionService interactionService;
+    private final TransactionalProcessor transactionalProcessor;
+    private final SimpleObjects simpleObjects;
+
+    @GetMapping("/custom/simpleObjects")
+    List<SimpleObject> all() {
+        return call("sven", simpleObjects::listAll)
+                .orElse(Collections.<SimpleObject>emptyList());
+    }
+
+    private <T> Optional<T> call(
+            final String username,
+            final Callable<T> callable) {
+
+        return interactionService.call(
+                InteractionContext.ofUserWithSystemDefaults(UserMemento.ofName(username)),
+                () -> transactionalProcessor.callWithinCurrentTransactionElseCreateNew(callable))
+                .optionalElseFail(); // re-throws exception that has occurred, if any
+    }
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/quartz/QuartzModule.java b/webapp/src/main/java/domainapp/webapp/quartz/QuartzModule.java
new file mode 100644
index 0000000..3220d64
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/quartz/QuartzModule.java
@@ -0,0 +1,41 @@
+package domainapp.webapp.quartz;
+
+import org.quartz.JobDetail;
+import org.quartz.SimpleTrigger;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.quartz.JobDetailFactoryBean;
+import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
+
+import lombok.val;
+
+import domainapp.webapp.quartz.job.SampleJob;
+
+@Configuration
+@ComponentScan
+public class QuartzModule {
+
+    private static final int REPEAT_INTERVAL_SECS = 60;
+    private static final int START_DELAY_SECS = 20;
+    private static final int MILLIS_PER_SEC = 1000;
+
+    @Bean
+    public JobDetailFactoryBean jobDetail() {
+        val jobDetailFactory = new JobDetailFactoryBean();
+        jobDetailFactory.setJobClass(SampleJob.class);
+        jobDetailFactory.setDescription("Invoke Sample Job service...");
+        jobDetailFactory.setDurability(true);
+        return jobDetailFactory;
+    }
+
+    @Bean
+    public SimpleTriggerFactoryBean trigger(JobDetail job) {
+        val trigger = new SimpleTriggerFactoryBean();
+        trigger.setJobDetail(job);
+        trigger.setStartDelay(START_DELAY_SECS * MILLIS_PER_SEC);
+        trigger.setRepeatInterval(REPEAT_INTERVAL_SECS * MILLIS_PER_SEC);
+        trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
+        return trigger;
+    }
+}
diff --git a/webapp/src/main/java/domainapp/webapp/quartz/job/SampleJob.java b/webapp/src/main/java/domainapp/webapp/quartz/job/SampleJob.java
new file mode 100644
index 0000000..d112ae9
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/quartz/job/SampleJob.java
@@ -0,0 +1,55 @@
+package domainapp.webapp.quartz.job;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.stereotype.Component;
+
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.applib.services.xactn.TransactionalProcessor;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@Component
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+@Log4j2
+public class SampleJob implements Job {
+
+    private final InteractionService interactionService;
+    private final TransactionalProcessor transactionalProcessor;
+    private final SimpleObjects simpleObjects;
+
+    @Override
+    public void execute(JobExecutionContext context) throws JobExecutionException {
+        final List<SimpleObject> all = all();
+        log.info("{} objects in the database", all.size());
+    }
+
+    List<SimpleObject> all() {
+        return call("sven", simpleObjects::listAll)
+                .orElse(Collections.<SimpleObject>emptyList());
+    }
+
+    private <T> Optional<T> call(
+            final String username,
+            final Callable<T> callable) {
+
+        return interactionService.call(
+                InteractionContext.ofUserWithSystemDefaults(UserMemento.ofName(username)),
+                () -> transactionalProcessor.callWithinCurrentTransactionElseCreateNew(callable))
+                .optionalElseFail(); // re-throws exception that has occurred, if any
+    }
+}
diff --git a/webapp/src/main/resources/application.yml b/webapp/src/main/resources/application.yml
new file mode 100644
index 0000000..01efdda
--- /dev/null
+++ b/webapp/src/main/resources/application.yml
@@ -0,0 +1,106 @@
+#
+# Recommend use for static configuration that does not change between environments.
+#
+# See also config/application.properties
+#
+isis:
+
+  applib:
+    annotation:
+      action:
+        explicit: true
+        command-publishing: ignore_safe
+        execution-publishing: all
+      action-layout:
+        css-class:
+          patterns:
+            delete.*:btn-danger,
+            discard.*:btn-warning,
+            remove.*:btn-warning
+      property:
+        command-publishing: all
+        execution-publishing: all
+      domain-object:
+        editing: false
+        entity-change-publishing: all
+
+  core:
+    meta-model:
+      introspector:
+        lock-after-full-introspection: true
+      validator:
+        allow-deprecated: false
+        no-params-only: true
+        explicit-object-type: true
+
+    runtime-services:
+      translation:
+        po:
+          mode: disabled
+
+  viewer:
+    wicket:
+      application:
+        about: Simple App
+        brand-logo-header: images/apache-isis/logo-48x48.png
+        css: css/application.css
+        favicon-url: images/favicon.png
+        js: scripts/application.js
+        menubars-layout-xml: menubars.layout.xml
+        name: Simple App
+        # https://stackoverflow.com/a/38983935/56880
+        version: @project.version@
+
+      credit:
+        - url:  http://isis.apache.org
+          image: images/apache-isis/logo-48x48.png
+          name: Apache Isis
+
+      bookmarked-pages:
+        show-chooser: true
+        show-drop-down-on-footer: true
+
+      max-title-length-in-standalone-tables: 0
+      max-title-length-in-parented-tables: 0
+
+      themes:
+        show-chooser: true
+
+  testing:
+    fixtures:
+      fixture-scripts-specification:
+        context-class: domainapp.webapp.application.fixture.scenarios.DomainAppDemo
+        run-script-default: domainapp.webapp.application.fixture.scenarios.DomainAppDemo
+        recreate: domainapp.webapp.application.fixture.scenarios.DomainAppDemo
+        non-persisted-objects-strategy: persist
+        multiple-execution-strategy: execute
+
+eclipselink:
+  # if enabled, then must be run with JVM arg:
+  # -javaagent:lib/spring-instrument-5.3.8.jar
+  weaving: false
+  # if weaving subproperties are required, then specify all eclipselink.weaving
+  # properties using application.properties instead (it's not possible to
+  # specify both eclipselink.weaving property and its subproperties using yaml syntax)
+
+resteasy:
+  jaxrs:
+    app:
+      registration: beans
+    defaultPath: "/restful"
+
+server:
+  max-http-header-size: 16KB
+
+spring:
+  banner:
+    location: banner.txt
+
+  quartz:
+    job-store-type: memory
+
+management:
+  endpoint:
+    health:
+      enabled: true
+
diff --git a/webapp/src/main/resources/banner.txt b/webapp/src/main/resources/banner.txt
new file mode 100644
index 0000000..1348a76
--- /dev/null
+++ b/webapp/src/main/resources/banner.txt
@@ -0,0 +1,6 @@
+     _                     _            ___     _
+    / \   _ __   __ _  ___| |__   ___  |_ _|___(_)___
+   / _ \ | '_ \ / _` |/ __| '_ \ / _ \  | |/ __| / __|
+  / ___ \| |_) | (_| | (__| | | |  __/  | |\__ \ \__ \
+ /_/   \_\ .__/ \__,_|\___|_| |_|\___| |___|___/_|___/
+         |_|
\ No newline at end of file
diff --git a/webapp/src/main/resources/config/application-SQLSERVER.properties b/webapp/src/main/resources/config/application-SQLSERVER.properties
new file mode 100644
index 0000000..07d8fe3
--- /dev/null
+++ b/webapp/src/main/resources/config/application-SQLSERVER.properties
@@ -0,0 +1,31 @@
+#
+# Recommend use for configuration that changes between environments.
+#
+# To override externally, see Spring Boot docs
+# https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
+#
+# See also /application.yml
+#
+
+#
+# to enable this profile use:
+#
+# -Dspring.profiles.active=SQLSERVER
+#
+# Prereq is an empty database called 'simpleapp', with user and password also 'simpleapp'
+#
+
+spring.flyway.enabled=true
+spring.flyway.default-schema=SIMPLE
+spring.flyway.schemas=SIMPLE
+
+spring.datasource.platform=sqlserver
+spring.datasource.url=jdbc:sqlserver://localhost;databaseName=simpleapp
+spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
+spring.datasource.username=simpleapp
+spring.datasource.password=simpleapp
+
+#isis.persistence.schema.create-schema-sql-template=   (use flyway instead)
+isis.persistence.schema.auto-create-schemas=
+
+
diff --git a/webapp/src/main/resources/config/application.properties b/webapp/src/main/resources/config/application.properties
new file mode 100644
index 0000000..8a7dcce
--- /dev/null
+++ b/webapp/src/main/resources/config/application.properties
@@ -0,0 +1,31 @@
+#
+# Recommend use for configuration that changes between environments.
+#
+# To override externally, see Spring Boot docs
+# https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
+#
+# See also /application.yml
+#
+
+
+spring.flyway.enabled=false
+
+spring.sql.init.platform=h2
+spring.datasource.url=jdbc:h2:mem:simple;DATABASE_TO_UPPER=false
+spring.datasource.driver-class-name=org.h2.Driver
+
+isis.persistence.schema.create-schema-sql-template=CREATE SCHEMA IF NOT EXISTS %s
+isis.persistence.schema.auto-create-schemas=simple
+
+#eclipselink.weaving=true
+eclipselink.deploy-on-startup=true
+eclipselink.ddl-generation.output-mode=both
+eclipselink.ddl-generation=create-tables
+eclipselink.create-ddl-jdbc-file-name=create-db-schema.sql
+eclipselink.application-location=.
+eclipselink.jpa.upper-case-column-names=false
+
+# uncomment to run during bootstrap
+#isis.testing.fixtures.initial-script = domainapp.webapp.application.fixture.scenarios.DomainAppDemo
+
+
diff --git a/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.10__create_schema_simple.sql b/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.10__create_schema_simple.sql
new file mode 100644
index 0000000..447a66f
--- /dev/null
+++ b/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.10__create_schema_simple.sql
@@ -0,0 +1,8 @@
+IF NOT EXISTS (
+    SELECT  1
+    FROM    sys.schemas
+    WHERE   name = N'simple'
+)
+    EXEC('CREATE SCHEMA [simple]');
+GO
+
diff --git a/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.14__create_table_simple_SimpleObject.sql b/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.14__create_table_simple_SimpleObject.sql
new file mode 100644
index 0000000..0d727cf
--- /dev/null
+++ b/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.14__create_table_simple_SimpleObject.sql
@@ -0,0 +1,25 @@
+SET ANSI_NULLS ON
+GO
+
+SET QUOTED_IDENTIFIER ON
+GO
+
+CREATE TABLE [simple].[SimpleObject](
+    [id]      [bigint]        IDENTITY(1,1) NOT NULL,
+    [name]    [varchar](40)                 NOT NULL,
+    [notes]   [varchar](4000)               NULL,
+    [version] [datetime2](7)                NOT NULL,
+
+    CONSTRAINT [SimpleObject_PK] PRIMARY KEY CLUSTERED
+        ([id] ASC)
+        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
+        ON [PRIMARY],
+
+    CONSTRAINT [SimpleObject_name_UNQ] UNIQUE NONCLUSTERED
+        ([name] ASC)
+        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
+        ON [PRIMARY]
+)
+ON [PRIMARY]
+GO
+
diff --git a/webapp/src/main/resources/log4j2-spring.xml b/webapp/src/main/resources/log4j2-spring.xml
new file mode 100644
index 0000000..fa5582e
--- /dev/null
+++ b/webapp/src/main/resources/log4j2-spring.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+    <Properties>
+        <Property name="PID">????</Property>
+        <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
+        <Property name="LOG_LEVEL_PATTERN">%5p</Property>
+        <Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
+        <Property name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+        <Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+    </Properties>
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT" follow="true">
+            <PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
+        </Console>
+    </Appenders>
+    <Loggers>
+
+        <!-- silence Wicket -->
+        <Logger name="org.apache.wicket.page.PartialPageUpdate" level="error" />
+
+        <Logger name="org.apache.isis.applib.services.publishing.log.CommandLogger" level="debug" />
+        <Logger name="org.apache.isis.applib.services.publishing.log.ExecutionLogger" level="debug" />
+        <Logger name="org.apache.isis.applib.services.publishing.log.EntityChangesLogger" level="debug" />
+        <Logger name="org.apache.isis.applib.services.publishing.log.EntityPropertyChangeLogger" level="debug" />
+
+        <Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
+        <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
+        <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
+        <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
+        <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
+        <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
+        <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
+        <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
+
+        <logger name="org.apache.directory" level="warn"/>
+        <logger name="org.apache.directory.api.ldap.model.entry.Value" level="off"/>
+
+        <logger name="DataNucleus.Persistence" level="info"/>
+        <logger name="DataNucleus.Transaction" level="info"/>
+        <logger name="DataNucleus.Datastore.Schema" level="info"/>
+        <logger name="DataNucleus.Datastore.Native" level="info"/>
+
+        <logger name="org.apache.isis.applib.services.publishing.log.CommandLogger" level="debug"/>
+        <logger name="org.apache.isis.applib.services.publishing.log.EntityChangesLogger" level="debug"/>
+        <logger name="org.apache.isis.applib.services.publishing.log.EntityPropertyChangeLogger" level="debug"/>
+        <logger name="org.apache.isis.applib.services.publishing.log.ExecutionLogger" level="debug"/>
+
+        <!-- request scoped -->
+        <logger name="org.apache.isis.viewer.restfulobjects.rendering.service.acceptheader.AcceptHeaderServiceForRest" level="debug"/>
+        <logger name="org.apache.isis.applib.services.command.CommandContext" level="debug"/>
+        <logger name="org.apache.isis.applib.services.iactn.InteractionProvider" level="debug"/>
+        <logger name="org.apache.isis.applib.services.scratchpad.Scratchpad" level="debug"/>
+        <logger name="org.apache.isis.core.runtimeservices.publish.PublisherDispatchServiceDefault" level="debug"/>
+
+        <!-- transaction scoped -->
+        <logger name="org.apache.isis.core.transaction.changetracking.EntityChangeTrackerDefault" level="debug"/>
+        <logger name="org.apache.isis.core.runtimeservices.publish.ExecutionPublisherDefault" level="debug"/>
+        <logger name="org.apache.isis.applib.services.queryresultscache.QueryResultsCache" level="debug"/>
+
+        <Root level="info">
+            <AppenderRef ref="Console" />
+        </Root>
+    </Loggers>
+</Configuration>
diff --git a/webapp/src/main/resources/menubars.layout.xml b/webapp/src/main/resources/menubars.layout.xml
new file mode 100644
index 0000000..b098222
--- /dev/null
+++ b/webapp/src/main/resources/menubars.layout.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<mb3:menuBars xsi:schemaLocation="http://isis.apache.org/applib/layout/menubars/bootstrap3 http://isis.apache.org/applib/layout/menubars/bootstrap3/menubars.xsd http://isis.apache.org/applib/layout/component http://isis.apache.org/applib/layout/component/component.xsd http://isis.apache.org/applib/layout/links http://isis.apache.org/applib/layout/links/links.xsd" xmlns:cpt="http://isis.apache.org/applib/layout/component" xmlns:lnk="http://isis.apache.org/applib/layout/links" xmlns:mb3="h [...]
+    <mb3:primary>
+        <mb3:menu>
+            <mb3:named>Simple Objects</mb3:named>
+            <mb3:section>
+                <mb3:serviceAction objectType="simple.SimpleObjects" id="create"/>
+                <mb3:serviceAction objectType="simple.SimpleObjects" id="findByName"/>
+                <mb3:serviceAction objectType="simple.SimpleObjects" id="findByNameLike"/>
+                <mb3:serviceAction objectType="simple.SimpleObjects" id="listAll"/>
+            </mb3:section>
+        </mb3:menu>
+        <mb3:menu unreferencedActions="true">
+            <mb3:named>Other</mb3:named>
+        </mb3:menu>
+    </mb3:primary>
+    <mb3:secondary>
+        <mb3:menu>
+            <mb3:named>Prototyping</mb3:named>
+            <mb3:section>
+                <mb3:named>Fixtures</mb3:named>
+                <mb3:serviceAction objectType="isis.testing.fixtures.FixtureScripts" id="runFixtureScript"/>
+                <mb3:serviceAction objectType="isis.testing.fixtures.FixtureScripts" id="recreateObjectsAndReturnFirst"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Layouts</mb3:named>
+                <mb3:serviceAction objectType="isis.applib.LayoutServiceMenu" id="downloadLayouts"/>
+                <mb3:serviceAction objectType="isis.applib.LayoutServiceMenu" id="downloadMenuBarsLayout"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Meta Model and Features</mb3:named>
+                <mb3:serviceAction objectType="isis.applib.MetaModelServiceMenu" id="downloadMetaModelXml"/>
+                <mb3:serviceAction objectType="isis.applib.MetaModelServiceMenu" id="downloadMetaModelCsv"/>
+                <mb3:serviceAction objectType="isis.applib.MetaModelServiceMenu" id="downloadMetaModelAscii"/>
+                <mb3:serviceAction objectType="isis.applib.MetaModelServiceMenu" id="downloadMetaModelDiff"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allNamespaces"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allTypes"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allActions"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allProperties"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allCollections"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Persistence</mb3:named>
+                <mb3:serviceAction objectType="isis.persistence.jdo.JdoMetamodelMenu" id="downloadMetamodels"/>
+                <mb3:serviceAction objectType="isis.ext.h2Console.H2ManagerMenu" id="openH2Console"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>REST API</mb3:named>
+                <mb3:serviceAction objectType="isis.viewer.restfulobjects.SwaggerServiceMenu" id="openSwaggerUi"/>
+                <mb3:serviceAction objectType="isis.viewer.restfulobjects.SwaggerServiceMenu" id="openRestApi"/>
+                <mb3:serviceAction objectType="isis.viewer.restfulobjects.SwaggerServiceMenu" id="downloadSwaggerSchemaDefinition"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>i18n</mb3:named>
+                <mb3:serviceAction objectType="isis.applib.TranslationServicePoMenu" id="downloadTranslations"/>
+                <mb3:serviceAction objectType="isis.applib.TranslationServicePoMenu" id="resetTranslationCache"/>
+                <mb3:serviceAction objectType="isis.applib.TranslationServicePoMenu" id="switchToReadingTranslations"/>
+                <mb3:serviceAction objectType="isis.applib.TranslationServicePoMenu" id="switchToWritingTranslations"/>
+            </mb3:section>
+        </mb3:menu>
+    </mb3:secondary>
+    <mb3:tertiary>
+        <mb3:menu>
+            <mb3:named/>
+            <mb3:section>
+                <mb3:named>Configuration</mb3:named>
+                <mb3:serviceAction objectType="isis.conf.ConfigurationMenu" id="configuration"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Impersonate</mb3:named>
+                <mb3:serviceAction objectType="isis.sudo.ImpersonateMenu" id="impersonate"/>
+                <mb3:serviceAction objectType="isis.sudo.ImpersonateMenu" id="impersonateWithRoles"/>
+                <mb3:serviceAction objectType="isis.applib.ImpersonateStopMenu" id="stopImpersonating"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Security</mb3:named>
+                <mb3:serviceAction objectType="isis.applib.UserMenu" id="me"/>
+                <mb3:serviceAction objectType="isis.security.LogoutMenu" id="logout"/>
+            </mb3:section>
+        </mb3:menu>
+    </mb3:tertiary>
+</mb3:menuBars>
diff --git a/webapp/src/main/resources/shiro.ini b/webapp/src/main/resources/shiro.ini
new file mode 100644
index 0000000..59e43ba
--- /dev/null
+++ b/webapp/src/main/resources/shiro.ini
@@ -0,0 +1,53 @@
+[main]
+
+# to use .ini file
+securityManager.realms = $iniRealm
+
+
+
+# -----------------------------------------------------------------------------
+# Users and their assigned roles
+#
+# Each line conforms to the format defined in the
+# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
+# -----------------------------------------------------------------------------
+
+[users]
+# user = password, role1, role2, role3, ...
+
+
+sven = pass, admin_role
+dick = pass, simple_role, default_role
+bob  = pass, simple_role, default_role, fixtures_role
+joe  = pass, simple_role, default_role
+guest = guest, guest_role, default_role
+
+
+
+# -----------------------------------------------------------------------------
+# Roles with assigned permissions
+#
+# Each line conforms to the format defined in the
+# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
+# -----------------------------------------------------------------------------
+
+[roles]
+# role = perm1, perm2, perm3, ...
+# perm in format: logicalTypeNamespace:logicalTypeSimpleName:memberName:r,w
+
+simple_role    = *:SimpleObjects:*:*,\
+                 *:SimpleObject:*:*
+guest_role     = *:SimpleObjects:findByName:*,\
+                 *:SimpleObjects:listAll:*,\
+                 *:SimpleObjects:*:r
+admin_role     = *
+default_role   = isis.applib,\
+                 isis.security
+fixtures_role  = isis.testing.fixtures
+features_role  = isis.feat
+metamodel_role = isis.metamodel
+h2_role        = isis.ext.h2Console
+jdo_role       = isis.persistence.jdo
+swagger_role   = isis.viewer.restfulobjects
+conf_role      = isis.conf
+sudo_role      = isis.sudo
diff --git a/webapp/src/main/resources/static/css/application.css b/webapp/src/main/resources/static/css/application.css
new file mode 100644
index 0000000..e69de29
diff --git a/webapp/src/main/resources/static/css/page.css b/webapp/src/main/resources/static/css/page.css
new file mode 100644
index 0000000..487d6a8
--- /dev/null
+++ b/webapp/src/main/resources/static/css/page.css
@@ -0,0 +1,27 @@
+body {
+    background-color: #1A467B;
+    font-family: Verdana, Helvetica, Arial, serif;
+    font-size: 90%;
+}
+
+li {
+    margin-top: 6px;
+    margin-bottom: 6px;
+}
+table {
+    border-collapse: collapse;
+}
+table, th, td {
+    border: 1px;
+    border-style: solid;
+    border-color: lightgray;
+}
+th, td {
+    padding: 10px;
+}
+#wrapper {
+    background-color: #ffffff;
+    width: 900px;
+    margin: 8px auto;
+    padding: 12px;
+}
diff --git a/webapp/src/main/resources/static/images/apache-isis/logo-48x48.png b/webapp/src/main/resources/static/images/apache-isis/logo-48x48.png
new file mode 100644
index 0000000..08e012c
Binary files /dev/null and b/webapp/src/main/resources/static/images/apache-isis/logo-48x48.png differ
diff --git a/webapp/src/main/resources/static/images/apache-isis/logo.png b/webapp/src/main/resources/static/images/apache-isis/logo.png
new file mode 100644
index 0000000..5284fe7
Binary files /dev/null and b/webapp/src/main/resources/static/images/apache-isis/logo.png differ
diff --git a/webapp/src/main/resources/static/images/favicon.png b/webapp/src/main/resources/static/images/favicon.png
new file mode 100644
index 0000000..2586589
Binary files /dev/null and b/webapp/src/main/resources/static/images/favicon.png differ
diff --git a/webapp/src/main/resources/static/index.html b/webapp/src/main/resources/static/index.html
new file mode 100644
index 0000000..5e8c12d
--- /dev/null
+++ b/webapp/src/main/resources/static/index.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+        <title>Apache Isis&trade; Simple App</title>
+
+        <link rel="stylesheet" type="text/css" href="css/page.css">
+    </head>
+    <body>
+        <div id="wrapper">
+            <img alt="Isis Logo" src="images/apache-isis/logo.png" />
+
+            <p>
+                This is a simple <a href="http://isis.apache.org">Apache Isis</a> application, structured so it can be
+                used as a starting point for developing your own applications.
+                <br/>
+            </p>
+
+            <p>To access the app:</p>
+            <ul>
+                <li>
+                    <p>
+                        <b><a href="wicket/">Generic UI (Wicket)</a></b>
+                    </p>
+                    <p>
+                        provides access to a generic UI for end-users.  This
+                        viewer is built with <a href="http://wicket.apache.org" target="_blank">Apache Wicket</a>&trade;.
+                    </p>
+                </li>
+                <li>
+                    <p>
+                        <b>
+                            <a href="swagger-ui/index.thtml">RESTful API (Swagger)</a>
+                        </b>
+                    </p>
+                    <p>
+                        provides access to a Swagger UI for convenient access
+                        to (a subset of) the automatically generated REST API.
+                    </p>
+                    <p>
+                        The full backend API (at <a href="restful/">restful/</a>) renders both simple and also richer
+                        hypermedia representations of domain objects, the latter conforming to the
+                        <a href="http://restfulobjects.org"  target="_blank">Restful Objects</a> spec.
+                    </p>
+                </li>
+            </ul>
+
+            <p>
+            The default user/password is <b><i>sven/pass</i></b>.
+            </p>
+
+        </div>
+    </body>
+</html>
diff --git a/webapp/src/main/resources/static/scripts/application.js b/webapp/src/main/resources/static/scripts/application.js
new file mode 100644
index 0000000..d8cf6fe
--- /dev/null
+++ b/webapp/src/main/resources/static/scripts/application.js
@@ -0,0 +1,3 @@
+$(document).ready(function() {
+	/// here...
+});
\ No newline at end of file
diff --git a/webapp/src/main/resources/templates/error.html b/webapp/src/main/resources/templates/error.html
new file mode 100644
index 0000000..f9d0a41
--- /dev/null
+++ b/webapp/src/main/resources/templates/error.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html xmlns:th="http://www.thymeleaf.org">
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+        <title>Apache Isis&trade; Simple App</title>
+
+        <link rel="stylesheet" type="text/css" th:href="@{/css/page.css}">
+    </head>
+    <body>
+        <div id="wrapper">
+            <img alt="Isis Logo" th:src="@{/images/apache-isis/logo.png}" />
+             
+            <p>
+                Our apologies - an error occurred.
+            </p>
+
+            <p>You can access the app <a th:href="@{/wicket/}">here</a>
+        </div>
+    </body>
+</html>
diff --git a/webapp/translations.po b/webapp/translations.po
new file mode 100644
index 0000000..e69de29