You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/09/01 13:27:19 UTC

[isis] branch master updated: ISIS-3167: major work on EntityFacets

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 836d8c9669 ISIS-3167: major work on EntityFacets
     new ae111b0b0d Merge remote-tracking branch 'origin/master'
836d8c9669 is described below

commit 836d8c9669c3a34e136d1f5a9767e82ae240ac98
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Sep 1 11:01:16 2022 +0200

    ISIS-3167: major work on EntityFacets
    
    - introducing a new EntityState: NEW
    - purging legacy ManageObject implementations
---
 .../applib/services/repository/EntityState.java    |  23 ++-
 .../facets/object/entity/EntityFacet.java          |  54 ++++++-
 .../object/entity/_EntityFacetForTesting.java      |   5 +-
 .../isis/core/metamodel/object/ManagedObject.java  | 102 ++++++++----
 .../core/metamodel/object/MmAssertionUtil.java     |  26 +++
 .../isis/core/metamodel/object/MmEntityUtil.java   |  82 ++--------
 .../isis/core/metamodel/object/MmSpecUtil.java     |  47 +++---
 .../isis/core/metamodel/object/Refetchable.java    |  27 ++++
 .../object/_ManagedObjectEntityAttached.java       | 156 ++++++++++++++++++
 ...tity.java => _ManagedObjectEntityDetached.java} |  56 ++++---
 .../object/_ManagedObjectEntityHybrid.java         | 144 +++++++++++++++++
 .../core/metamodel/object/_ManagedObjectOther.java |   7 +-
 .../metamodel/object/_ManagedObjectSpecified.java  |  24 ++-
 .../object/_ManagedObjectWithBookmark.java         | 174 ---------------------
 .../object/_ManagedObjectWithEagerSpec.java        |  85 ----------
 .../metamodel/objectmanager/ObjectManager.java     |  44 +-----
 .../detach/ObjectDetacher_builtinHandlers.java     |  20 +--
 .../identify/ObjectBookmarker_builtinHandlers.java |  54 +------
 .../load/ObjectLoader_builtinHandlers.java         |  14 +-
 .../query/ObjectBulkLoader_builtinHandlers.java    |   7 +-
 .../refresh/ObjectRefresher_builtinHandlers.java   |  20 +--
 .../isis/core/metamodel/spec/Hierarchical.java     |   5 +
 .../core/metamodel/spec/ObjectSpecification.java   |  23 ++-
 .../specimpl/ObjectSpecificationAbstract.java      |  35 +++++
 .../title/TitleAnnotationFacetFactoryTest.java     |   8 +-
 .../core/metamodel/object/ManagedObjectTest.java   |   4 +-
 .../command/SchemaValueMarshallerDefault.java      |   8 +-
 .../executor/MemberExecutorServiceDefault.java     |   4 +-
 .../factory/FactoryServiceDefault.java             |   8 +-
 .../repository/RepositoryServiceDefault.java       |   2 +-
 .../handlers/DomainObjectInvocationHandler.java    |   3 +-
 .../xmlsnapshot/XmlSnapshotBuilder.java            |   2 +-
 .../xmlsnapshot/XmlSnapshotServiceDefault.java     |   2 +-
 .../wkt/viewer/EventProviderAbstract.java          |   2 +-
 .../graphql/viewer/source/ObjectTypeFactory.java   |   2 +-
 .../graphql/viewer/source/QueryFieldFactory.java   |   7 +-
 .../viewer/vaadin/ui/binding/BindingsVaa.java      |   6 +-
 .../changetracking/JdoLifecycleListener.java       |  29 ++--
 .../jdo/datanucleus/changetracking/_Utils.java     |  21 +--
 .../entities/DnEntityStateProvider.java            |   9 +-
 .../metamodel/facets/entity/JdoEntityFacet.java    |  69 ++------
 .../jpa/integration/entity/JpaEntityFacet.java     |  49 ++----
 .../DomainModelTest_usingGoodDomain.java           |   4 +-
 .../testdomain/interact/SimulatedUiChoices.java    |  10 +-
 .../testdomain/interact/SimulatedUiComponent.java  |   2 +-
 .../domainmodel/jdo/DomainModelTest.java           |   3 +-
 .../injecting/jdo/JdoEntityInjectingTest.java      |   2 +-
 .../persistence/jpa/JpaBootstrappingTest.java      |   6 +-
 ...JpaNonGeneratedStringIdEntityLifecycleTest.java |   2 +
 .../springdata/SpringDataJpaBootstrappingTest.java |   3 +-
 .../isis/testdomain/value/ValueSemanticsTest.java  |   2 +-
 .../testdomain/value/ValueSemanticsTester.java     |   4 +-
 .../publishing/PublishingTestFactoryJdo.java       |   2 +-
 .../publishing/PublishingTestFactoryJpa.java       |   2 +-
 .../interaction/DomainObjectTesterFactory.java     |   9 +-
 .../binding/BindingConverterForManagedObject.java  |   2 +-
 .../viewer/wicket/model/models/BooleanModel.java   |   2 +-
 .../wicket/model/models/ManagedObjectModel.java    |   3 +-
 .../wicket/model/util/PageParameterUtils.java      |   2 +-
 .../components/tree/IsisToWicketTreeAdapter.java   |   2 +-
 .../viewer/services/DeepLinkServiceWicket.java     |   2 +-
 61 files changed, 779 insertions(+), 754 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/repository/EntityState.java b/api/applib/src/main/java/org/apache/isis/applib/services/repository/EntityState.java
index 1da34628ae..2ca6ae644b 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/repository/EntityState.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/repository/EntityState.java
@@ -38,6 +38,12 @@ public enum EntityState {
      * the database.
      */
     PERSISTABLE_ATTACHED,
+
+    /**
+     * DN/JDO specific on pre-store. Is attached, has no OID yet.
+     */
+    PERSISTABLE_NEW,
+
     /**
      * Object with this state is an entity but that is detached from a
      * persistence session, in other words changes to the entity will <i>not</i>
@@ -65,6 +71,12 @@ public enum EntityState {
     public boolean isAttached() {
         return this == PERSISTABLE_ATTACHED;
     }
+    /**
+     * DN/JDO specific on pre-store. Is attached, has no OID yet.
+     */
+    public boolean isNew() {
+        return this == PERSISTABLE_NEW;
+    }
     /**
      * Object with this state is an entity but that is detached from a
      * persistence session, in other words changes to the entity will <i>not</i>
@@ -95,9 +107,16 @@ public enum EntityState {
      * @apiNote 'removed' is only supported by JDO.
      */
     public boolean isAttachedOrRemoved() {
-        return this == PERSISTABLE_ATTACHED
-                || this == PERSISTABLE_REMOVED;
+        return isAttached()
+                || isRemoved();
     }
 
+    /**
+     * @apiNote 'new' is only supported by JDO.
+     */
+    public boolean isAttachedOrNew() {
+        return isAttached()
+                || isNew();
+    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/EntityFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/EntityFacet.java
index 548ba3b4f1..f3537fb078 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/EntityFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/EntityFacet.java
@@ -19,15 +19,21 @@
 package org.apache.isis.core.metamodel.facets.object.entity;
 
 import java.lang.reflect.Method;
+import java.util.Optional;
+
+import org.springframework.lang.Nullable;
 
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.repository.EntityState;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.beans.PersistenceStack;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.object.ManagedObject;
+import org.apache.isis.core.metamodel.object.MmSpecUtil;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 /**
  * Indicates that this class is managed by a persistence context.
@@ -35,9 +41,51 @@ import org.apache.isis.core.metamodel.object.ManagedObject;
  */
 public interface EntityFacet extends Facet {
 
-    String identifierFor(Object pojo);
+    /**
+     * The {@link ObjectSpecification} of the entity type this
+     * facet is associated with.
+     */
+    default ObjectSpecification getEntitySpecification() {
+        return (ObjectSpecification)getFacetHolder();
+    }
+
+    /**
+     * Optionally the stringified OID,
+     * based on whether the entity has one associated.
+     * @throws IllegalArgumentException if the pojo's class is not recognized
+     *      by the persistence layer
+     */
+    Optional<String> identifierFor(@Nullable Object pojo);
+
+    /**
+     * Optionally the {@link Bookmark},
+     * based on whether the entity has an OID associated.
+     * eg. it has not if not persisted yet
+     * @throws IllegalArgumentException if the pojo's class is not recognized
+     *      by the persistence layer or does not exactly match the expected
+     */
+    default Optional<Bookmark> bookmarkFor(final @Nullable Object pojo) {
+        return identifierFor(pojo)
+                .map(id->Bookmark.forLogicalTypeAndIdentifier(
+                        MmSpecUtil.quicklyResolveObjectSpecificationFor(
+                                getEntitySpecification(),
+                                pojo.getClass())
+                        .getLogicalType(),
+                        id));
+    }
+
+    default Bookmark bookmarkForElseFail(final @Nullable Object pojo) {
+        return bookmarkFor(pojo)
+                .orElseThrow(()->_Exceptions.noSuchElement("entity has no OID: %s",
+                        getEntitySpecification().getLogicalType()));
+    }
 
-    ManagedObject fetchByIdentifier(Bookmark bookmark);
+
+    /**
+     * Optionally the entity pojo corresponding to given {@link Bookmark},
+     * based on whether could be found.
+     */
+    Optional<Object> fetchByBookmark(Bookmark bookmark);
 
     Can<ManagedObject> fetchByQuery(Query<?> query);
 
@@ -67,4 +115,6 @@ public interface EntityFacet extends Facet {
         return new _EntityFacetForTesting(persistenceStandard, facetHolder);
     }
 
+
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/_EntityFacetForTesting.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/_EntityFacetForTesting.java
index d4d1239f0b..97289b4ea2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/_EntityFacetForTesting.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/_EntityFacetForTesting.java
@@ -19,6 +19,7 @@
 package org.apache.isis.core.metamodel.facets.object.entity;
 
 import java.lang.reflect.Method;
+import java.util.Optional;
 import java.util.function.BiConsumer;
 
 import org.apache.isis.applib.query.Query;
@@ -55,12 +56,12 @@ class _EntityFacetForTesting implements EntityFacet {
     }
 
     @Override
-    public String identifierFor(final Object pojo) {
+    public Optional<String> identifierFor(final Object pojo) {
         throw _Exceptions.unsupportedOperation();
     }
 
     @Override
-    public ManagedObject fetchByIdentifier(final Bookmark bookmark) {
+    public Optional<Object> fetchByBookmark(final Bookmark bookmark) {
         throw _Exceptions.unsupportedOperation();
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
index df2861b712..8997ef491a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java
@@ -24,9 +24,8 @@ import java.util.function.Supplier;
 import org.springframework.lang.Nullable;
 
 import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.repository.EntityState;
 import org.apache.isis.commons.collections.Can;
-import org.apache.isis.commons.internal.assertions._Assert;
-import org.apache.isis.commons.internal.collections._Collections;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.HasMetaModelContext;
 import org.apache.isis.core.metamodel.facets.object.icon.ObjectIcon;
@@ -41,9 +40,9 @@ import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
 /**
- * Represents an instance of some element of the meta-model managed by the framework,
- * that is <i>Spring</i> managed beans, persistence-stack provided entities, view-models
- * or instances of value types.
+ * Represents an instance of some element of the meta-model recognized by the framework,
+ * that is <i>Spring</i> managed beans, persistence-stack provided entities, view-models,
+ * mixins or instances of value types.
  *
  * @since 2.0 {@index}}
  *
@@ -325,6 +324,11 @@ extends
      */
     Object getPojo();
 
+    @NonNull
+    default EntityState getEntityState() {
+        return EntityState.NOT_PERSISTABLE;
+    }
+
     /**
      * If the underlying domain object is a viewmodel, refreshes any referenced entities.
      * (Acts as a no-op otherwise.)
@@ -434,23 +438,48 @@ extends
             final @Nullable Object pojo,
             final Optional<Bookmark> bookmarkIfKnown) {
         return pojo != null
-                ? bookmarkIfKnown.map(bookmark->bookmarked(spec, pojo, bookmark)) //FIXME
-                        .orElseGet(()->new _ManagedObjectWithEagerSpec(spec, pojo)) //FIXME
+                ? new _ManagedObjectViewmodel(spec, pojo, bookmarkIfKnown)
                 : empty(spec);
     }
     /**
      * ENTITY
      * @param spec - required
      * @param pojo - if <code>null</code> maps to {@link #empty(ObjectSpecification)}
+     * @param bookmark
      * @see ManagedObject.Specialization.TypePolicy#EXACT_TYPE_REQUIRED
      * @see ManagedObject.Specialization.BookmarkPolicy#IMMUTABLE
      * @see ManagedObject.Specialization.PojoPolicy#REFETCHABLE
      */
     static ManagedObject entity(
+            final @NonNull ObjectSpecification spec,
+            final @Nullable Object pojo,
+            final @NonNull Optional<Bookmark> bookmarkIfKnown) {
+        if(pojo == null) {
+            return empty(spec);
+        }
+        val bookmarkIfAny = bookmarkIfKnown
+                .or(()->spec.entityFacetElseFail().bookmarkFor(pojo));
+        if(bookmarkIfAny.isPresent()) {
+            return entityAttached(spec, pojo, bookmarkIfAny);
+        } else {
+            return entityDetached(spec, pojo);
+        }
+    }
+    //FIXME java-doc
+    static ManagedObject entityAttached(
+            final @NonNull ObjectSpecification spec,
+            final @NonNull Object pojo,
+            final @NonNull Optional<Bookmark> bookmarkIfKnown) {
+        return new _ManagedObjectEntityHybrid(
+                        new _ManagedObjectEntityAttached(spec, pojo, bookmarkIfKnown));
+    }
+    //FIXME java-doc
+    static ManagedObject entityDetached(
             final @NonNull ObjectSpecification spec,
             final @Nullable Object pojo) {
         return pojo != null
-                ? new _ManagedObjectWithEagerSpec(spec, pojo) //FIXME
+                ? new _ManagedObjectEntityHybrid(
+                        new _ManagedObjectEntityDetached(spec, pojo))
                 : empty(spec);
     }
     /**
@@ -502,23 +531,44 @@ extends
      * @param specLoader - required
      * @param pojo - required, required non-scalar
      */
-    static ManagedObject wrapScalar(
+    static ManagedObject adaptScalar(
             final @NonNull SpecificationLoader specLoader,
             final @NonNull Object pojo) {
         if(pojo instanceof ManagedObject) {
             return (ManagedObject)pojo;
         }
         val spec = specLoader.specForType(pojo.getClass()).orElse(null);
-        return wrapScalarInternal(spec, pojo, Optional.empty());
+        return adaptScalarInternal(spec, pojo, Optional.empty());
+    }
+
+    static ManagedObject adaptScalar(
+            final @NonNull ObjectSpecification guess,
+            final @Nullable Object pojo) {
+        if(pojo instanceof ManagedObject) {
+            return (ManagedObject)pojo;
+        }
+        return adaptScalarInternal(guess, pojo, Optional.empty());
     }
 
-    private static ManagedObject wrapScalarInternal(
-            final @Nullable ObjectSpecification spec,
+    static ManagedObject identified(
+            final @NonNull  ObjectSpecification spec,
+            final @Nullable Object pojo,
+            final @NonNull  Bookmark bookmark) {
+        return adaptScalarInternal(spec, pojo, Optional.of(bookmark));
+    }
+
+    // -- HELPER
+
+    /**
+     * spec and pojo don't need to be strictly in sync, we adapt if required
+     */
+    private static ManagedObject adaptScalarInternal(
+            final @Nullable ObjectSpecification guess,
             final @NonNull Object pojo,
             final @NonNull Optional<Bookmark> bookmarkIfAny) {
 
-        _Assert.assertTrue(!_Collections.isCollectionOrArrayOrCanType(pojo.getClass()),
-                ()->String.format("is scalar %s", pojo.getClass()));
+        MmAssertionUtil.assertPojoIsScalar(pojo);
+        val spec = MmSpecUtil.quicklyResolveObjectSpecificationFor(guess, pojo.getClass());
 
         val specialization = spec!=null
                 ? Specialization.inferFrom(spec, pojo)
@@ -534,7 +584,7 @@ extends
         case VIEWMODEL:
             return viewmodel(spec, pojo, bookmarkIfAny);
         case ENTITY:
-            return entity(spec, pojo);
+            return entity(spec, pojo, bookmarkIfAny);
         case MIXIN:
             return mixin(spec, pojo);
         case OTHER:
@@ -549,18 +599,6 @@ extends
 
     // -- FACTORIES LEGACY
 
-    @Deprecated
-    static ManagedObject notBookmarked(
-            final ObjectSpecification spec,
-            final Object pojo) {
-        if(pojo instanceof ManagedObject) {
-            return (ManagedObject)pojo;
-        }
-        return !_Collections.isCollectionOrArrayOrCanType(pojo.getClass())
-                ? wrapScalarInternal(spec, pojo, Optional.empty())
-                : new _ManagedObjectWithEagerSpec(spec, pojo);
-    }
-
     /**
      * Optimized for cases, when the pojo's specification is already available.
      * If {@code pojo} is an entity, automatically memoizes its bookmark.
@@ -572,7 +610,9 @@ extends
             final @NonNull ObjectSpecification spec,
             final @Nullable Object pojo) {
 
-        MmAssertionUtil.assertPojoNotWrapped(pojo);
+        if(pojo instanceof ManagedObject) {
+            return (ManagedObject)pojo;
+        }
 
         //ISIS-2430 Cannot assume Action Param Spec to be correct when eagerly loaded
         //actual type in use (during runtime) might be a sub-class of the above, so re-adapt with hinting spec
@@ -589,9 +629,7 @@ extends
             final @NonNull Object pojo,
             final @NonNull Bookmark bookmark) {
 
-        if(pojo!=null) {
-            _Assert.assertFalse(_Collections.isCollectionOrArrayOrCanType(pojo.getClass()));
-        }
+        MmAssertionUtil.assertPojoIsScalar(pojo);
 
         if(!spec.getCorrespondingClass().isAssignableFrom(pojo.getClass())) {
             throw _Exceptions.illegalArgument(
@@ -602,7 +640,7 @@ extends
                     spec.getCorrespondingClass(), pojo.getClass(), pojo.toString());
         }
         MmAssertionUtil.assertPojoNotWrapped(pojo);
-        return _ManagedObjectWithEagerSpec.identified(spec, pojo, bookmark);
+        return ManagedObject.identified(spec, pojo, bookmark);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java
index b31317dfd3..f32da3b83d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java
@@ -24,6 +24,7 @@ import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 import org.apache.isis.commons.internal.assertions._Assert;
+import org.apache.isis.commons.internal.collections._Collections;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.commons.ClassUtil;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -51,6 +52,23 @@ public class MmAssertionUtil {
                     + "does not exaclty match %s%n", actualSpec, requiredSpec));
     }
 
+    /**
+     * Guard against incompatible type.
+     */
+    public @NonNull UnaryOperator<ObjectSpecification> assertTypeOf(
+            final @NonNull ObjectSpecification requiredSpec) {
+        return specUnderInvestigation -> {
+            _Assert.assertNotNull(specUnderInvestigation);
+            if(specUnderInvestigation.isOfTypeResolvePrimitive(requiredSpec)) {
+                return specUnderInvestigation;
+            }
+            throw _Exceptions.illegalArgument("Object has incompatible type %s, "
+                    + "must be an instance of %s.",
+                    specUnderInvestigation,
+                    requiredSpec);
+        };
+    }
+
     /**
      * Guard against incompatible type.
      */
@@ -87,4 +105,12 @@ public class MmAssertionUtil {
         }
     }
 
+    public void assertPojoIsScalar(final @Nullable Object pojo) {
+        if(pojo==null) {
+            return;
+        }
+        _Assert.assertTrue(!_Collections.isCollectionOrArrayOrCanType(pojo.getClass()),
+                ()->String.format("is scalar %s", pojo.getClass()));
+    }
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmEntityUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmEntityUtil.java
index 1305817bb1..715c8b812f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmEntityUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmEntityUtil.java
@@ -24,13 +24,10 @@ import java.util.function.UnaryOperator;
 import org.springframework.lang.Nullable;
 
 import org.apache.isis.applib.services.repository.EntityState;
-import org.apache.isis.commons.functional.Try;
 import org.apache.isis.commons.internal.assertions._Assert;
-import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.beans.PersistenceStack;
 import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
-import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader;
 
 import lombok.NonNull;
 import lombok.val;
@@ -49,45 +46,28 @@ public final class MmEntityUtil {
             return Optional.empty();
         }
 
-        val entityFacet = spec.getFacet(EntityFacet.class);
-        if(entityFacet==null) {
-            return Optional.empty();
-        }
-
-        return Optional.of(entityFacet.getPersistenceStack());
+        return spec.entityFacet()
+                .map(EntityFacet::getPersistenceStack);
     }
 
     @NonNull
     public static EntityState getEntityState(final @Nullable ManagedObject adapter) {
-        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) {
-            return EntityState.NOT_PERSISTABLE;
-        }
-        val spec = adapter.getSpecification();
-        val pojo = adapter.getPojo();
-
-        if(!spec.isEntity()) {
-            return EntityState.NOT_PERSISTABLE;
-        }
-
-        val entityFacet = spec.getFacet(EntityFacet.class);
-        if(entityFacet==null) {
-            throw _Exceptions.unrecoverable("Entity types must have an EntityFacet");
-        }
-
-        return entityFacet.getEntityState(pojo);
+        return adapter!=null
+             ? adapter.getEntityState()
+             : EntityState.NOT_PERSISTABLE;
     }
 
     public static void persistInCurrentTransaction(final ManagedObject managedObject) {
         requiresEntity(managedObject);
         val spec = managedObject.getSpecification();
-        val entityFacet = spec.getFacet(EntityFacet.class);
+        val entityFacet = spec.entityFacetElseFail();
         entityFacet.persist(managedObject.getPojo());
     }
 
     public static void destroyInCurrentTransaction(final ManagedObject managedObject) {
         requiresEntity(managedObject);
         val spec = managedObject.getSpecification();
-        val entityFacet = spec.getFacet(EntityFacet.class);
+        val entityFacet = spec.entityFacetElseFail();
         entityFacet.delete(managedObject.getPojo());
     }
 
@@ -126,53 +106,8 @@ public final class MmEntityUtil {
         return managedObject;
     }
 
+    @Deprecated
     public static ManagedObject refetch(final @Nullable ManagedObject managedObject) {
-        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(managedObject)) {
-            return managedObject;
-        }
-        if(managedObject instanceof PackedManagedObject) {
-            ((PackedManagedObject)managedObject).unpack().forEach(MmEntityUtil::refetch);
-            return managedObject;
-        }
-        val entityState = MmEntityUtil.getEntityState(managedObject);
-        if(!entityState.isPersistable()) {
-            return managedObject;
-        }
-        if(!entityState.isDetached()) {
-            return managedObject;
-        }
-
-        val spec = managedObject.getSpecification();
-        val objectManager = managedObject.getObjectManager();
-
-        val reattached = ManagedObjects.bookmark(managedObject)
-        .map(bookmark->
-                ObjectLoader.Request.of(
-                                spec,
-                                bookmark))
-        .map(loadRequest->Try.call(
-                ()->objectManager.loadObject(loadRequest)))
-        .map(loadResult->
-                // a valid scenario for entities: not found eg. after deletion,
-                // which will fail the load request
-                loadResult.isFailure()
-                        ? ManagedObject.empty(managedObject.getSpecification())
-                        : loadResult.getValue().get()
-        )
-        .orElse(managedObject);
-
-        // handles deleted entities
-        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(reattached)) {
-            // returns the 'emptied' ManagedObject from above
-            return reattached;
-        }
-
-        val newState = MmEntityUtil.getEntityState(reattached);
-        _Assert.assertTrue(newState.isAttached());
-
-        _Casts.castTo(_ManagedObjectWithBookmark.class, managedObject)
-        .ifPresent(obj->obj.replacePojo(old->reattached.getPojo()));
-
         return managedObject;
     }
 
@@ -236,4 +171,5 @@ public final class MmEntityUtil {
         return adapter;
     }
 
+
 }
\ No newline at end of file
diff --git a/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmSpecUtil.java
similarity index 53%
copy from viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java
copy to core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmSpecUtil.java
index 06b02e91c7..f3d40188eb 100644
--- a/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmSpecUtil.java
@@ -16,34 +16,29 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.viewer.commons.model.binding;
+package org.apache.isis.core.metamodel.object;
 
-import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.core.metamodel.object.ManagedObject;
-import org.apache.isis.core.metamodel.object.MmUnwrapUtil;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(staticName = "of")
-public final class BindingConverterForManagedObject<T>
-implements BindingConverter<ManagedObject, T> {
-
-    @Getter private final ObjectSpecification valueSpecification;
-
-    @Override
-    public ManagedObject toLeft(final T pojo) {
-        return ManagedObject.of(getValueSpecification(), pojo);
-    }
-
-    @Override
-    public T toRight(final ManagedObject adapter) {
-        return _Casts.uncheckedCast(MmUnwrapUtil.single(adapter));
+import lombok.NonNull;
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public final class MmSpecUtil {
+
+    /**
+     * optimized for the case when a specification that probably matches is known in advance
+     * the result must be an instance of guess
+     */
+    public ObjectSpecification quicklyResolveObjectSpecificationFor(
+            final @NonNull ObjectSpecification guess,
+            final @NonNull Class<?> requiredType) {
+        return guess.getCorrespondingClass().equals(requiredType)
+                // when successful guess
+                ? guess
+                // else lookup
+                : MmAssertionUtil.assertTypeOf(guess)
+                    .apply(guess.getSpecificationLoader().specForTypeElseFail(requiredType));
     }
 
-}
-
-
-
-
+}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/Refetchable.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/Refetchable.java
new file mode 100644
index 0000000000..f00463b887
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/Refetchable.java
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.object;
+
+/** don't expose outside this package */
+interface Refetchable {
+
+    /** side-effect free for toString, equals and hashCode */
+    Object peekAtPojo();
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityAttached.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityAttached.java
new file mode 100644
index 0000000000..238e30a829
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityAttached.java
@@ -0,0 +1,156 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.object;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import org.springframework.lang.Nullable;
+
+import org.apache.isis.applib.exceptions.unrecoverable.ObjectNotFoundException;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.repository.EntityState;
+import org.apache.isis.commons.internal.debug._Debug;
+import org.apache.isis.commons.internal.debug.xray.XrayUi;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+
+import lombok.NonNull;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+/**
+ * (package private) specialization corresponding to a attached {@link Specialization#ENTITY}
+ * @see ManagedObject.Specialization#ENTITY
+ */
+@Log4j2
+final class _ManagedObjectEntityAttached
+extends _ManagedObjectSpecified
+implements Refetchable {
+
+    private /*final*/ @Nullable Object pojo;
+    private final @NonNull Bookmark bookmark;
+
+    _ManagedObjectEntityAttached(
+            final ObjectSpecification spec,
+            final Object pojo,
+            final @NonNull Optional<Bookmark> bookmarkIfKnown) {
+        super(ManagedObject.Specialization.ENTITY, spec);
+        this.pojo = assertCompliance(pojo);
+        this.bookmark = bookmarkIfKnown
+                .orElseGet(this::createBookmark);
+    }
+
+    @Override
+    public Optional<Bookmark> getBookmark() {
+        return Optional.of(bookmark);
+    }
+
+    @Override
+    public Optional<Bookmark> getBookmarkRefreshed() {
+        return getBookmark(); // no-op for entities
+    }
+
+    @Override
+    public boolean isBookmarkMemoized() {
+        return true;
+    }
+
+    @Override
+    public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) {
+        // no-op for entities
+    }
+
+    @Override
+    public Object peekAtPojo() {
+        return pojo;
+    }
+
+    @Override
+    public Object getPojo() {
+
+        // refetch if required ...
+
+        val entityFacet = entityFacet();
+
+        val entityState = entityFacet.getEntityState(pojo);
+        if(!entityState.isPersistable()) {
+            throw _Exceptions.illegalState("not persistable %s", getSpecification());
+        }
+        if(!entityState.isDetached()) {
+            return pojo; // is attached
+        }
+
+        // throws on deleted entity
+        val reattached = entityFacet.fetchByBookmark(bookmark)
+                .orElseThrow(()->{
+                    return new ObjectNotFoundException(""+bookmark);});
+
+        if(!entityFacet.getEntityState(reattached).isAttached()) {
+            throw _Exceptions.illegalState("entity not attached after refetch attempt %s", bookmark);
+        }
+
+        return this.pojo = assertCompliance(reattached);
+    }
+
+    @Override
+    public @NonNull EntityState getEntityState() {
+        val entityFacet = entityFacet();
+        return entityFacet.getEntityState(pojo);
+    }
+
+    // -- HELPER
+
+    private EntityFacet entityFacet() {
+        return getSpecification().entityFacetElseFail();
+    }
+
+    @SuppressWarnings("deprecation")
+    private Bookmark createBookmark() {
+        val entityFacet = entityFacet();
+
+        // fail early when detached entities are detected
+        // should have been re-fetched at start of this request-cycle
+        if(
+//                && EntityUtil.getPersistenceStandard(managedObject)
+//                    .map(PersistenceStandard::isJdo)
+//                    .orElse(false)
+                !entityFacet.getEntityState(pojo).isAttached()) {
+
+            _Debug.onCondition(XrayUi.isXrayEnabled(), ()->{
+                _Debug.log("detached entity detected %s", pojo);
+            });
+
+            val msg = String.format(
+                    "The persistence layer does not recognize given object %s, "
+                    + "meaning the object has no identifier that associates it with the persistence layer. "
+                    + "(most likely, because the object is detached, eg. was not persisted after being new-ed up)",
+                    getSpecification());
+
+            // in case of the exception getting swallowed, also write a log
+            log.error(msg);
+
+            throw _Exceptions.illegalArgument(msg);
+        }
+
+        return entityFacet.bookmarkForElseFail(pojo);
+    }
+
+}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntity.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityDetached.java
similarity index 57%
rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntity.java
rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityDetached.java
index 91b8c9192c..299c14fd29 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntity.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityDetached.java
@@ -18,65 +18,63 @@
  */
 package org.apache.isis.core.metamodel.object;
 
-import java.util.Optional;
 import java.util.function.Supplier;
 
-import org.springframework.lang.Nullable;
-
 import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.repository.EntityState;
+import org.apache.isis.commons.internal.assertions._Assert;
+import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
+import lombok.Getter;
 import lombok.NonNull;
+import lombok.val;
+import lombok.experimental.Accessors;
 
 /**
- * (package private) specialization corresponding to {@link Specialization#ENTITY}
+ * (package private) specialization corresponding to a detached {@link Specialization#ENTITY}
  * @see ManagedObject.Specialization#ENTITY
  */
-final class _ManagedObjectEntity
-extends _ManagedObjectSpecified {
+final class _ManagedObjectEntityDetached
+extends _ManagedObjectSpecified
+implements Bookmarkable.NoBookmark, Refetchable {
 
-    private /*final*/ @Nullable Object pojo;
-    private final @NonNull Bookmark bookmark;
+    @Getter(onMethod_ = {@Override}) @Accessors(makeFinal = true)
+    private final @NonNull Object pojo;
 
-    _ManagedObjectEntity(
+    _ManagedObjectEntityDetached(
             final ObjectSpecification spec,
-            final Object pojo,
-            final @NonNull Bookmark bookmark) {
+            final Object pojo) {
         super(ManagedObject.Specialization.ENTITY, spec);
+        _Assert.assertTrue(spec.isEntity());
         this.pojo = assertCompliance(pojo);
-        this.bookmark = bookmark;
     }
 
     @Override
-    public Optional<Bookmark> getBookmark() {
-        return Optional.of(bookmark);
-    }
-
-    @Override
-    public Optional<Bookmark> getBookmarkRefreshed() {
-        return getBookmark(); // no-op for entities
+    public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) {
+        // no-op for other
     }
 
     @Override
-    public boolean isBookmarkMemoized() {
-        return true;
+    public String getTitle() {
+        return "detached entity object";
     }
 
     @Override
-    public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) {
-        // no-op for entities
+    public Object peekAtPojo() {
+        return pojo;
     }
 
     @Override
-    public Object getPojo() {
-        // TODO refetch if required
-        return pojo;
+    public @NonNull EntityState getEntityState() {
+        val entityFacet = entityFacet();
+        return entityFacet.getEntityState(pojo);
     }
 
     // -- HELPER
 
-//    private EntityFacet entityFacet() {
-//        return getSpecification().entityFacet().orElseThrow();
-//    }
+    private EntityFacet entityFacet() {
+        return getSpecification().entityFacetElseFail();
+    }
 
 }
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityHybrid.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityHybrid.java
new file mode 100644
index 0000000000..31da98859f
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityHybrid.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.object;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.repository.EntityState;
+import org.apache.isis.commons.functional.Either;
+import org.apache.isis.commons.internal.assertions._Assert;
+
+import lombok.NonNull;
+import lombok.Synchronized;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+/**
+ * (package private) specialization corresponding to {@link Specialization#ENTITY}
+ * @see ManagedObject.Specialization#ENTITY
+ */
+@Log4j2
+final class _ManagedObjectEntityHybrid
+extends _ManagedObjectSpecified
+implements Refetchable {
+
+    /**
+     * dynamically mutates from one to the other based on pojos persistent state;
+     * however the pojo reference must be kept identical
+     */
+    private @NonNull Either<_ManagedObjectEntityDetached, _ManagedObjectEntityAttached>
+        eitherDetachedOrAttached;
+
+    private EntityState entityState;
+
+    _ManagedObjectEntityHybrid(
+            final @NonNull _ManagedObjectEntityDetached detached) {
+        super(ManagedObject.Specialization.ENTITY, detached.getSpecification());
+        this.eitherDetachedOrAttached = Either.left(detached);
+        this.entityState = EntityState.PERSISTABLE_DETACHED;
+    }
+
+    _ManagedObjectEntityHybrid(
+            final @NonNull _ManagedObjectEntityAttached attached) {
+        super(ManagedObject.Specialization.ENTITY, attached.getSpecification());
+        this.eitherDetachedOrAttached = Either.right(attached);
+        this.entityState = EntityState.PERSISTABLE_ATTACHED;
+    }
+
+    @Override
+    public Optional<Bookmark> getBookmark() {
+        return eitherDetachedOrAttached
+                .fold(ManagedObject::getBookmark, ManagedObject::getBookmark);
+    }
+
+    @Override
+    public Optional<Bookmark> getBookmarkRefreshed() {
+        return eitherDetachedOrAttached
+                .fold(ManagedObject::getBookmarkRefreshed, ManagedObject::getBookmarkRefreshed);
+    }
+
+    @Override
+    public boolean isBookmarkMemoized() {
+        return eitherDetachedOrAttached
+                .fold(ManagedObject::isBookmarkMemoized, ManagedObject::isBookmarkMemoized);
+    }
+
+    @Override
+    public @NonNull EntityState getEntityState() {
+
+        val entityState = eitherDetachedOrAttached
+                .fold(ManagedObject::getEntityState, ManagedObject::getEntityState);
+
+        if(this.entityState!=entityState) {
+            log.debug("about to morph {} -> {}", this.entityState, entityState);
+            this.entityState = entityState;
+            reassessVariant(entityState, peekAtPojo());
+            if(entityState.isAttached()) {
+                _Assert.assertTrue(eitherDetachedOrAttached.isRight());
+            } else {
+                _Assert.assertTrue(eitherDetachedOrAttached.isLeft());
+            }
+        }
+
+        return entityState;
+    }
+
+    @Override
+    public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) {
+        // no-op for entities
+    }
+
+    @Override
+    public Object getPojo() {
+        val pojo = eitherDetachedOrAttached
+                .fold(ManagedObject::getPojo, ManagedObject::getPojo);
+        getEntityState(); // triggers reassessment
+        return pojo;
+    }
+
+    // -- HELPER
+
+    @Override
+    public Object peekAtPojo() {
+        return eitherDetachedOrAttached
+            .fold(Refetchable::peekAtPojo, Refetchable::peekAtPojo);
+    }
+
+    @Synchronized
+    private void reassessVariant(final EntityState entityState, final Object pojo) {
+        if(eitherDetachedOrAttached.isLeft()
+                && entityState.isAttached()) {
+            // morph into attached
+            val bookmark = getSpecification().entityFacetElseFail().bookmarkFor(pojo);
+            eitherDetachedOrAttached = Either.right(
+                    new _ManagedObjectEntityAttached(getSpecification(), pojo, bookmark));
+            return;
+        }
+        if(eitherDetachedOrAttached.isRight()
+                && !entityState.isAttached()) {
+            // morph into detached
+            eitherDetachedOrAttached = Either.left(
+                    new _ManagedObjectEntityDetached(getSpecification(), pojo));
+            return;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectOther.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectOther.java
index 4618fad95a..17f71f1aeb 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectOther.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectOther.java
@@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.object;
 import java.util.function.Supplier;
 
 import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 
 import lombok.Getter;
@@ -42,9 +43,9 @@ implements Bookmarkable.NoBookmark {
             final ObjectSpecification spec,
             final Object pojo) {
         super(ManagedObject.Specialization.OTHER, spec);
-        //_Assert.assertTrue(spec.isOther()); //TODO later
-        //this.pojo = assertCompliance(pojo); //TODO later
-        this.pojo = pojo;
+        _Assert.assertTrue(!spec.isValue());
+        _Assert.assertTrue(!spec.isEntityOrViewModel());
+        this.pojo = assertCompliance(pojo);
     }
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
index 65d1d0871c..76a9f9efe7 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java
@@ -67,6 +67,9 @@ implements ManagedObject {
         if(specialization.getTypePolicy().isExactTypeRequired()) {
             MmAssertionUtil.assertExactType(specification, pojo);
         }
+        if(getSpecification().isEntityOrViewModel()) {
+            getServiceInjector().injectServicesInto(pojo); // might be redundant
+        }
         return pojo;
     }
 
@@ -94,16 +97,22 @@ implements ManagedObject {
         if(!this.getSpecification().equals(other.getSpecification())) {
             return false;
         }
-        val canGetPojosWithoutSideeffect = !this.getSpecialization().getPojoPolicy().isRefetchable();
+        val canGetPojosWithoutSideeffect = !getSpecialization().getPojoPolicy().isRefetchable();
         if(canGetPojosWithoutSideeffect) {
             // expected to work for packed variant just fine, as it compares lists
             return Objects.equals(this.getPojo(), other.getPojo());
         }
-        // objects are considered equal if their bookmarks match
-        _Assert.assertTrue(other.isBookmarkMemoized()); // guarantee no side-effects on other
-        return Objects.equals(
-                sideEffectFreeBookmark(),
-                other.getBookmark().orElseThrow(_Exceptions::unexpectedCodeReach));
+
+        if(this.isBookmarkMemoized()
+                && other.isBookmarkMemoized()) {
+            return Objects.equals(
+                    sideEffectFreeBookmark(),
+                    other.getBookmark().orElseThrow(_Exceptions::unexpectedCodeReach));
+        }
+
+        val a = (Refetchable) this;
+        val b = (Refetchable) this;
+        return Objects.equals(a.peekAtPojo(), b.peekAtPojo());
     }
 
     @Override
@@ -124,7 +133,7 @@ implements ManagedObject {
                 getSpecification(),
                 !getSpecialization().getPojoPolicy().isRefetchable()
                     ? getPojo() // its safe to get pojo side-effect free
-                    : !getSpecialization().getBookmarkPolicy().isNoBookmark()
+                    : isBookmarkMemoized()
                         ? String.format("(refetchable, %s)", sideEffectFreeBookmark())
                         : "(refetchable, suppressed to not cause side effects)");
     }
@@ -132,7 +141,6 @@ implements ManagedObject {
     // -- HELPER
 
     private Bookmark sideEffectFreeBookmark() {
-        _Assert.assertFalse(getSpecialization().getBookmarkPolicy().isNoBookmark());
         _Assert.assertTrue(isBookmarkMemoized());
         return getBookmark().orElseThrow(_Exceptions::unexpectedCodeReach);
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java
deleted file mode 100644
index b928865fde..0000000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.core.metamodel.object;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.function.Supplier;
-import java.util.function.UnaryOperator;
-
-import org.springframework.lang.Nullable;
-
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.commons.internal.base._Lazy;
-import org.apache.isis.commons.internal.debug._XrayEvent;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-
-import lombok.NonNull;
-import lombok.val;
-
-abstract class _ManagedObjectWithBookmark
-extends _ManagedObjectSpecifiedLegacy {
-
-    protected final _Lazy<Optional<Bookmark>> bookmarkLazy =
-            _Lazy.threadSafe(()->bookmark(this));
-
-    protected _ManagedObjectWithBookmark(final Specialization specialization) {
-        super(specialization);
-    }
-
-    @Override
-    public final Optional<Bookmark> getBookmark() {
-        return bookmarkLazy.get();
-    }
-
-    @Override
-    public final boolean isBookmarkMemoized() {
-        return bookmarkLazy.isMemoized();
-    }
-
-    @Override
-    public final Optional<Bookmark> getBookmarkRefreshed() {
-        // silently ignore invalidation, when the pojo is an entity
-        if(!getSpecification().isEntity()) {
-            bookmarkLazy.clear();
-        }
-        return getBookmark();
-    }
-
-    private void replaceBookmark(final UnaryOperator<Bookmark> replacer) {
-        final Bookmark old = bookmarkLazy.isMemoized()
-                ? bookmarkLazy.get().orElse(null)
-                : null;
-        bookmarkLazy.clear();
-        bookmarkLazy.set(Optional.ofNullable(replacer.apply(old)));
-    }
-
-    // guards against non-identifiable objects;
-    // historically, we allowed non-identifiable to be handled by the objectManager,
-    // which as a fallback creates 'random' UUIDs
-    private Optional<Bookmark> bookmark(final @Nullable ManagedObject adapter) {
-
-        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)
-                || adapter.getSpecification().isValue()
-                || !ManagedObjects.isIdentifiable(adapter)) {
-            return Optional.empty();
-        }
-
-        return ManagedObjects.spec(adapter)
-                .map(ObjectSpecification::getMetaModelContext)
-                .map(MetaModelContext::getObjectManager)
-                .map(objectManager->objectManager.bookmarkObject(adapter));
-    }
-
-    // -- REFRESH OPTIMIZATION
-
-    private UUID interactionIdDuringWhichRefreshed = null;
-
-    @Override
-    public final void refreshViewmodel(final @Nullable Supplier<Bookmark> bookmarkSupplier) {
-        val spec = getSpecification();
-        if(spec.isViewModel()) {
-            val viewModelFacet = spec.getFacet(ViewModelFacet.class);
-            if(viewModelFacet.containsEntities()) {
-
-                val shouldRefresh = spec.getMetaModelContext().getInteractionProvider().getInteractionId()
-                .map(this::shouldRefresh)
-                .orElse(true); // if there is no current interaction, refresh regardless; unexpected state, might fail later
-
-                if(!shouldRefresh) {
-                    return;
-                }
-
-                if(isBookmarkMemoized()) {
-                    reloadViewmodelFromMemoizedBookmark();
-                } else {
-                    val bookmark = bookmarkSupplier!=null
-                            ? bookmarkSupplier.get()
-                            : null;
-                    if(bookmark!=null) {
-                        reloadViewmodelFromBookmark(bookmark);
-                    }
-                }
-            }
-        }
-    }
-
-    private boolean shouldRefresh(final @NonNull UUID interactionId) {
-        if(Objects.equals(this.interactionIdDuringWhichRefreshed, interactionId)) {
-            return false; // already refreshed within current interaction
-        }
-        this.interactionIdDuringWhichRefreshed = interactionId;
-        return true;
-    }
-
-    /**
-     * Reload current viewmodel object from memoized bookmark, otherwise does nothing.
-     */
-    private void reloadViewmodelFromMemoizedBookmark() {
-        val spec = getSpecification();
-        if(isBookmarkMemoized()
-                && spec.isViewModel()) {
-
-            val bookmark = getBookmark().get();
-            val viewModelClass = spec.getCorrespondingClass();
-
-            val recreatedViewmodel =
-                    getMetaModelContext().getFactoryService().viewModel(viewModelClass, bookmark);
-
-            _XrayEvent.event("Viewmodel '%s' recreated from memoized bookmark.", viewModelClass.getName());
-
-            replacePojo(old->recreatedViewmodel);
-        }
-    }
-
-    private void reloadViewmodelFromBookmark(final @NonNull Bookmark bookmark) {
-        val spec = getSpecification();
-        if(spec.isViewModel()) {
-            val viewModelClass = spec.getCorrespondingClass();
-
-            val recreatedViewmodel =
-                    getMetaModelContext().getFactoryService().viewModel(viewModelClass, bookmark);
-
-            _XrayEvent.event("Viewmodel '%s' recreated from provided bookmark.", viewModelClass.getName());
-
-            replacePojo(old->recreatedViewmodel);
-            replaceBookmark(old->bookmark);
-        }
-    }
-
-    /**
-     * Introduced, so we can re-fetch detached entity pojos in place.
-     */
-    abstract void replacePojo(UnaryOperator<Object> replacer);
-
-}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java
deleted file mode 100644
index fdf49ed4f3..0000000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.core.metamodel.object;
-
-import java.util.Optional;
-import java.util.function.UnaryOperator;
-
-import org.springframework.lang.Nullable;
-
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.commons.internal.assertions._Assert;
-import org.apache.isis.commons.internal.collections._Collections;
-import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.ToString;
-import lombok.val;
-
-@Deprecated
-@EqualsAndHashCode(of = "pojo", callSuper = false)
-@ToString(of = {"specification", "pojo"}) //ISIS-2317 make sure toString() is without side-effects
-@Getter
-final class _ManagedObjectWithEagerSpec
-extends _ManagedObjectWithBookmark {
-
-    public static ManagedObject identified(
-            final @NonNull  ObjectSpecification spec,
-            final @Nullable Object pojo,
-            final @NonNull  Bookmark bookmark) {
-
-        if(pojo!=null) {
-            _Assert.assertFalse(_Collections.isCollectionOrArrayOrCanType(pojo.getClass()));
-        }
-
-        val managedObject = new _ManagedObjectWithEagerSpec(spec, pojo);
-        managedObject.bookmarkLazy.set(Optional.of(bookmark));
-        return managedObject;
-    }
-
-    _ManagedObjectWithEagerSpec(
-            final ObjectSpecification spec,
-            final Object pojo) {
-        super(ManagedObject.Specialization.inferFrom(spec, pojo));
-        //_Assert.assertFalse(getSpecialization().isValue()); // VALUE already migrated
-        _Assert.assertFalse(getSpecialization().isService()); // SERVICE already migrated
-        _Assert.assertFalse(getSpecialization().isMixin()); // MIXIN already migrated
-
-        this.specification = spec;
-        this.pojo = pojo;
-    }
-
-    @NonNull private final ObjectSpecification specification;
-    @Nullable private /*final*/ Object pojo;
-
-    @Override
-    public void replacePojo(final UnaryOperator<Object> replacer) {
-        pojo = assertCompliance(replacer.apply(pojo));
-    }
-
-    @Override
-    public final String getTitle() {
-        return _InternalTitleUtil.titleString(
-                TitleRenderRequest.forObject(this));
-    }
-
-}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java
index 07b9b73210..165b141cfa 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java
@@ -27,7 +27,6 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 import org.apache.isis.core.metamodel.objectmanager.create.ObjectCreator;
 import org.apache.isis.core.metamodel.objectmanager.detach.ObjectDetacher;
@@ -115,26 +114,12 @@ public interface ObjectManager {
 
     // -- ADAPTING POJOS
 
-    enum EntityAdaptingMode {
-        MEMOIZE_BOOKMARK,
-        SKIP_MEMOIZATION;
-        public boolean isMemoize() { return this == MEMOIZE_BOOKMARK;}
-    }
-
     /**
      * Not suitable for adapting a non-scalar
      * If {@code pojo} is an entity, automatically memoizes its bookmark.
      */
     public default ManagedObject adapt(final @Nullable Object pojo) {
-        return adapt(pojo, ()->specForType(Object.class).orElseThrow(), EntityAdaptingMode.MEMOIZE_BOOKMARK);
-    }
-
-    /**
-     * Not suitable for adapting a non-scalar.
-     * If {@code pojo} is an entity, memoizes its bookmark based on policy {@code bookmarking}.
-     */
-    public default ManagedObject adapt(final @Nullable Object pojo, final EntityAdaptingMode bookmarking) {
-        return adapt(pojo, ()->specForType(Object.class).orElseThrow(), bookmarking);
+        return adapt(pojo, ()->specForType(Object.class).orElseThrow());
     }
 
     /**
@@ -143,8 +128,7 @@ public interface ObjectManager {
      */
     public default ManagedObject adapt(
             final @Nullable Object pojo,
-            final @NonNull Supplier<ObjectSpecification> fallbackElementType,
-            final EntityAdaptingMode entityAdaptingMode) {
+            final @NonNull Supplier<ObjectSpecification> fallbackElementType) {
         if(pojo==null) {
             return ManagedObject.unspecified();
         }
@@ -158,11 +142,11 @@ public interface ObjectManager {
             return ManagedObject.unspecified();
         }
         return spec.isScalar()
-                ? managedObjectEagerlyBookmarkedIfRequired(spec, pojo, entityAdaptingMode)
+                ? managedObjectEagerlyBookmarkedIfRequired(spec, pojo)
                 : ManagedObject.packed(
                         spec.getElementSpecification().orElseGet(fallbackElementType),
                         _NullSafe.streamAutodetect(pojo)
-                        .map(element->adapt(element, entityAdaptingMode))
+                        .map(element->adapt(element))
                         .collect(Can.toCan()));
     }
 
@@ -190,7 +174,7 @@ public interface ObjectManager {
             // if actual type matches spec's, we assume, that we don't need to reload,
             // so this is a shortcut for performance reasons
             ? managedObjectEagerlyBookmarkedIfRequired(
-                    proposedSpec, pojo, EntityAdaptingMode.MEMOIZE_BOOKMARK)
+                    proposedSpec, pojo)
             // fallback, ignoring proposedSpec
             : adapt(pojo);
         return adapter;
@@ -200,26 +184,14 @@ public interface ObjectManager {
 
     /**
      * {@link ManagedObject} factory, that in case of given pojo representing an entity
-     * and the entityAdaptingMode equals {@link EntityAdaptingMode#isMemoize()},
+     * and the entityAdaptingMode equals {@link EntityAdaptingMode#isBookmarkable()},
      * then tries to memoize its {@link Bookmark} eagerly
      * (otherwise its {@link Bookmark} is lazily resolved).
      */
     private static ManagedObject managedObjectEagerlyBookmarkedIfRequired(
             final ObjectSpecification spec,
-            final Object pojo,
-            final EntityAdaptingMode entityAdaptingMode) {
-
-        if(entityAdaptingMode.isMemoize()
-                && spec.isEntity()) {
-            val entityFacet = spec.getFacet(EntityFacet.class);
-            val state = entityFacet.getEntityState(pojo);
-            if(state.isAttached()) {
-                val id = entityFacet.identifierFor(pojo);
-                val bookmark = Bookmark.forLogicalTypeAndIdentifier(spec.getLogicalType(), id);
-                return ManagedObject.bookmarked(spec, pojo, bookmark);
-            }
-        }
-        return ManagedObject.notBookmarked(spec, pojo);
+            final Object pojo) {
+        return ManagedObject.adaptScalar(spec, pojo);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/detach/ObjectDetacher_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/detach/ObjectDetacher_builtinHandlers.java
index 39b9b71b53..b20094fd28 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/detach/ObjectDetacher_builtinHandlers.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/detach/ObjectDetacher_builtinHandlers.java
@@ -18,9 +18,7 @@
  */
 package org.apache.isis.core.metamodel.objectmanager.detach;
 
-import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 
 import lombok.Data;
@@ -41,7 +39,7 @@ final class ObjectDetacher_builtinHandlers {
         private MetaModelContext metaModelContext;
 
         @Override
-        public boolean isHandling(ManagedObject managedObject) {
+        public boolean isHandling(final ManagedObject managedObject) {
 
             if(managedObject==null || managedObject.getPojo()==null) {
                 return true;
@@ -51,7 +49,7 @@ final class ObjectDetacher_builtinHandlers {
         }
 
         @Override
-        public ManagedObject handle(ManagedObject managedObject) {
+        public ManagedObject handle(final ManagedObject managedObject) {
             return null; // noop
         }
 
@@ -63,20 +61,16 @@ final class ObjectDetacher_builtinHandlers {
         private final MetaModelContext metaModelContext;
 
         @Override
-        public boolean isHandling(ManagedObject request) {
+        public boolean isHandling(final ManagedObject request) {
             val spec = request.getSpecification();
             return spec.isEntity();
         }
 
         @Override
-        public ManagedObject handle(ManagedObject request) {
+        public ManagedObject handle(final ManagedObject request) {
 
             val spec = request.getSpecification();
-            val entityFacet = spec.getFacet(EntityFacet.class);
-            if(entityFacet==null) {
-                throw _Exceptions.illegalArgument(
-                        "ObjectSpecification is missing an EntityFacet: %s", spec);
-            }
+            val entityFacet = spec.entityFacetElseFail();
 
             Object detachedPojo = entityFacet.detach(request.getPojo());
 
@@ -92,13 +86,13 @@ final class ObjectDetacher_builtinHandlers {
     public static class DetachOther implements ObjectDetacher.Handler {
 
         @Override
-        public boolean isHandling(ManagedObject request) {
+        public boolean isHandling(final ManagedObject request) {
             // if no one else feels responsible, we do
             return true;
         }
 
         @Override
-        public ManagedObject handle(ManagedObject request) {
+        public ManagedObject handle(final ManagedObject request) {
             return request;
         }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/identify/ObjectBookmarker_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/identify/ObjectBookmarker_builtinHandlers.java
index ef74f4bff2..9130d1b88d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/identify/ObjectBookmarker_builtinHandlers.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/identify/ObjectBookmarker_builtinHandlers.java
@@ -26,10 +26,7 @@ import org.apache.isis.applib.services.bookmark.Oid;
 import org.apache.isis.applib.value.semantics.ValueSemanticsProvider;
 import org.apache.isis.commons.internal.base._Bytes;
 import org.apache.isis.commons.internal.base._Strings;
-import org.apache.isis.commons.internal.debug._Debug;
-import org.apache.isis.commons.internal.debug.xray.XrayUi;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 import org.apache.isis.core.metamodel.object.PackedManagedObject;
@@ -37,7 +34,6 @@ import org.apache.isis.core.metamodel.objectmanager.identify.ObjectBookmarker.Ha
 
 import lombok.SneakyThrows;
 import lombok.val;
-import lombok.extern.log4j.Log4j2;
 
 class ObjectBookmarker_builtinHandlers {
 
@@ -63,67 +59,25 @@ class ObjectBookmarker_builtinHandlers {
 
         @Override
         public boolean isHandling(final ManagedObject managedObject) {
-            return managedObject.getSpecification().isInjectable();
+            return managedObject.getSpecialization().isService();
         }
 
         @Override
         public Bookmark handle(final ManagedObject managedObject) {
-            final String identifier = SERVICE_IDENTIFIER;
-            return Bookmark.forLogicalTypeAndIdentifier(
-                    managedObject.getSpecification().getLogicalType(),
-                    identifier);
+            return managedObject.getBookmark().orElseThrow();
         }
-
     }
 
-    @Log4j2
     static class BookmarkForEntities implements Handler {
 
         @Override
         public boolean isHandling(final ManagedObject managedObject) {
-            return managedObject.getSpecification().isEntity();
+            return managedObject.getSpecialization().isEntity();
         }
 
         @Override
         public Bookmark handle(final ManagedObject managedObject) {
-            val spec = managedObject.getSpecification();
-            val entityPojo = managedObject.getPojo();
-            if(entityPojo==null) {
-                val msg = String.format("entity '%s' is null, cannot identify", managedObject);
-                throw _Exceptions.unrecoverable(msg);
-            }
-            val entityFacet = spec.getFacet(EntityFacet.class);
-            if(entityFacet==null) {
-                val msg = String.format("entity '%s' has no EntityFacet associated", managedObject);
-                throw _Exceptions.unrecoverable(msg);
-            }
-
-            // fail early when detached entities are detected
-            // should have been re-fetched at start of this request-cycle
-            if(!managedObject.isBookmarkMemoized()
-//                    && EntityUtil.getPersistenceStandard(managedObject)
-//                        .map(PersistenceStandard::isJdo)
-//                        .orElse(false)
-                    && !entityFacet.getEntityState(entityPojo).isAttached()) {
-
-                _Debug.onCondition(XrayUi.isXrayEnabled(), ()->{
-                    _Debug.log("detached entity detected %s", entityPojo);
-                });
-
-                val msg = String.format(
-                        "The persistence layer does not recognize given object %s, "
-                        + "meaning the object has no identifier that associates it with the persistence layer. "
-                        + "(most likely, because the object is detached, eg. was not persisted after being new-ed up)",
-                        managedObject);
-
-                // in case of the exception getting swallowed, also write a log
-                log.error(msg);
-
-                throw _Exceptions.illegalArgument(msg);
-            }
-
-            val identifier = entityFacet.identifierFor(entityPojo);
-            return Bookmark.forLogicalTypeAndIdentifier(spec.getLogicalType(), identifier);
+            return managedObject.getBookmark().orElseThrow();
         }
 
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
index 88581e4e1b..4b0116eb1f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java
@@ -24,7 +24,6 @@ import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.commons.internal.ioc._ManagedBeanAdapter;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 
@@ -179,15 +178,14 @@ final class ObjectLoader_builtinHandlers {
         public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) {
 
             val spec = objectLoadRequest.getObjectSpecification();
-            val entityFacet = spec.getFacet(EntityFacet.class);
-            if(entityFacet==null) {
-                throw _Exceptions.illegalArgument(
-                        "ObjectSpecification is missing an EntityFacet: %s", spec);
-            }
+            val entityFacet = spec.entityFacetElseFail();
 
             val bookmark = objectLoadRequest.getBookmark();
-            val entity = entityFacet.fetchByIdentifier(bookmark);
-            return entity;
+            val entityPojoIfAny = entityFacet.fetchByBookmark(bookmark);
+
+            return entityPojoIfAny
+                    .map(entityPojo->ManagedObject.entity(spec, entityPojo, Optional.of(bookmark)))
+                    .orElseGet(()->ManagedObject.empty(spec));
         }
 
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/query/ObjectBulkLoader_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/query/ObjectBulkLoader_builtinHandlers.java
index 7eb33b8e98..e632428813 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/query/ObjectBulkLoader_builtinHandlers.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/query/ObjectBulkLoader_builtinHandlers.java
@@ -21,7 +21,6 @@ package org.apache.isis.core.metamodel.objectmanager.query;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 
 import lombok.NonNull;
@@ -85,11 +84,7 @@ final class ObjectBulkLoader_builtinHandlers {
         public Can<ManagedObject> handle(final ObjectBulkLoader.Request objectQuery) {
 
             val spec = objectQuery.getObjectSpecification();
-            val entityFacet = spec.getFacet(EntityFacet.class);
-            if(entityFacet==null) {
-                throw _Exceptions.illegalArgument(
-                        "ObjectSpecification is missing an EntityFacet: %s", spec.getCorrespondingClass());
-            }
+            val entityFacet = spec.entityFacetElseFail();
 
             val entities = entityFacet.fetchByQuery(objectQuery.getQuery());
             val serviceInjector = metaModelContext.getServiceInjector();
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher_builtinHandlers.java
index 9f6f80a93f..3cecee0d22 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher_builtinHandlers.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher_builtinHandlers.java
@@ -18,9 +18,7 @@
  */
 package org.apache.isis.core.metamodel.objectmanager.refresh;
 
-import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 
 import lombok.Data;
@@ -41,7 +39,7 @@ final class ObjectRefresher_builtinHandlers {
         private MetaModelContext metaModelContext;
 
         @Override
-        public boolean isHandling(ManagedObject managedObject) {
+        public boolean isHandling(final ManagedObject managedObject) {
 
             if(managedObject==null || managedObject.getPojo()==null) {
                 return true;
@@ -51,7 +49,7 @@ final class ObjectRefresher_builtinHandlers {
         }
 
         @Override
-        public Void handle(ManagedObject managedObject) {
+        public Void handle(final ManagedObject managedObject) {
             return null; // noop
         }
 
@@ -61,20 +59,16 @@ final class ObjectRefresher_builtinHandlers {
     public static class RefreshEntity implements ObjectRefresher.Handler {
 
         @Override
-        public boolean isHandling(ManagedObject request) {
+        public boolean isHandling(final ManagedObject request) {
             val spec = request.getSpecification();
             return spec.isEntity();
         }
 
         @Override
-        public Void handle(ManagedObject request) {
+        public Void handle(final ManagedObject request) {
 
             val spec = request.getSpecification();
-            val entityFacet = spec.getFacet(EntityFacet.class);
-            if(entityFacet==null) {
-                throw _Exceptions.illegalArgument(
-                        "ObjectSpecification is missing an EntityFacet: %s", spec);
-            }
+            val entityFacet = spec.entityFacetElseFail();
 
             entityFacet.refresh(request.getPojo());
 
@@ -90,13 +84,13 @@ final class ObjectRefresher_builtinHandlers {
     public static class RefreshOther implements ObjectRefresher.Handler {
 
         @Override
-        public boolean isHandling(ManagedObject request) {
+        public boolean isHandling(final ManagedObject request) {
             // if no one else feels responsible, we do
             return true;
         }
 
         @Override
-        public Void handle(ManagedObject request) {
+        public Void handle(final ManagedObject request) {
             return null; // noop
         }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/Hierarchical.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/Hierarchical.java
index 1915659ba5..c6225b2ffb 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/Hierarchical.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/Hierarchical.java
@@ -45,6 +45,11 @@ public interface Hierarchical {
      */
     boolean isOfType(ObjectSpecification other);
 
+    /**
+     * Same as {@link #isOfType(ObjectSpecification)}, except treating wrapper/primitive the same.
+     */
+    boolean isOfTypeResolvePrimitive(ObjectSpecification other);
+
     public static enum Depth {
         DIRECT,
         TRANSITIVE
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java
index 847ea7f3d7..a220b5adde 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java
@@ -440,13 +440,13 @@ extends
     default boolean isEntity() {
         return getBeanSort().isEntity()
                 || (getBeanSort().isAbstract()
-                        && lookupFacet(EntityFacet.class).isPresent());
+                        && entityFacet().isPresent());
     }
 
     default boolean isViewModel() {
         return getBeanSort().isViewModel()
                 || (getBeanSort().isAbstract()
-                        && lookupFacet(ViewModelFacet.class).isPresent());
+                        && viewmodelFacet().isPresent());
     }
 
     default boolean isEntityOrViewModel() {
@@ -629,5 +629,24 @@ extends
     /** introduced for lookup optimization / allow memoization */
     @SuppressWarnings("rawtypes")
     Optional<ValueFacet> valueFacet();
+    @SuppressWarnings("rawtypes")
+    default ValueFacet valueFacetElseFail() {
+        return valueFacet().orElseThrow(()->
+            _Exceptions.unrecoverable("Value type %s must have a ValueFacet", toString()));
+    }
+
+    /** introduced for lookup optimization / allow memoization */
+    Optional<EntityFacet> entityFacet();
+    default EntityFacet entityFacetElseFail() {
+        return entityFacet().orElseThrow(()->
+            _Exceptions.unrecoverable("Entity type %s must have an EntityFacet", toString()));
+    }
+
+    /** introduced for lookup optimization / allow memoization */
+    Optional<ViewModelFacet> viewmodelFacet();
+    default ViewModelFacet viewmodelFacetElseFail() {
+        return viewmodelFacet().orElseThrow(()->
+            _Exceptions.unrecoverable("ViewModel type %s must have a ViewModelFacet", toString()));
+    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index 76296c1606..af88318689 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -29,6 +29,8 @@ import java.util.stream.Stream;
 
 import javax.enterprise.inject.Vetoed;
 
+import org.springframework.util.ClassUtils;
+
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.applib.annotation.Introspection.IntrospectionPolicy;
 import org.apache.isis.applib.id.LogicalType;
@@ -60,6 +62,7 @@ import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaFactory;
+import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.facets.object.icon.IconFacet;
 import org.apache.isis.core.metamodel.facets.object.icon.ObjectIcon;
 import org.apache.isis.core.metamodel.facets.object.immutable.ImmutableFacet;
@@ -70,6 +73,7 @@ import org.apache.isis.core.metamodel.facets.object.parented.ParentedCollectionF
 import org.apache.isis.core.metamodel.facets.object.title.TitleFacet;
 import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest;
 import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
+import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.core.metamodel.interactions.InteractionContext;
 import org.apache.isis.core.metamodel.interactions.InteractionUtils;
 import org.apache.isis.core.metamodel.interactions.ObjectTitleContext;
@@ -181,6 +185,8 @@ implements ObjectSpecification {
     private ObjectSpecification superclassSpec;
 
     private ValueFacet valueFacet;
+    private EntityFacet entityFacet;
+    private ViewModelFacet viewmodelFacet;
     private TitleFacet titleFacet;
     private IconFacet iconFacet;
     private NavigableParentFacet navigableParentFacet;
@@ -394,6 +400,24 @@ implements ObjectSpecification {
         return Optional.ofNullable(valueFacet);
     }
 
+    @Override
+    public final Optional<EntityFacet> entityFacet() {
+        // deliberately don't memoize lookup misses, because could be too early
+        if(entityFacet==null) {
+            entityFacet = getFacet(EntityFacet.class);
+        }
+        return Optional.ofNullable(entityFacet);
+    }
+
+    @Override
+    public final Optional<ViewModelFacet> viewmodelFacet() {
+        // deliberately don't memoize lookup misses, because could be too early
+        if(viewmodelFacet==null) {
+            viewmodelFacet = getFacet(ViewModelFacet.class);
+        }
+        return Optional.ofNullable(viewmodelFacet);
+    }
+
     @Override
     public String getTitle(final TitleRenderRequest titleRenderRequest) {
         if (titleFacet != null) {
@@ -466,6 +490,17 @@ implements ObjectSpecification {
                 || otherClass.isAssignableFrom(thisClass);
     }
 
+    @Override
+    public boolean isOfTypeResolvePrimitive(final ObjectSpecification other) {
+
+        val thisClass = ClassUtils.resolvePrimitiveIfNecessary(this.getCorrespondingClass());
+        val otherClass = ClassUtils.resolvePrimitiveIfNecessary(other.getCorrespondingClass());
+
+        return thisClass == otherClass
+                || otherClass.isAssignableFrom(thisClass);
+    }
+
+
     // -- NAME, DESCRIPTION, PERSISTABILITY
 
     @Override
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java
index dc2e7c70d3..c0c13c4e43 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java
@@ -132,7 +132,7 @@ extends AbstractFacetFactoryJUnit4TestCase {
         }
 
         final Customer2 customer = new Customer2();
-        val objectAdapter = ManagedObject.wrapScalar(specificationLoader, customer);
+        val objectAdapter = ManagedObject.adaptScalar(specificationLoader, customer);
 
         final String title = titleFacetViaTitleAnnotation.title(objectAdapter);
         assertThat(title, is("titleElement1. titleElement3,titleElement2"));
@@ -198,9 +198,9 @@ extends AbstractFacetFactoryJUnit4TestCase {
     public void titleAnnotatedMethodsSomeOfWhichReturnNulls() throws Exception {
 
         { // check prerequisites
-            val wThree = ManagedObject.wrapScalar(specificationLoader, Integer.valueOf(3));
+            val wThree = ManagedObject.adaptScalar(specificationLoader, Integer.valueOf(3));
             assertEquals("3", wThree.getTitle());
-            val pThree = ManagedObject.wrapScalar(specificationLoader, 3);
+            val pThree = ManagedObject.adaptScalar(specificationLoader, 3);
             assertEquals("3", pThree.getTitle());
         }
 
@@ -208,7 +208,7 @@ extends AbstractFacetFactoryJUnit4TestCase {
                 .forTesting(Customer4.class, mockMethodRemover, facetedMethod));
 
         final Customer4 customer = new Customer4();
-        val objectAdapter = ManagedObject.wrapScalar(specificationLoader, customer);
+        val objectAdapter = ManagedObject.adaptScalar(specificationLoader, customer);
 
         assertThat(objectAdapter.getTitle(),
                 is("titleElement1 titleElement3 titleElement5 3 this needs to be trimmed"));
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java
index 3bfdf8587a..02de16d75d 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java
@@ -84,7 +84,7 @@ class ManagedObjectTest {
         val emptyObject = ManagedObject.empty(spec);
         assertNotNull(emptyObject);
 
-        val presentObject = ManagedObject.wrapScalar(specLoader, 3);
+        val presentObject = ManagedObject.adaptScalar(specLoader, 3);
         assertEquals(Specialization.VALUE, presentObject.getSpecialization());
 
         presentObject.assertCompliance(6);
@@ -113,7 +113,7 @@ class ManagedObjectTest {
         val constructor = cls.getConstructor(_Constants.emptyClasses);
         val pojo = constructor.newInstance(_Constants.emptyObjects);
 
-        val presentObject = ManagedObject.wrapScalar(specLoader, pojo);
+        val presentObject = ManagedObject.adaptScalar(specLoader, pojo);
         assertEquals(Specialization.VIEWMODEL, presentObject.getSpecialization());
 
         presentObject.assertCompliance(pojo);
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
index 357262c9de..3f8a406b1f 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java
@@ -146,13 +146,7 @@ extends SchemaValueMarshallerAbstract {
                 ? fromTypedTuple(context, valueDto.getComposite())
                 : context.getSemantics().compose(ValueDecomposition.ofFundamental(valueDto));
 
-        if(recoveredValueAsPojo==null) {
-            return ManagedObject.empty(context.getElementType());
-        }
-
-        val recoveredValue = recoveredValueAsPojo!=null
-                ? ManagedObject.of(elementSpec, recoveredValueAsPojo)
-                : ManagedObject.empty(context.getElementType());
+        val recoveredValue = ManagedObject.value(elementSpec, recoveredValueAsPojo);
         return recoveredValue;
     }
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
index 4cc45784e3..68692ac664 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java
@@ -62,7 +62,6 @@ import org.apache.isis.core.metamodel.object.MmUnwrapUtil;
 import org.apache.isis.core.metamodel.object.MmVisibilityUtil;
 import org.apache.isis.core.metamodel.object.PackedManagedObject;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
-import org.apache.isis.core.metamodel.objectmanager.ObjectManager.EntityAdaptingMode;
 import org.apache.isis.core.metamodel.services.events.MetamodelEventService;
 import org.apache.isis.core.metamodel.services.ixn.InteractionDtoFactory;
 import org.apache.isis.core.metamodel.services.publishing.ExecutionPublisher;
@@ -177,7 +176,7 @@ implements MemberExecutorService {
 
         val returnedPojo = priorExecution.getReturned();
         val returnedAdapter = objectManager.adapt(
-                returnedPojo, owningAction::getElementType, EntityAdaptingMode.MEMOIZE_BOOKMARK);
+                returnedPojo, owningAction::getElementType);
 
         // sync DTO with result
         interactionDtoFactory
@@ -279,6 +278,7 @@ implements MemberExecutorService {
         }
 
         val entityState = MmEntityUtil.getEntityState(resultAdapter);
+        //FIXME what to do with new state
         if(entityState.isDetached())   {
             // ensure that any still-to-be-persisted adapters get persisted to DB.
             getTransactionService().flushTransaction();
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/factory/FactoryServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/factory/FactoryServiceDefault.java
index 0211ddb2c6..0e0fb5cb0b 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/factory/FactoryServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/factory/FactoryServiceDefault.java
@@ -33,7 +33,6 @@ import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.applib.services.iactnlayer.InteractionService;
-import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.environment.IsisSystemEnvironment;
@@ -56,7 +55,6 @@ public class FactoryServiceDefault implements FactoryService {
 
     @Inject InteractionService interactionService; // dependsOn
     @Inject private SpecificationLoader specificationLoader;
-    @Inject private ServiceInjector serviceInjector;
     @Inject private IsisSystemEnvironment isisSystemEnvironment;
     @Inject private Provider<ObjectLifecyclePublisher> objectLifecyclePublisherProvider;
     private ObjectLifecyclePublisher objectLifecyclePublisher() { return objectLifecyclePublisherProvider.get(); }
@@ -95,8 +93,7 @@ public class FactoryServiceDefault implements FactoryService {
             throw _Exceptions.illegalArgument("Type '%s' is not recogniced as an entity type by the framework.",
                     entityClass);
         }
-        serviceInjector.injectServicesInto(entityPojo);
-        objectLifecyclePublisher().onPostCreate(ManagedObject.of(spec, entityPojo));
+        objectLifecyclePublisher().onPostCreate(ManagedObject.entityDetached(spec, entityPojo));
         return entityPojo;
     }
 
@@ -124,8 +121,7 @@ public class FactoryServiceDefault implements FactoryService {
             throw _Exceptions.illegalArgument("Type '%s' is not recogniced as a ViewModel by the framework.",
                     viewModelClass);
         }
-        serviceInjector.injectServicesInto(viewModelPojo);
-        objectLifecyclePublisher().onPostCreate(ManagedObject.of(spec, viewModelPojo));
+        objectLifecyclePublisher().onPostCreate(ManagedObject.viewmodel(spec, viewModelPojo, Optional.empty()));
         return viewModelPojo;
     }
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java
index 22dd0420c9..c605a71ac4 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java
@@ -97,7 +97,7 @@ public class RepositoryServiceDefault implements RepositoryService {
         if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) {
             throw new PersistFailedException("Object not known to framework (unable to create/obtain an adapter)");
         }
-        // only persist detached entities, otherwise skip
+        // only persist detached or new entities, otherwise skip
         val entityState = MmEntityUtil.getEntityState(adapter);
         if(!entityState.isPersistable()
                 || entityState.isAttached()) {
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
index 84ed080cfb..107e227938 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
@@ -127,8 +127,7 @@ extends DelegatingInvocationHandlerDefault<T> {
                     nsme);
         }
 
-        entityFacet = targetAdapter.getSpecification()
-                .getFacet(EntityFacet.class);
+        entityFacet = targetAdapter.getSpecification().entityFacet().orElse(null);
 
         this.mixeeAdapter = mixeeAdapter;
     }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotBuilder.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotBuilder.java
index ac1bae20da..75e698543a 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotBuilder.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotBuilder.java
@@ -79,7 +79,7 @@ public class XmlSnapshotBuilder {
     }
 
     public XmlSnapshot build() {
-        final ManagedObject adapter = ManagedObject.wrapScalar(specificationLoader, domainObject);
+        final ManagedObject adapter = ManagedObject.adaptScalar(specificationLoader, domainObject);
         final XmlSnapshot snapshot = (schema != null)
                 ? new XmlSnapshot(adapter, schema)
                 : new XmlSnapshot(adapter);
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java
index 669b1ae402..f8ec1e95f6 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java
@@ -86,7 +86,7 @@ public class XmlSnapshotServiceDefault implements XmlSnapshotService {
      */
     @Override
     public XmlSnapshotService.Snapshot snapshotFor(final Object domainObject) {
-        final ManagedObject adapter = ManagedObject.wrapScalar(specificationLoader, domainObject);
+        final ManagedObject adapter = ManagedObject.adaptScalar(specificationLoader, domainObject);
         return new XmlSnapshot(adapter);
     }
 
diff --git a/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/EventProviderAbstract.java b/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/EventProviderAbstract.java
index 012cacff38..8715a7db0f 100644
--- a/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/EventProviderAbstract.java
+++ b/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/EventProviderAbstract.java
@@ -121,7 +121,7 @@ public abstract class EventProviderAbstract implements EventProvider {
             final Object dereferencedObject = dereference(commonContext, domainObjectPojo);
 
             val dereferencedManagedObject =
-                    ManagedObject.wrapScalar(commonContext.getSpecificationLoader(), dereferencedObject);
+                    ManagedObject.adaptScalar(commonContext.getSpecificationLoader(), dereferencedObject);
 
             val oid = ManagedObjects.bookmark(dereferencedManagedObject).orElse(null);
             if(oid!=null) {
diff --git a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/ObjectTypeFactory.java b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/ObjectTypeFactory.java
index d0b7c46a3e..4f15234a8c 100644
--- a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/ObjectTypeFactory.java
+++ b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/ObjectTypeFactory.java
@@ -414,7 +414,7 @@ public class ObjectTypeFactory {
                         Class<?> domainObjectInstanceClass = domainObjectInstance.getClass();
                         ObjectSpecification specification = specificationLoader.loadSpecification(domainObjectInstanceClass);
 
-                        ManagedObject owner = ManagedObject.of(specification, domainObjectInstance);
+                        ManagedObject owner = ManagedObject.adaptScalar(specification, domainObjectInstance);
 
                         ManagedObject managedObject = otom.get(owner);
 
diff --git a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/QueryFieldFactory.java b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/QueryFieldFactory.java
index 2c84794a57..d800229253 100644
--- a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/QueryFieldFactory.java
+++ b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/QueryFieldFactory.java
@@ -123,7 +123,7 @@ public class QueryFieldFactory {
                                 ObjectSpecification specification = specificationLoader
                                         .loadSpecification(domainObjectInstanceClass);
 
-                                ManagedObject owner = ManagedObject.of(specification, domainObjectInstance);
+                                ManagedObject owner = ManagedObject.adaptScalar(specification, domainObjectInstance);
 
                                 ActionInteractionHead actionInteractionHead = objectAction.interactionHead(owner);
 
@@ -133,10 +133,7 @@ public class QueryFieldFactory {
                                     Object argumentValue = arguments.get(oap.getId());
                                     ObjectSpecification elementType = oap.getElementType();
 
-                                    if (argumentValue == null)
-                                        return ManagedObject.empty(elementType);
-                                    return ManagedObject.of(elementType, argumentValue);
-
+                                    return ManagedObject.adaptScalar(elementType, argumentValue);
 
                                 }).collect(Can.toCan());
 
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java
index d95e499217..389d0063bc 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java
@@ -20,8 +20,6 @@ package org.apache.isis.incubator.viewer.vaadin.ui.binding;
 
 import java.util.function.UnaryOperator;
 
-import org.springframework.lang.Nullable;
-
 import com.vaadin.flow.component.HasValidation;
 import com.vaadin.flow.component.HasValue;
 import com.vaadin.flow.data.binder.Binder;
@@ -30,6 +28,8 @@ import com.vaadin.flow.data.binder.Setter;
 import com.vaadin.flow.data.converter.Converter;
 import com.vaadin.flow.function.ValueProvider;
 
+import org.springframework.lang.Nullable;
+
 import org.apache.isis.commons.binding.Bindable;
 import org.apache.isis.commons.binding.Observable;
 import org.apache.isis.commons.internal.base._Casts;
@@ -361,7 +361,7 @@ public final class BindingsVaa {
         //SETTER
         @Override
         public void accept(@NonNull final Bindable<ManagedObject> target, final V fieldValue) {
-            target.setValue(ManagedObject.of(valueSpec, fieldValue));
+            target.setValue(ManagedObject.adaptScalar(valueSpec, fieldValue));
         }
 
 
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java
index 410d455401..8be754c905 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java
@@ -31,11 +31,11 @@ import javax.jdo.listener.StoreLifecycleListener;
 
 import org.datanucleus.enhancement.Persistable;
 
+import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.facets.object.publish.entitychange.EntityChangePublishingFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
-import org.apache.isis.core.metamodel.objectmanager.ObjectManager.EntityAdaptingMode;
 import org.apache.isis.core.metamodel.services.objectlifecycle.ObjectLifecyclePublisher;
 import org.apache.isis.persistence.jdo.datanucleus.entities.DnObjectProviderForIsis;
 
@@ -89,7 +89,7 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif
     public void postLoad(final InstanceLifecycleEvent event) {
         log.debug("postLoad {}", ()->_Utils.debug(event));
         final Persistable pojo = _Utils.persistableFor(event);
-        val entity = adaptEntityAndInjectServices(pojo, EntityAdaptingMode.MEMOIZE_BOOKMARK);
+        val entity = adaptEntity(pojo);
 
         objectLifecyclePublisher.onPostLoad(entity);
 
@@ -101,10 +101,11 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif
 
         final Persistable pojo = _Utils.persistableFor(event);
 
-        /* Called either when an entity is initially persisted, or when an entity is updated; fires the appropriate
+        /* Called either when an entity is initially persisted,
+         * or when an entity is updated; fires the appropriate
          * lifecycle callback. So filter for those events when initially persisting. */
         if(pojo.dnGetStateManager().isNew(pojo)) {
-            val entity = adaptEntity(pojo, EntityAdaptingMode.SKIP_MEMOIZATION);
+            val entity = adaptEntity(pojo);
             objectLifecyclePublisher.onPrePersist(entity);
         }
     }
@@ -114,7 +115,7 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif
         log.debug("postStore {}", ()->_Utils.debug(event));
 
         final Persistable pojo = _Utils.persistableFor(event);
-        val entity = adaptEntityAndInjectServices(pojo, EntityAdaptingMode.MEMOIZE_BOOKMARK);
+        val entity = adaptEntity(pojo);
 
         if(EntityChangePublishingFacet.isPublishingEnabled(entity.getSpecification())) {
 
@@ -150,7 +151,7 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif
     }
 
     private final void doPreDirty(final Persistable pojo) {
-        val entity = adaptEntity(pojo, EntityAdaptingMode.MEMOIZE_BOOKMARK);
+        val entity = adaptEntity(pojo);
         objectLifecyclePublisher.onPreUpdate(entity, null);
     }
 
@@ -164,7 +165,10 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif
         log.debug("preDelete {}", ()->_Utils.debug(event));
 
         final Persistable pojo = _Utils.persistableFor(event);
-        val entity = adaptEntity(pojo, EntityAdaptingMode.SKIP_MEMOIZATION);
+
+        _Assert.assertNotNull(pojo.dnGetObjectId());
+        //val entity = adaptEntity(pojo, EntityAdaptingMode.NOT_YET_BOOKMARKABLE);
+        val entity = adaptEntity(pojo);
 
         objectLifecyclePublisher.onPreRemove(entity);
     }
@@ -204,15 +208,8 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif
     // -- HELPER
 
     private ManagedObject adaptEntity(
-            final @NonNull Persistable pojo,
-            final @NonNull EntityAdaptingMode bookmarking) {
-        return _Utils.adaptEntity(metaModelContext, pojo, bookmarking);
-    }
-
-    private ManagedObject adaptEntityAndInjectServices(
-            final @NonNull Persistable pojo,
-            final @NonNull EntityAdaptingMode bookmarking) {
-        return _Utils.adaptEntityAndInjectServices(metaModelContext, pojo, bookmarking);
+            final @NonNull Persistable pojo) {
+        return _Utils.adaptEntity(metaModelContext, pojo);
     }
 
 }
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/_Utils.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/_Utils.java
index 6fa5f63e90..cd46eb93c0 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/_Utils.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/_Utils.java
@@ -29,7 +29,6 @@ import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 import org.apache.isis.core.metamodel.object.ManagedObjects;
-import org.apache.isis.core.metamodel.objectmanager.ObjectManager.EntityAdaptingMode;
 
 import lombok.NonNull;
 import lombok.val;
@@ -64,40 +63,36 @@ final class _Utils {
 
     ManagedObject adaptEntity(
             final @NonNull MetaModelContext mmc,
-            final @NonNull Object entityPojo,
-            final @NonNull EntityAdaptingMode bookmarking) {
+            final @NonNull Object entityPojo) {
 
         val objectManager = mmc.getObjectManager();
-        val entity = objectManager.adapt(entityPojo, bookmarking);
+        val entity = objectManager.adapt(entityPojo);
         _Assert.assertTrue(entity.getSpecification().isEntity());
         return entity;
     }
 
     ManagedObject adaptNullableEntity(
             final @NonNull MetaModelContext mmc,
-            final @Nullable Object entityPojo,
-            final @NonNull EntityAdaptingMode bookmarking) {
+            final @Nullable Object entityPojo) {
 
         return entityPojo == null
                 ? ManagedObject.unspecified()
-                : adaptEntity(mmc, entityPojo, bookmarking);
+                : adaptEntity(mmc, entityPojo);
     }
 
     ManagedObject adaptNullableAndInjectServices(
             final @NonNull MetaModelContext mmc,
-            final @Nullable Object entityPojo,
-            final @NonNull EntityAdaptingMode bookmarking) {
+            final @Nullable Object entityPojo) {
 
         return entityPojo == null
                 ? ManagedObject.unspecified()
-                : adaptEntityAndInjectServices(mmc, entityPojo, bookmarking);
+                : adaptEntityAndInjectServices(mmc, entityPojo);
     }
 
     ManagedObject adaptEntityAndInjectServices(
             final @NonNull MetaModelContext mmc,
-            final @NonNull Object entityPojo,
-            final @NonNull EntityAdaptingMode bookmarking) {
-        return injectServices(mmc, adaptEntity(mmc, entityPojo, bookmarking));
+            final @NonNull Object entityPojo) {
+        return injectServices(mmc, adaptEntity(mmc, entityPojo));
     }
 
 
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/entities/DnEntityStateProvider.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/entities/DnEntityStateProvider.java
index f1d894d971..0e2fa29a70 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/entities/DnEntityStateProvider.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/entities/DnEntityStateProvider.java
@@ -21,9 +21,8 @@ package org.apache.isis.persistence.jdo.datanucleus.entities;
 import java.lang.reflect.Method;
 import java.util.Set;
 
-import org.springframework.lang.Nullable;
-
 import org.datanucleus.enhancement.Persistable;
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 
 import org.apache.isis.applib.services.repository.EntityState;
@@ -76,7 +75,9 @@ public class DnEntityStateProvider implements JdoFacetContext {
             }
             val isPersistent = persistable.dnIsPersistent();
             if(isPersistent) {
-                return EntityState.PERSISTABLE_ATTACHED;
+                return persistable.dnGetObjectId()!=null
+                        ? EntityState.PERSISTABLE_ATTACHED
+                        : EntityState.PERSISTABLE_NEW;
             }
             return EntityState.PERSISTABLE_DETACHED;
         }
@@ -100,7 +101,7 @@ public class DnEntityStateProvider implements JdoFacetContext {
     }
 
     @Override
-    public EntityFacet createEntityFacet(final FacetHolder facetHolder, Class<?> entityClass) {
+    public EntityFacet createEntityFacet(final FacetHolder facetHolder, final Class<?> entityClass) {
         return new JdoEntityFacet(facetHolder, entityClass);
     }
 
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java
index ac2b3741e3..6865d76cd1 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java
@@ -21,6 +21,7 @@ package org.apache.isis.persistence.jdo.datanucleus.metamodel.facets.entity;
 import java.lang.reflect.Method;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Supplier;
 
@@ -46,8 +47,6 @@ import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.assertions._Assert;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.collections._Maps;
-import org.apache.isis.commons.internal.debug._Debug;
-import org.apache.isis.commons.internal.debug.xray.XrayUi;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.beans.PersistenceStack;
 import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
@@ -56,7 +55,6 @@ import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 import org.apache.isis.core.metamodel.services.objectlifecycle.ObjectLifecyclePublisher;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.runtime.idstringifier.IdStringifierService;
 import org.apache.isis.persistence.jdo.datanucleus.entities.DnEntityStateProvider;
 import org.apache.isis.persistence.jdo.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacetFactory;
@@ -98,48 +96,22 @@ implements EntityFacet {
     }
 
     @Override
-    public String identifierFor(final Object pojo) {
+    public Optional<String> identifierFor(final Object pojo) {
 
-        if(pojo==null) {
-            throw _Exceptions.illegalArgument(
-                    "The persistence layer cannot identify a pojo that is null (given type %s)",
-                    entityClass.getName());
-        }
-
-        if(!isPersistableType(pojo.getClass())) {
-            throw _Exceptions.illegalArgument(
-                    "The persistence layer does not recognize given type %s",
-                    pojo.getClass().getName());
+        if (!getEntityState(pojo).isAttached()) {
+            return Optional.empty();
         }
 
         val pm = getPersistenceManager();
-        var primaryKey = pm.getObjectId(pojo);
-
-//        if(primaryKey==null) {
-//            pm.makePersistent(pojo);
-//            primaryKey = pm.getObjectId(pojo);
-//        }
-
-        if(primaryKey==null) {
-
-            _Debug.onCondition(XrayUi.isXrayEnabled(), ()->{
-                _Debug.log("detached entity detected %s", pojo);
-            });
-
-            throw _Exceptions.illegalArgument(
-                    "The persistence layer does not recognize given object of type %s, "
-                    + "meaning the object has no identifier that associates it with the persistence layer. "
-                    + "(most likely, because the object is detached, eg. was not persisted after being new-ed up)",
-                    pojo.getClass().getName());
-        }
+        var primaryKeyIfAny = pm.getObjectId(pojo);
 
-        return idStringifierService.enstringPrimaryKey(primaryKey.getClass(), primaryKey);
+        return Optional.ofNullable(primaryKeyIfAny)
+                .map(primaryKey->
+                    idStringifierService.enstringPrimaryKey(primaryKey.getClass(), primaryKey));
     }
 
-
     @Override
-    public ManagedObject fetchByIdentifier(
-            final @NonNull Bookmark bookmark) {
+    public Optional<Object> fetchByBookmark(final @NonNull Bookmark bookmark) {
 
         log.debug("fetchEntity; bookmark={}", bookmark);
 
@@ -166,14 +138,7 @@ implements EntityFacet {
             throw e;
         }
 
-        if (entityPojo == null) {
-            throw new ObjectNotFoundException(""+bookmark);
-        }
-
-        val actualEntitySpec = getSpecificationLoader().specForTypeElseFail(entityPojo.getClass());
-        getServiceInjector().injectServicesInto(entityPojo); // might be redundant
-        //TODO integrate with entity change tracking
-        return ManagedObject.bookmarked(actualEntitySpec, entityPojo, bookmark);
+        return Optional.ofNullable(entityPojo);
     }
 
     private Map<Class<?>, Class<?>> primaryKeyClassByEntityClass = new ConcurrentHashMap<>();
@@ -209,11 +174,6 @@ implements EntityFacet {
         }
     }
 
-    private ObjectSpecification getEntitySpec() {
-        return getSpecificationLoader().specForType(entityClass)
-                .orElseThrow(() -> new IllegalStateException(String.format("Could not load specification for entity class '%s'", entityClass)));
-    }
-
     @Override
     public Can<ManagedObject> fetchByQuery(final Query<?> query) {
 
@@ -294,7 +254,7 @@ implements EntityFacet {
 
         if(pojo==null
                 || !isPersistableType(pojo.getClass())
-                || DnEntityStateProvider.entityState(pojo).isAttached()) {
+                || DnEntityStateProvider.entityState(pojo).isAttachedOrNew()) {
             return; // nothing to do
         }
 
@@ -404,7 +364,8 @@ implements EntityFacet {
 
     private Can<ManagedObject> fetchWithinTransaction(final Supplier<List<?>> fetcher) {
 
-        val objectLifecyclePublisher = getFacetHolder().getServiceRegistry().lookupServiceElseFail(ObjectLifecyclePublisher.class);
+        val objectLifecyclePublisher = getFacetHolder().getServiceRegistry()
+                .lookupServiceElseFail(ObjectLifecyclePublisher.class);
 
         return getTransactionalProcessor().callWithinCurrentTransactionElseCreateNew(
                 ()->_NullSafe.stream(fetcher.get())
@@ -413,7 +374,9 @@ implements EntityFacet {
                 .getValue().orElseThrow();
     }
 
-    private ManagedObject adopt(final ObjectLifecyclePublisher objectLifecyclePublisher, final Object fetchedObject) {
+    private ManagedObject adopt(
+            final ObjectLifecyclePublisher objectLifecyclePublisher,
+            final Object fetchedObject) {
         // handles lifecycle callbacks and injects services
 
         // ought not to be necessary, however for some queries it seems that the
diff --git a/persistence/jpa/integration/src/main/java/org/apache/isis/persistence/jpa/integration/entity/JpaEntityFacet.java b/persistence/jpa/integration/src/main/java/org/apache/isis/persistence/jpa/integration/entity/JpaEntityFacet.java
index adc42ad8dc..7c764ec931 100644
--- a/persistence/jpa/integration/src/main/java/org/apache/isis/persistence/jpa/integration/entity/JpaEntityFacet.java
+++ b/persistence/jpa/integration/src/main/java/org/apache/isis/persistence/jpa/integration/entity/JpaEntityFacet.java
@@ -28,8 +28,8 @@ import javax.persistence.metamodel.EntityType;
 
 import org.eclipse.persistence.exceptions.DescriptorException;
 import org.springframework.data.jpa.repository.JpaContext;
+import org.springframework.lang.Nullable;
 
-import org.apache.isis.applib.exceptions.unrecoverable.ObjectNotFoundException;
 import org.apache.isis.applib.query.AllInstancesQuery;
 import org.apache.isis.applib.query.NamedQuery;
 import org.apache.isis.applib.query.Query;
@@ -45,7 +45,6 @@ import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.runtime.idstringifier.IdStringifierService;
 
 import lombok.NonNull;
@@ -80,32 +79,23 @@ public class JpaEntityFacet
     }
 
     @Override
-    public String identifierFor(final Object pojo) {
+    public Optional<String> identifierFor(final @Nullable Object pojo) {
 
-        if (pojo == null) {
-            throw _Exceptions.illegalArgument(
-                    "The persistence layer cannot identify a pojo that is null (given type %s)",
-                    entityClass.getName());
+        if (!getEntityState(pojo).isAttached()) {
+            return Optional.empty();
         }
 
         val entityManager = getEntityManager();
         val persistenceUnitUtil = getPersistenceUnitUtil(entityManager);
-        val primaryKey = persistenceUnitUtil.getIdentifier(pojo);
-
-        if (primaryKey == null) {
-            throw _Exceptions.illegalArgument(
-                    "The persistence layer does not recognize given object of type %s, "
-                            + "meaning the object has no identifier that associates it with the persistence layer. "
-                            + "(most likely, because the object is detached, eg. was not persisted after being new-ed up)",
-                    pojo.getClass().getName());
-        }
+        val primaryKeyIfAny = persistenceUnitUtil.getIdentifier(pojo);
 
-        return idStringifierService.enstringPrimaryKey(getPrimaryKeyType(), primaryKey);
+        return Optional.ofNullable(primaryKeyIfAny)
+                .map(primaryKey->
+                    idStringifierService.enstringPrimaryKey(getPrimaryKeyType(), primaryKey));
     }
 
     @Override
-    public ManagedObject fetchByIdentifier(
-            final @NonNull Bookmark bookmark) {
+    public Optional<Object> fetchByBookmark(final @NonNull Bookmark bookmark) {
 
         log.debug("fetchEntity; bookmark={}", bookmark);
 
@@ -114,18 +104,7 @@ public class JpaEntityFacet
 
         val entityManager = getEntityManager();
         val entityPojo = entityManager.find(entityClass, primaryKey);
-
-        if (entityPojo == null) {
-            throw new ObjectNotFoundException("" + bookmark);
-        }
-
-        final ObjectSpecification entitySpec = getEntitySpec();
-        return ManagedObject.bookmarked(entitySpec, entityPojo, bookmark);
-    }
-
-    private ObjectSpecification getEntitySpec() {
-        return getSpecificationLoader().specForType(entityClass)
-                            .orElseThrow(() -> new IllegalStateException(String.format("Could not load specification for entity class '%s'", entityClass)));
+        return Optional.ofNullable(entityPojo);
     }
 
     private Class<?> getPrimaryKeyType() {
@@ -164,10 +143,10 @@ public class JpaEntityFacet
                 typedQuery.setMaxResults(range.getLimitAsInt());
             }
 
-            val spec = getEntitySpec();
+            val entitySpec = getEntitySpecification();
             return Can.ofStream(
                     typedQuery.getResultStream()
-                            .map(entity -> ManagedObject.of(spec, entity)));
+                            .map(entity -> ManagedObject.adaptScalar(entitySpec, entity)));
 
         } else if (query instanceof NamedQuery) {
 
@@ -191,10 +170,10 @@ public class JpaEntityFacet
                     .forEach((paramName, paramValue) ->
                             namedQuery.setParameter(paramName, paramValue));
 
-            val spec = getEntitySpec();
+            val entitySpec = getEntitySpecification();
             return Can.ofStream(
                     namedQuery.getResultStream()
-                            .map(entity -> ManagedObject.of(spec, entity)));
+                            .map(entity -> ManagedObject.adaptScalar(entitySpec, entity)));
 
         }
 
diff --git a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
index 82a33d1151..cffab3a342 100644
--- a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
+++ b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
@@ -262,7 +262,7 @@ class DomainModelTest_usingGoodDomain {
             assertEquals("inherited title", titleService.titleOf(instance));
             assertEquals("inherited icon", titleService.iconNameOf(instance));
 
-            val domainObject = ManagedObject.of(spec, instance);
+            val domainObject = ManagedObject.adaptScalar(spec, instance);
             assertEquals("inherited title", domainObject.getTitle());
             assertEquals("inherited icon", iconFacet.iconName(domainObject));
         }
@@ -538,7 +538,7 @@ class DomainModelTest_usingGoodDomain {
 
         val objectSpec = specificationLoader.specForTypeElseFail(ProperMemberSupport.class);
         val member = objectSpec.getMemberElseFail(memberId);
-        val sampleObject = ManagedObject.of(objectSpec, new ProperMemberSupport());
+        val sampleObject = ManagedObject.adaptScalar(objectSpec, new ProperMemberSupport());
 
         assertEquals(named, member.getFriendlyName(()->sampleObject));
         assertEquals(described, member.getDescription(()->sampleObject).orElse(null));
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiChoices.java b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiChoices.java
index d747b60c54..9608666727 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiChoices.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiChoices.java
@@ -74,13 +74,9 @@ public class SimulatedUiChoices extends HasValueValidation {
      * @param choiceIndices
      */
     public void simulateMultiChoiceSelect(final int ... choiceIndices) {
-        val newValuePojos = choiceBox.getValue()
-                .pickByIndex(choiceIndices)
-                .map(ManagedObject::getPojo);
-        val newValue = ManagedObject.of(
-                valueSpecification,
-                newValuePojos.toList());
-        selectedItem.setValue(newValue);
+        val newValues = choiceBox.getValue()
+                .pickByIndex(choiceIndices);
+        selectedItem.setValue(ManagedObject.packed(valueSpecification, newValues));
     }
 
     public ManagedObject getValue() {
diff --git a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiComponent.java b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiComponent.java
index c97fce05e6..4525ca2023 100644
--- a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiComponent.java
+++ b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiComponent.java
@@ -44,7 +44,7 @@ public class SimulatedUiComponent extends HasValueValidation {
     }
 
     public void simulateValueChange(final Object newValue) {
-        value.setValue(ManagedObject.of(valueSpec, newValue));
+        value.setValue(ManagedObject.adaptScalar(valueSpec, newValue));
     }
 
     public ManagedObject getValue() {
diff --git a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java
index de7f9e704a..9a4a137e4d 100644
--- a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java
+++ b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java
@@ -33,7 +33,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import org.apache.isis.applib.services.metamodel.BeanSort;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
 import org.apache.isis.core.config.presets.IsisPresets;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.persistence.jdo.provider.metamodel.facets.object.datastoreidentity.JdoDatastoreIdentityFacet;
 import org.apache.isis.persistence.jdo.provider.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacet;
@@ -95,7 +94,7 @@ class DomainModelTest {
         val entitySpec = specificationLoader.loadSpecification(JdoEntityMetaAnnotated.class);
 
         assertEquals(BeanSort.ENTITY, entitySpec.getBeanSort());
-        assertNotNull(entitySpec.getFacet(EntityFacet.class));
+        assertNotNull(entitySpec.entityFacetElseFail());
 
         //@PersistenceCapable(identityType = IdentityType.DATASTORE)
         val persistenceCapableFacet = entitySpec.getFacet(JdoPersistenceCapableFacet.class);
diff --git a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/injecting/jdo/JdoEntityInjectingTest.java b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/injecting/jdo/JdoEntityInjectingTest.java
index fe8cc62357..46a3c06cb2 100644
--- a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/injecting/jdo/JdoEntityInjectingTest.java
+++ b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/injecting/jdo/JdoEntityInjectingTest.java
@@ -71,7 +71,7 @@ class JdoEntityInjectingTest extends IsisIntegrationTestAbstract {
     void init() {
         // given
         jdoTestFixtures.reinstall(()->kvStore.clear(JdoBook.class));
-        assertInjectCountRange(3, 9); //TODO there is some injection redundancy
+        assertInjectCountRange(3, 12); //TODO there is some injection redundancy
     }
 
 
diff --git a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java
index 1f409f9306..3589d7ae9e 100644
--- a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java
+++ b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java
@@ -29,7 +29,6 @@ import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestMethodOrder;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.test.annotation.Rollback;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.transaction.PlatformTransactionManager;
@@ -43,7 +42,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.config.presets.IsisPresets;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.testdomain.conf.Configuration_usingJpa;
 import org.apache.isis.testdomain.jdo.JdoTestFixtures;
@@ -101,11 +99,11 @@ class JpaBootstrappingTest extends IsisIntegrationTestAbstract {
     void jpaEntities_shouldBeRecognisedAsSuch() {
         val productSpec = specLoader.loadSpecification(JpaProduct.class);
         assertTrue(productSpec.isEntity());
-        assertNotNull(productSpec.getFacet(EntityFacet.class));
+        assertNotNull(productSpec.entityFacetElseFail());
 
         val inventorySpec = specLoader.loadSpecification(JpaInventory.class);
         assertTrue(inventorySpec.isEntity());
-        assertNotNull(inventorySpec.getFacet(EntityFacet.class));
+        assertNotNull(inventorySpec.entityFacetElseFail());
     }
 
     @Test @Order(1) @Rollback(false)
diff --git a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/entitylifecycle/JpaNonGeneratedStringIdEntityLifecycleTest.java b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/entitylifecycle/JpaNonGeneratedStringIdEntityLifecycleTest.java
index 2cad44d6b7..f9405155ea 100644
--- a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/entitylifecycle/JpaNonGeneratedStringIdEntityLifecycleTest.java
+++ b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/entitylifecycle/JpaNonGeneratedStringIdEntityLifecycleTest.java
@@ -90,6 +90,8 @@ class JpaNonGeneratedStringIdEntityLifecycleTest {
 
         repository.persist(entity.getPojo());
 
+        System.err.printf("entity %s%n", entity);
+
         assertEquals(
                 EntityState.PERSISTABLE_ATTACHED,
                 MmEntityUtil.getEntityState(entity));
diff --git a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/springdata/SpringDataJpaBootstrappingTest.java b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/springdata/SpringDataJpaBootstrappingTest.java
index b780d2dbe3..e9ec920458 100644
--- a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/springdata/SpringDataJpaBootstrappingTest.java
+++ b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/springdata/SpringDataJpaBootstrappingTest.java
@@ -42,7 +42,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.core.config.presets.IsisPresets;
-import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.testdomain.conf.Configuration_usingSpringDataJpa;
 import org.apache.isis.testdomain.jpa.springdata.Employee;
@@ -111,7 +110,7 @@ class SpringDataJpaBootstrappingTest extends IsisIntegrationTestAbstract {
     void jpaEntities_shouldBeRecognisedAsSuch() {
         val productSpec = specLoader.loadSpecification(Employee.class);
         assertTrue(productSpec.isEntity());
-        assertNotNull(productSpec.getFacet(EntityFacet.class));
+        assertNotNull(productSpec.entityFacetElseFail());
     }
 
     @Test @Order(1) @Rollback(false)
diff --git a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
index fa82d0061d..e2016a76af 100644
--- a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
+++ b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java
@@ -205,7 +205,7 @@ class ValueSemanticsTest {
 
                                 val spec = specLoader.specForTypeElseFail(valueMixin.getClass());
                                 val interaction = ActionInteraction
-                                        .start(ManagedObject.of(spec,  valueMixin), "act", Where.ANYWHERE);
+                                        .start(ManagedObject.mixin(spec,  valueMixin), "act", Where.ANYWHERE);
 
                                 val pendingParams = interaction
                                         .startParameterNegotiation()
diff --git a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java
index e4c1db52e7..da62128771 100644
--- a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java
+++ b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java
@@ -115,10 +115,10 @@ public class ValueSemanticsTester<T> {
             val command = interactionService.currentInteractionElseFail().getCommand();
 
             val propInteraction = PropertyInteraction
-                    .wrap(ManagedProperty.of(ManagedObject.of(objSpec, domainObject), prop, Where.OBJECT_FORMS));
+                    .wrap(ManagedProperty.of(ManagedObject.adaptScalar(objSpec, domainObject), prop, Where.OBJECT_FORMS));
 
             propInteraction.modifyProperty(managedProp->
-                ManagedObject.of(managedProp.getElementType(), newProperyValueProvider.apply(managedProp)));
+                ManagedObject.adaptScalar(managedProp.getElementType(), newProperyValueProvider.apply(managedProp)));
 
             probe.testCommand(context, command);
         });
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJdo.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJdo.java
index afdebc781d..b734d06fc5 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJdo.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJdo.java
@@ -227,7 +227,7 @@ extends PublishingTestFactoryAbstract {
                     val managedProperty = propertyInteraction.getManagedPropertyElseThrow(__->_Exceptions.noSuchElement());
                     val propertyModel = managedProperty.startNegotiation();
                     val propertySpec = managedProperty.getElementType();
-                    propertyModel.getValue().setValue(ManagedObject.of(propertySpec, "Book #2"));
+                    propertyModel.getValue().setValue(ManagedObject.value(propertySpec, "Book #2"));
                     propertyModel.submit();
 
                 });
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJpa.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJpa.java
index 3e58308d07..ca7c5402b9 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJpa.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJpa.java
@@ -223,7 +223,7 @@ extends PublishingTestFactoryAbstract {
                     val managedProperty = propertyInteraction.getManagedPropertyElseThrow(__->_Exceptions.noSuchElement());
                     val propertyModel = managedProperty.startNegotiation();
                     val propertySpec = managedProperty.getElementType();
-                    propertyModel.getValue().setValue(ManagedObject.of(propertySpec, "Book #2"));
+                    propertyModel.getValue().setValue(ManagedObject.value(propertySpec, "Book #2"));
                     propertyModel.submit();
 
                 });
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java
index 09424fb3b2..f47b5fb5cd 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java
@@ -625,7 +625,9 @@ public class DomainObjectTesterFactory {
 
         @SuppressWarnings("unchecked")
         static void updatePojo(final ManagedValue managedValue, final UnaryOperator replacer) {
-            managedValue.update(v->ManagedObject.of(v.getSpecification(), replacer.apply(v.getPojo())));
+            managedValue.update(v->ManagedObject.adaptScalar(
+                    v.getSpecification(),
+                    replacer.apply(v.getPojo())));
         }
 
         @SneakyThrows
@@ -1128,7 +1130,10 @@ public class DomainObjectTesterFactory {
 
         protected Tester<T> init() {
             this.objectSpecification = specificationLoader.specForTypeElseFail(domainObjectType);
-            this.vm = ManagedObject.of(objectSpecification, factoryService.viewModel(domainObjectType));
+            this.vm = ManagedObject.viewmodel(
+                    objectSpecification,
+                    factoryService.viewModel(domainObjectType),
+                    Optional.empty());
             return this;
         }
 
diff --git a/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java b/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java
index 06b02e91c7..13e480f34c 100644
--- a/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java
+++ b/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java
@@ -34,7 +34,7 @@ implements BindingConverter<ManagedObject, T> {
 
     @Override
     public ManagedObject toLeft(final T pojo) {
-        return ManagedObject.of(getValueSpecification(), pojo);
+        return ManagedObject.value(getValueSpecification(), pojo);
     }
 
     @Override
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BooleanModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BooleanModel.java
index b903f15afb..2b0d5a5a88 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BooleanModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BooleanModel.java
@@ -59,7 +59,7 @@ extends ChainingModel<Boolean> {
 
     @Override
     public void setObject(final Boolean value) {
-        val adaptedValue = ManagedObject.of(
+        val adaptedValue = ManagedObject.value(
                 scalarModel().getScalarTypeSpec(),
                 (value==null
                     && isPrimitive)
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
index eb204fc3df..bc0a884369 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java
@@ -27,7 +27,6 @@ import org.apache.isis.applib.annotation.BookmarkPolicy;
 import org.apache.isis.applib.id.LogicalType;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.commons.internal.collections._Collections;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 import org.apache.isis.core.metamodel.object.ManagedObjects;
 import org.apache.isis.core.metamodel.object.PackedManagedObject;
@@ -83,7 +82,7 @@ extends ModelAbstract<ManagedObject> {
 
         super.setObject(adapter);
 
-        if(_Collections.isCollectionOrArrayOrCanType(adapter.getPojo().getClass())) {
+        if(adapter instanceof PackedManagedObject) {
             setObjectCollection((PackedManagedObject)adapter);
         } else {
             memento = super.getMementoService().mementoForSingle(adapter);
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/PageParameterUtils.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/PageParameterUtils.java
index a68acc0863..44075d5f0b 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/PageParameterUtils.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/PageParameterUtils.java
@@ -229,7 +229,7 @@ public class PageParameterUtils {
         }
 
         if(objSpec.isValue()) {
-            return ManagedObject.of(objSpec,
+            return ManagedObject.value(objSpec,
                     Facets.valueSerializerElseFail(objSpec, objSpec.getCorrespondingClass())
                         .fromEncodedString(Format.JSON, encoded));
         }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
index 621dfb6fcb..e205c9a0a3 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
@@ -259,7 +259,7 @@ class IsisToWicketTreeAdapter {
             this.commonContext = commonContext;
             this.factoryService = commonContext.lookupServiceElseFail(FactoryService.class);
             this.pojoToAdapter = pojo ->
-                ManagedObject.wrapScalar(commonContext.getSpecificationLoader(), pojo);
+                ManagedObject.adaptScalar(commonContext.getSpecificationLoader(), pojo);
         }
 
         private TreeAdapter wrappedTreeAdapter() {
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/DeepLinkServiceWicket.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/DeepLinkServiceWicket.java
index bbfdde5944..db3751d98c 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/DeepLinkServiceWicket.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/DeepLinkServiceWicket.java
@@ -58,7 +58,7 @@ public class DeepLinkServiceWicket implements DeepLinkService {
     @Override
     public URI deepLinkFor(final Object domainObject) {
 
-        final ManagedObject objectAdapter = ManagedObject.wrapScalar(specificationLoader, domainObject);
+        final ManagedObject objectAdapter = ManagedObject.adaptScalar(specificationLoader, domainObject);
 
         final PageParameters pageParameters = PageParameterUtils.createPageParametersForObject(objectAdapter);