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 2020/12/31 13:01:28 UTC

[isis] branch 2033-Spring_Data_Integration updated: ISIS-2033: re-implement transaction events on top of spring transaction infra.

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

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


The following commit(s) were added to refs/heads/2033-Spring_Data_Integration by this push:
     new b68d990  ISIS-2033: re-implement transaction events on top of spring transaction infra.
b68d990 is described below

commit b68d9900ffac6011b7017d583d05b199b96fe55e
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Dec 31 14:01:04 2020 +0100

    ISIS-2033: re-implement transaction events on top of spring transaction
    infra.
---
 ...InteractionScope.java => InteractionScope.java} |  14 +--
 .../applib/services/xactn/TransactionState.java    |   4 +
 .../interaction/IsisModuleCoreInteraction.java     |   4 +-
 ...leEvent.java => InteractionLifecycleEvent.java} |   2 +-
 .../interaction/integration/IsisRequestCycle.java  |  40 +------
 ...InteractionScope.java => InteractionScope.java} |  19 ++--
 ... InteractionScopeBeanFactoryPostProcessor.java} |  20 ++--
 ....java => InteractionScopeLifecycleHandler.java} |   5 +-
 .../interaction/session/InteractionFactory.java    |   5 -
 .../core/runtime/context/IsisAppCommonContext.java |   8 --
 .../runtime/events/AppLifecycleEventService.java   |  17 ++-
 .../publish/ExecutionPublisherDefault.java         |   4 +-
 .../QueryResultsCacheDefault.java                  |  17 ++-
 .../scratchpad/ScratchpadDefault.java              |   8 +-
 .../session/InteractionFactoryDefault.java         |  34 ++----
 .../xactn/TransactionServiceSpring.java            |  11 +-
 .../changetracking/EntityChangeTrackerDefault.java |  54 ++++-----
 .../transaction/events/TransactionBeginEvent.java} |  17 +--
 .../transaction/events/TransactionEndedEvent.java} |  16 +--
 .../events/TransactionEndingEvent.java}            |  16 +--
 .../events/TransactionEventAbstract.java           |  41 +++----
 .../ApplicationLayerAwareTransactionManager.java   |  64 +++++++++++
 .../viewer/javafx/ui/main/UiBuilderFx.java         |  15 ++-
 .../config/DnEntityDiscoveryListener.java          |   7 +-
 .../jdo/integration/IsisModuleJdoIntegration.java  |  11 +-
 .../lifecycles/JdoPersistenceLifecycleService.java |  12 +-
 .../metamodel/facets/entity/JdoEntityFacet.java    |   8 +-
 .../persistence/JdoPersistenceSession5.java        | 122 +++++++++++++--------
 .../jdo/lightweight/IsisModuleJdoLightweight.java  |  11 +-
 .../applayer/ApplicationLayerTestFactory.java      |  47 ++++----
 .../testdomain/conf/Configuration_headless.java    |  45 +-------
 .../util/interaction/InteractionBoundaryProbe.java |  57 ++++++----
 .../isis/JdoIsisEntityChangesPublishingTest.java   |  17 ++-
 .../jdo/spring/JdoSpringBootstrappingTest.java     |  12 +-
 .../JdoSpringTransactionScopeListenerTest.java     |  11 ++
 .../acceptheader/AcceptHeaderServiceForRest.java   |  14 +--
 .../viewer/integration/WebRequestCycleForIsis.java |   3 +-
 37 files changed, 419 insertions(+), 393 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/annotation/IsisInteractionScope.java b/api/applib/src/main/java/org/apache/isis/applib/annotation/InteractionScope.java
similarity index 80%
rename from api/applib/src/main/java/org/apache/isis/applib/annotation/IsisInteractionScope.java
rename to api/applib/src/main/java/org/apache/isis/applib/annotation/InteractionScope.java
index f69da06..b47ea45 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/annotation/IsisInteractionScope.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/annotation/InteractionScope.java
@@ -27,13 +27,13 @@ import java.lang.annotation.Target;
 import org.springframework.context.annotation.Scope;
 
 /**
- * {@code @IsisInteractionScope} is a specialization of {@link Scope @Scope} for a
- * component whose lifecycle is bound to the current top-level IsisInteraction.
+ * {@code @InteractionScope} is a specialization of {@link Scope @Scope} for a
+ * component whose lifecycle is bound to the current top-level Interaction.
  *
- * <p>Specifically, {@code @IsisInteractionScope} is a <em>composed annotation</em> that
- * acts as a shortcut for {@code @Scope("isis-interaction")}.
+ * <p>Specifically, {@code @InteractionScope} is a <em>composed annotation</em> that
+ * acts as a shortcut for {@code @Scope("interaction")}.
  *
- * <p>{@code @IsisInteractionScope} may be used as a meta-annotation to create custom
+ * <p>{@code @InteractionScope} may be used as a meta-annotation to create custom
  * composed annotations.
  *
  * @since 2.0 {@index}
@@ -46,7 +46,7 @@ import org.springframework.context.annotation.Scope;
 @Target({ElementType.TYPE, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
-@Scope("isis-interaction")
-public @interface IsisInteractionScope {
+@Scope("interaction")
+public @interface InteractionScope {
 
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionState.java b/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionState.java
index 05d23e5..3acb14f 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionState.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionState.java
@@ -92,6 +92,10 @@ public enum TransactionState {
         return this == COMMITTED || this == ABORTED;
     }
 
+    public boolean isInProgress() {
+        return this == IN_PROGRESS;
+    }
+    
     public boolean mustAbort() {
         return this == MUST_ABORT;
     }
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/IsisModuleCoreInteraction.java b/core/interaction/src/main/java/org/apache/isis/core/interaction/IsisModuleCoreInteraction.java
index b99c8c7..ea85fe2 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/IsisModuleCoreInteraction.java
+++ b/core/interaction/src/main/java/org/apache/isis/core/interaction/IsisModuleCoreInteraction.java
@@ -3,12 +3,12 @@ package org.apache.isis.core.interaction;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
-import org.apache.isis.core.interaction.scope.IsisInteractionScopeBeanFactoryPostProcessor;
+import org.apache.isis.core.interaction.scope.InteractionScopeBeanFactoryPostProcessor;
 
 @Configuration
 @Import({
     
-    IsisInteractionScopeBeanFactoryPostProcessor.class
+    InteractionScopeBeanFactoryPostProcessor.class
     
 })
 public class IsisModuleCoreInteraction {
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/events/IsisInteractionLifecycleEvent.java b/core/interaction/src/main/java/org/apache/isis/core/interaction/events/InteractionLifecycleEvent.java
similarity index 96%
rename from core/interaction/src/main/java/org/apache/isis/core/interaction/events/IsisInteractionLifecycleEvent.java
rename to core/interaction/src/main/java/org/apache/isis/core/interaction/events/InteractionLifecycleEvent.java
index fb6a868..b98a7d5 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/events/IsisInteractionLifecycleEvent.java
+++ b/core/interaction/src/main/java/org/apache/isis/core/interaction/events/InteractionLifecycleEvent.java
@@ -25,7 +25,7 @@ import lombok.ToString;
 import lombok.Value;
 
 @Value(staticConstructor="of") @ToString(of = "eventType")
-public class IsisInteractionLifecycleEvent {
+public class InteractionLifecycleEvent {
 
     public enum EventType {
         HAS_STARTED,
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/integration/IsisRequestCycle.java b/core/interaction/src/main/java/org/apache/isis/core/interaction/integration/IsisRequestCycle.java
index ee8caa4..1a2b39b 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/integration/IsisRequestCycle.java
+++ b/core/interaction/src/main/java/org/apache/isis/core/interaction/integration/IsisRequestCycle.java
@@ -18,16 +18,10 @@
  */
 package org.apache.isis.core.interaction.integration;
 
-import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.TransactionStatus;
-import org.springframework.transaction.support.TransactionTemplate;
-
-import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.interaction.session.InteractionFactory;
 import org.apache.isis.core.security.authentication.Authentication;
 
 import lombok.RequiredArgsConstructor;
-import lombok.val;
 
 /**
  * 
@@ -36,55 +30,23 @@ import lombok.val;
 @RequiredArgsConstructor(staticName = "next")
 public class IsisRequestCycle {
 
-    // -- SUPPORTING ISIS TRANSACTION FILTER FOR RESTFUL OBJECTS ...
-
     private final InteractionFactory isisInteractionFactory;
-    private final TransactionTemplate transactionTemplate;
-    private TransactionStatus txStatus;
 
     // -- SUPPORTING WEB REQUEST CYCLE FOR ISIS ...
 
     public void onBeginRequest(Authentication authentication) {
 
         isisInteractionFactory.openInteraction(authentication);
-
-        txStatus = getTransactionManager().getTransaction(null);
-
     }
 
     public void onRequestHandlerExecuted() {
 
-        if(txStatus==null) {
-            return;    
-        }
-
-        txStatus.flush();
     }
 
     public void onEndRequest() {
 
-        if(txStatus==null) {
-            return;    
-        }
+        isisInteractionFactory.closeSessionStack();
 
-        try {
-
-            getTransactionManager().commit(txStatus);
-
-        } finally {
-            isisInteractionFactory.closeSessionStack();
-        }
-
-    }
-    
-    // -- HELPER
-    
-    private PlatformTransactionManager getTransactionManager() {
-        val txMan = transactionTemplate.getTransactionManager();
-        if(txMan == null) {
-            throw _Exceptions.illegalState("IsisRequestCycle needs a PlatformTransactionManager (Spring)");
-        }
-        return txMan;
     }
 
 
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScope.java b/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScope.java
similarity index 89%
rename from core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScope.java
rename to core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScope.java
index d98c066..6dfed09 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScope.java
+++ b/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScope.java
@@ -38,7 +38,7 @@ import lombok.extern.log4j.Log4j2;
  * @since 2.0
  */
 @Log4j2
-class IsisInteractionScope implements Scope, IsisInteractionScopeCloseListener {
+class InteractionScope implements Scope, InteractionScopeLifecycleHandler {
     
     @Inject private InteractionTracker isisInteractionTracker;
 
@@ -61,13 +61,13 @@ class IsisInteractionScope implements Scope, IsisInteractionScopeCloseListener {
     public Object get(String name, ObjectFactory<?> objectFactory) {
         
         if(isisInteractionTracker==null) {
-            throw _Exceptions.illegalState("Creation of bean %s with @IsisInteractionScope requires the "
-                    + "IsisInteractionScopeBeanFactoryPostProcessor registered and initialized.", name);
+            throw _Exceptions.illegalState("Creation of bean %s with @InteractionScope requires the "
+                    + "InteractionScopeBeanFactoryPostProcessor registered and initialized.", name);
         }
         
         if(!isisInteractionTracker.isInInteractionSession()) {
-            throw _Exceptions.illegalState("Creation of bean %s with @IsisInteractionScope requires the "
-                    + "calling %s to have an open IsisInteraction on the thread-local stack. Running into "
+            throw _Exceptions.illegalState("Creation of bean %s with @InteractionScope requires the "
+                    + "calling %s to have an open Interaction on the thread-local stack. Running into "
                     + "this issue might be caused by use of ... @Inject MyScopedBean bean ..., instead of "
                     + "... @Inject Provider<MyScopedBean> provider ...", name, _Probe.currentThreadId());
         }
@@ -113,11 +113,12 @@ class IsisInteractionScope implements Scope, IsisInteractionScopeCloseListener {
     }
     
     @Override
-    public void preTopLevelIsisInteractionClose() {
-        removeAll();
+    public void onTopLevelInteractionOpened() {
+        // nothing to do
     }
-    
-    private void removeAll() {
+
+    @Override
+    public void onTopLevelInteractionClosing() {
         try {
             scopedObjects.get().values().forEach(ScopedObject::preDestroy);
         } finally {
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeBeanFactoryPostProcessor.java b/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScopeBeanFactoryPostProcessor.java
similarity index 67%
rename from core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeBeanFactoryPostProcessor.java
rename to core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScopeBeanFactoryPostProcessor.java
index 6f0695e..221b234 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeBeanFactoryPostProcessor.java
+++ b/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScopeBeanFactoryPostProcessor.java
@@ -33,21 +33,21 @@ import lombok.val;
  * @since 2.0
  */
 @Component
-public class IsisInteractionScopeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
+public class InteractionScopeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
 
     @Override
     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
-        val isisInteractionScope = new IsisInteractionScope();
-        // scope name as defined in annotation @IsisInteractionScope
-        beanFactory.registerScope("isis-interaction", isisInteractionScope);
-        _Context.put(IsisInteractionScope.class, isisInteractionScope, true);
+        val interactionScope = new InteractionScope();
+        // scope name as defined in annotation @InteractionScope
+        beanFactory.registerScope("interaction", interactionScope);
+        _Context.put(InteractionScope.class, interactionScope, true);
     }
 
-    public static IsisInteractionScopeCloseListener initIsisInteractionScopeSupport(
+    public static InteractionScopeLifecycleHandler initIsisInteractionScopeSupport(
             @NonNull final ServiceInjector serviceInjector) {
-        val isisInteractionScope = _Context.getElseFail(IsisInteractionScope.class);
-        serviceInjector.injectServicesInto(isisInteractionScope);
-        _Context.remove(IsisInteractionScope.class); // cleanup
-        return isisInteractionScope;
+        val interactionScope = _Context.getElseFail(InteractionScope.class);
+        serviceInjector.injectServicesInto(interactionScope);
+        _Context.remove(InteractionScope.class); // cleanup
+        return interactionScope;
     }
 }
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java b/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScopeLifecycleHandler.java
similarity index 87%
copy from core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java
copy to core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScopeLifecycleHandler.java
index 1518af9..82ad852 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java
+++ b/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/InteractionScopeLifecycleHandler.java
@@ -21,8 +21,9 @@ package org.apache.isis.core.interaction.scope;
 /**
  * @since 2.0
  */
-public interface IsisInteractionScopeCloseListener {
+public interface InteractionScopeLifecycleHandler {
 
-    void preTopLevelIsisInteractionClose();
+    void onTopLevelInteractionOpened();
+    void onTopLevelInteractionClosing();
     
 }
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java b/core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java
index 6935825..66db00c 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java
+++ b/core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java
@@ -64,11 +64,6 @@ public interface InteractionFactory {
      * @return whether the calling thread is within the context of an open {@link InteractionSession} 
      */
     boolean isInInteractionSession();
-
-    /**
-     * @return whether the calling thread is within the context of an open IsisTransactionSession
-     */
-    boolean isInTransaction();
     
     /**
      * Executes a block of code with a new or reused {@link InteractionSession} using a new or 
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/context/IsisAppCommonContext.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/context/IsisAppCommonContext.java
index fcf4174..cf37fd3 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/context/IsisAppCommonContext.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/context/IsisAppCommonContext.java
@@ -22,9 +22,6 @@ import java.util.Optional;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
-import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.support.TransactionTemplate;
-
 import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.applib.services.menu.MenuBarsService;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
@@ -103,11 +100,6 @@ public class IsisAppCommonContext implements HasMetaModelContext {
         return getMetaModelContext().getServiceInjector().injectServicesInto(pojo);
     }
     
-    public TransactionTemplate createTransactionTemplate() {
-        val txMan = lookupServiceElseFail(PlatformTransactionManager.class);
-        return new TransactionTemplate(txMan);
-    }
-    
     public ObjectMemento mementoFor(ManagedObject adapter) {
         return getMementoService().mementoForObject(adapter);
     }
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/events/AppLifecycleEventService.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/events/AppLifecycleEventService.java
index a110056..688e7b7 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/events/AppLifecycleEventService.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/events/AppLifecycleEventService.java
@@ -28,7 +28,8 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.eventbus.EventBusService;
-import org.apache.isis.core.interaction.events.IsisInteractionLifecycleEvent;
+import org.apache.isis.commons.internal.debug._Probe;
+import org.apache.isis.core.interaction.events.InteractionLifecycleEvent;
 import org.apache.isis.core.interaction.session.InteractionSession;
 import org.apache.isis.core.interaction.session.InteractionTracker;
 import org.apache.isis.core.transaction.changetracking.events.PostStoreEvent;
@@ -65,17 +66,23 @@ public class AppLifecycleEventService {
     // -- INTERACTION
 
     public void fireInteractionHasStarted(InteractionSession interactionSession) {
+        
+        _Probe.errOut("fireInteractionHasStarted");
+        
         val conversationId = interactionTracker.getConversationId().orElse(null);
         eventBusService.post(
-                IsisInteractionLifecycleEvent
-                .of(conversationId, interactionSession, IsisInteractionLifecycleEvent.EventType.HAS_STARTED));
+                InteractionLifecycleEvent
+                .of(conversationId, interactionSession, InteractionLifecycleEvent.EventType.HAS_STARTED));
     }
 
     public void fireInteractionIsEnding(InteractionSession interactionSession) {
+        
+        _Probe.errOut("fireInteractionIsEnding");
+        
         val conversationId = interactionTracker.getConversationId().orElse(null);
         eventBusService.post(
-                IsisInteractionLifecycleEvent
-                .of(conversationId, interactionSession, IsisInteractionLifecycleEvent.EventType.IS_ENDING));
+                InteractionLifecycleEvent
+                .of(conversationId, interactionSession, InteractionLifecycleEvent.EventType.IS_ENDING));
     }
 	
     // -- PERSISTENT OBJECT EVENTS
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ExecutionPublisherDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ExecutionPublisherDefault.java
index 2d6474c..75ce1d3 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ExecutionPublisherDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/publish/ExecutionPublisherDefault.java
@@ -32,7 +32,7 @@ import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.applib.annotation.IsisInteractionScope;
+import org.apache.isis.applib.annotation.InteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.publishing.spi.ExecutionSubscriber;
@@ -48,7 +48,7 @@ import lombok.val;
 @Order(OrderPrecedence.MIDPOINT)
 @Primary
 @Qualifier("Default")
-@IsisInteractionScope
+@InteractionScope
 @RequiredArgsConstructor(onConstructor_ = {@Inject})
 //@Log4j2
 public class ExecutionPublisherDefault 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/queryresultscache/QueryResultsCacheDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/queryresultscache/QueryResultsCacheDefault.java
index 323d2e9..75d6b95 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/queryresultscache/QueryResultsCacheDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/queryresultscache/QueryResultsCacheDefault.java
@@ -29,20 +29,19 @@ import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.applib.annotation.IsisInteractionScope;
+import org.apache.isis.applib.annotation.InteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.TransactionScopeListener;
-import org.apache.isis.applib.services.queryresultscache.QueryResultCacheControl;
-import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
-import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.commons.internal.base._NullSafe;
-import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.applib.services.MethodReferences.Call0;
 import org.apache.isis.applib.services.MethodReferences.Call1;
 import org.apache.isis.applib.services.MethodReferences.Call2;
 import org.apache.isis.applib.services.MethodReferences.Call3;
 import org.apache.isis.applib.services.MethodReferences.Call4;
 import org.apache.isis.applib.services.MethodReferences.Call5;
+import org.apache.isis.applib.services.queryresultscache.QueryResultCacheControl;
+import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
+import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.commons.internal.collections._Maps;
 
 import lombok.extern.log4j.Log4j2;
 
@@ -59,10 +58,10 @@ import lombok.extern.log4j.Log4j2;
 @Named("isisRuntime.QueryResultsCacheDefault")
 @Order(OrderPrecedence.EARLY)
 @Primary
-@IsisInteractionScope
+@InteractionScope
 @Qualifier("Default")
 @Log4j2
-public class QueryResultsCacheDefault implements QueryResultsCache, TransactionScopeListener {
+public class QueryResultsCacheDefault implements QueryResultsCache {
 
     private final Map<Key, Value<?>> cache = _Maps.newHashMap();
     
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/scratchpad/ScratchpadDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/scratchpad/ScratchpadDefault.java
index 1f8afdd..3a6821c 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/scratchpad/ScratchpadDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/scratchpad/ScratchpadDefault.java
@@ -27,13 +27,11 @@ import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.applib.annotation.IsisInteractionScope;
+import org.apache.isis.applib.annotation.InteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.scratchpad.Scratchpad;
 import org.apache.isis.commons.internal.collections._Maps;
 
-import lombok.extern.log4j.Log4j2;
-
 /**
  * This service (API and implementation) provides a mechanism to interchange information between multiple objects invoked in the same
  * interaction.  Most commonly this will be as the result of invoking a {@link org.apache.isis.applib.annotation.Bulk}
@@ -49,8 +47,8 @@ import lombok.extern.log4j.Log4j2;
 @Order(OrderPrecedence.EARLY)
 @Primary
 @Qualifier("Default")
-@IsisInteractionScope
-@Log4j2
+@InteractionScope
+//@Log4j2
 public class ScratchpadDefault implements Scratchpad {
 
     /**
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionFactoryDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionFactoryDefault.java
index 44503d0..7e217c9 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionFactoryDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/InteractionFactoryDefault.java
@@ -19,6 +19,8 @@
 
 package org.apache.isis.core.runtimeservices.session;
 
+import static org.apache.isis.commons.internal.base._With.requires;
+
 import java.io.File;
 import java.sql.Timestamp;
 import java.util.Objects;
@@ -37,7 +39,6 @@ import org.springframework.context.event.ContextRefreshedEvent;
 import org.springframework.context.event.EventListener;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.support.TransactionSynchronizationManager;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.clock.ClockService;
@@ -51,8 +52,8 @@ import org.apache.isis.commons.internal.concurrent._ConcurrentTaskList;
 import org.apache.isis.commons.internal.debug._Probe;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.IsisConfiguration;
-import org.apache.isis.core.interaction.scope.IsisInteractionScopeBeanFactoryPostProcessor;
-import org.apache.isis.core.interaction.scope.IsisInteractionScopeCloseListener;
+import org.apache.isis.core.interaction.scope.InteractionScopeBeanFactoryPostProcessor;
+import org.apache.isis.core.interaction.scope.InteractionScopeLifecycleHandler;
 import org.apache.isis.core.interaction.session.AuthenticationLayer;
 import org.apache.isis.core.interaction.session.InteractionFactory;
 import org.apache.isis.core.interaction.session.InteractionSession;
@@ -65,8 +66,6 @@ import org.apache.isis.core.runtime.events.AppLifecycleEventService;
 import org.apache.isis.core.security.authentication.Authentication;
 import org.apache.isis.core.security.authentication.manager.AuthenticationManager;
 
-import static org.apache.isis.commons.internal.base._With.requires;
-
 import lombok.NonNull;
 import lombok.SneakyThrows;
 import lombok.val;
@@ -101,11 +100,12 @@ implements InteractionFactory, InteractionTracker {
     @Inject ClockService clockService;
     @Inject CommandPublisher commandPublisher;
 
-    private IsisInteractionScopeCloseListener isisInteractionScopeCloseListener;
+    private InteractionScopeLifecycleHandler interactionScopeLifecycleHandler;
 
     @PostConstruct
     public void initIsisInteractionScopeSupport() {
-        this.isisInteractionScopeCloseListener = IsisInteractionScopeBeanFactoryPostProcessor.initIsisInteractionScopeSupport(serviceInjector);        
+        this.interactionScopeLifecycleHandler = InteractionScopeBeanFactoryPostProcessor
+                .initIsisInteractionScopeSupport(serviceInjector);        
     }
     
     //@PostConstruct .. too early, needs services to be provisioned first
@@ -221,23 +221,6 @@ implements InteractionFactory, InteractionTracker {
         return !authenticationStack.get().isEmpty();
     }
 
-    @Override
-    public boolean isInTransaction() {
-        
-        return TransactionSynchronizationManager.isActualTransactionActive();
-
-//        return currentInteractionSession().map(isisInteraction->{
-//            if (isisInteraction.getCurrentTransactionId() != null) {
-//                if (!isisInteraction.getCurrentTransactionState().isComplete()) {
-//                    return true;
-//                }
-//            }
-//            return false;
-//        })
-//        .orElse(false);
-
-    }
-
     // -- AUTHENTICATED EXECUTION
     
     @Override
@@ -319,13 +302,14 @@ implements InteractionFactory, InteractionTracker {
     
     private void postSessionOpened(InteractionSession session) {
         conversationId.set(UUID.randomUUID());
+        interactionScopeLifecycleHandler.onTopLevelInteractionOpened();
         runtimeEventService.fireInteractionHasStarted(session); // only fire on top-level session
     }
     
     private void preSessionClosed(InteractionSession session) {
         completeAndPublishCurrentCommand();
         runtimeEventService.fireInteractionIsEnding(session); // only fire on top-level session 
-        isisInteractionScopeCloseListener.preTopLevelIsisInteractionClose(); // cleanup the isis-session scope
+        interactionScopeLifecycleHandler.onTopLevelInteractionClosing(); // cleanup the isis-session scope
         session.close(); // do this last
     }
     
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xactn/TransactionServiceSpring.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xactn/TransactionServiceSpring.java
index 30ef4a4..7959ada 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xactn/TransactionServiceSpring.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xactn/TransactionServiceSpring.java
@@ -87,6 +87,7 @@ public class TransactionServiceSpring implements TransactionService {
     public void nextTransaction() {
         
         val txManager = singletonTransactionManagerElseFail(); 
+        
         val txTemplate = new TransactionTemplate(txManager);
         txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 
@@ -126,19 +127,15 @@ public class TransactionServiceSpring implements TransactionService {
         return currentTransactionStatus()
         .map(txStatus->{
         
-            if(txStatus.isNewTransaction()) {
-                return txStatus.isRollbackOnly()
-                        ? TransactionState.MUST_ABORT
-                        : TransactionState.IN_PROGRESS;
-            }
-
             if(txStatus.isCompleted()) {
                 return txStatus.isRollbackOnly()
                         ? TransactionState.ABORTED
                         : TransactionState.COMMITTED;
             }
             
-            throw _Exceptions.unexpectedCodeReach();
+            return txStatus.isRollbackOnly()
+                    ? TransactionState.MUST_ABORT
+                    : TransactionState.IN_PROGRESS;
             
         })
         .orElse(TransactionState.NONE);
diff --git a/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTrackerDefault.java b/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTrackerDefault.java
index 39b2889..76f40f5 100644
--- a/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTrackerDefault.java
+++ b/core/transaction/src/main/java/org/apache/isis/core/transaction/changetracking/EntityChangeTrackerDefault.java
@@ -32,12 +32,12 @@ import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.event.TransactionalEventListener;
 
 import org.apache.isis.applib.annotation.EntityChangeKind;
-import org.apache.isis.applib.annotation.IsisInteractionScope;
+import org.apache.isis.applib.annotation.InteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.events.lifecycle.AbstractLifecycleEvent;
-import org.apache.isis.applib.services.TransactionScopeListener;
 import org.apache.isis.applib.services.eventbus.EventBusService;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.iactn.InteractionContext;
@@ -71,6 +71,7 @@ import org.apache.isis.core.metamodel.spec.feature.MixedIn;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.core.transaction.changetracking.events.IsisTransactionPlaceholder;
+import org.apache.isis.core.transaction.events.TransactionEndedEvent;
 
 import lombok.AccessLevel;
 import lombok.Getter;
@@ -86,11 +87,10 @@ import lombok.extern.log4j.Log4j2;
 @Order(OrderPrecedence.EARLY)
 @Primary
 @Qualifier("Default")
-@IsisInteractionScope
+@InteractionScope
 @Log4j2
 public class EntityChangeTrackerDefault
 implements
-    TransactionScopeListener,
     MetricsService,
     EntityChangeTracker,
     HasEnlistedEntityPropertyChanges,
@@ -175,26 +175,28 @@ implements
     /**
      * @apiNote intended to be called during pre-commit of a transaction by the framework internally
      */
-    @Override
-    public void onPreCommit(PreCommitPhase preCommitPhase) {
-        switch (preCommitPhase) {
-        case WHILE_PUBLISHING:
-            log.debug("about to publish entity changes");
-            prepareCommandPublishing();
-            entityPropertyChangePublisher.publishChangedProperties(this);
-            entityChangesPublisher.publishChangingEntities(this);
-            break;
-        case POST_PUBLISHING:
-            log.debug("purging entity change records");
-            enlistedEntityPropertiesForAuditing.clear();
-            changeKindByEnlistedAdapter.clear();
-            changedObjectPropertiesRef.clear();
-            entityChangeEventCount.reset();
-            numberEntitiesLoaded.reset();
-            break;
-        default:
-            break;
-        }
+
+    /** TRANSACTION END BOUNDARY */
+    @TransactionalEventListener(TransactionEndedEvent.class)
+    public void onPreCommit(TransactionEndedEvent event) {
+        whilePublishing();
+        postPublishing();
+    }
+
+    private void whilePublishing() {
+        log.debug("about to publish entity changes");
+        prepareCommandPublishing();
+        entityPropertyChangePublisher.publishChangedProperties(this);
+        entityChangesPublisher.publishChangingEntities(this);
+    }
+
+    private void postPublishing() {
+        log.debug("purging entity change records");
+        enlistedEntityPropertiesForAuditing.clear();
+        changeKindByEnlistedAdapter.clear();
+        changedObjectPropertiesRef.clear();
+        entityChangeEventCount.reset();
+        numberEntitiesLoaded.reset();
     }
 
     private void prepareCommandPublishing() {
@@ -416,8 +418,8 @@ implements
             final TransactionId txId) {
 
         return getPropertyChangeRecords().stream()
-        .map(propertyChangeRecord->EntityPropertyChangeFactory
-                .createEntityPropertyChange(timestamp, userName, txId, propertyChangeRecord));
+                .map(propertyChangeRecord->EntityPropertyChangeFactory
+                        .createEntityPropertyChange(timestamp, userName, txId, propertyChangeRecord));
     }
 
 }
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java b/core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionBeginEvent.java
similarity index 71%
copy from core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java
copy to core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionBeginEvent.java
index 1518af9..be8a28c 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java
+++ b/core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionBeginEvent.java
@@ -16,13 +16,16 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.core.interaction.scope;
+package org.apache.isis.core.transaction.events;
 
-/**
- * @since 2.0
- */
-public interface IsisInteractionScopeCloseListener {
+import org.springframework.transaction.TransactionStatus;
+
+public class TransactionBeginEvent extends TransactionEventAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public TransactionBeginEvent(final TransactionStatus source) {
+        super(source);
+    }
 
-    void preTopLevelIsisInteractionClose();
-    
 }
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java b/core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEndedEvent.java
similarity index 71%
copy from core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java
copy to core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEndedEvent.java
index 1518af9..190a0b2 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java
+++ b/core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEndedEvent.java
@@ -16,13 +16,15 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.core.interaction.scope;
+package org.apache.isis.core.transaction.events;
 
-/**
- * @since 2.0
- */
-public interface IsisInteractionScopeCloseListener {
+import org.springframework.transaction.TransactionStatus;
+
+public class TransactionEndedEvent extends TransactionEventAbstract {
 
-    void preTopLevelIsisInteractionClose();
+    private static final long serialVersionUID = 1L;
     
-}
+    public TransactionEndedEvent(final TransactionStatus source) {
+        super(source);
+    }
+}
\ No newline at end of file
diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java b/core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEndingEvent.java
similarity index 71%
rename from core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java
rename to core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEndingEvent.java
index 1518af9..b17ebc9 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/scope/IsisInteractionScopeCloseListener.java
+++ b/core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEndingEvent.java
@@ -16,13 +16,15 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.core.interaction.scope;
+package org.apache.isis.core.transaction.events;
 
-/**
- * @since 2.0
- */
-public interface IsisInteractionScopeCloseListener {
+import org.springframework.transaction.TransactionStatus;
+
+public class TransactionEndingEvent extends TransactionEventAbstract {
 
-    void preTopLevelIsisInteractionClose();
+    private static final long serialVersionUID = 1L;
     
-}
+    public TransactionEndingEvent(final TransactionStatus source) {
+        super(source);
+    }
+}
\ No newline at end of file
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/TransactionScopeListener.java b/core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEventAbstract.java
similarity index 50%
rename from api/applib/src/main/java/org/apache/isis/applib/services/TransactionScopeListener.java
rename to core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEventAbstract.java
index 3fe562a..e77dfed 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/TransactionScopeListener.java
+++ b/core/transaction/src/main/java/org/apache/isis/core/transaction/events/TransactionEventAbstract.java
@@ -16,35 +16,26 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
+package org.apache.isis.core.transaction.events;
 
-package org.apache.isis.applib.services;
+import java.util.EventObject;
 
-import org.apache.isis.applib.annotation.IsisInteractionScope;
+import org.springframework.transaction.TransactionStatus;
 
-/**
- * Domain services that need to be aware of transaction boundaries can
- * implement this interface.
- * 
- * @apiNote Implementing services most likely need to be scoped in a way that
- * binds the scope to the current thread (eg. {@link IsisInteractionScope})
- *  
- * @since 2.0 (renamed from WithTransactionScope) {@index}
- */
-public interface TransactionScopeListener {
-    
-    enum PreCommitPhase {
-        PRE_PUBLISHING,
-        WHILE_PUBLISHING,
-        POST_PUBLISHING
-    }
+import lombok.Getter;
+
+public abstract class TransactionEventAbstract extends EventObject {
+
+    private static final long serialVersionUID = 1L;
     
-    default void onTransactionStarted() {
-        // default: do nothing
-    }
+    /**
+     * Same as {@link #getSource()}.
+     */
+    @Getter private final TransactionStatus transactionStatus;
 
-    /** triggered during the pre-commit phase in a transaction*/
-    default void onPreCommit(PreCommitPhase preCommitPhase) {
-        // default: do nothing
+    public TransactionEventAbstract(final TransactionStatus source) {
+        super(source);
+        this.transactionStatus = source;
     }
-    
+
 }
diff --git a/core/transaction/src/main/java/org/apache/isis/core/transaction/manager/ApplicationLayerAwareTransactionManager.java b/core/transaction/src/main/java/org/apache/isis/core/transaction/manager/ApplicationLayerAwareTransactionManager.java
new file mode 100644
index 0000000..b1d4b15
--- /dev/null
+++ b/core/transaction/src/main/java/org/apache/isis/core/transaction/manager/ApplicationLayerAwareTransactionManager.java
@@ -0,0 +1,64 @@
+/*
+ *  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.transaction.manager;
+
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionException;
+import org.springframework.transaction.TransactionStatus;
+
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.core.transaction.events.TransactionBeginEvent;
+import org.apache.isis.core.transaction.events.TransactionEndedEvent;
+import org.apache.isis.core.transaction.events.TransactionEndingEvent;
+
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
+@RequiredArgsConstructor
+public class ApplicationLayerAwareTransactionManager 
+implements PlatformTransactionManager {
+
+    private final PlatformTransactionManager txManager;
+    private final EventBusService eventBusService;
+    
+    @Override
+    public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
+       val txStatus = txManager.getTransaction(definition);
+       if(txStatus.isNewTransaction()) {
+           eventBusService.post(new TransactionBeginEvent(txStatus));
+       }
+       return txStatus; 
+    }
+
+    @Override
+    public void commit(TransactionStatus txStatus) throws TransactionException {
+        eventBusService.post(new TransactionEndingEvent(txStatus));
+        txManager.commit(txStatus);
+        eventBusService.post(new TransactionEndedEvent(txStatus));
+    }
+
+    @Override
+    public void rollback(TransactionStatus txStatus) throws TransactionException {
+        eventBusService.post(new TransactionEndingEvent(txStatus));
+        txManager.rollback(txStatus);
+        eventBusService.post(new TransactionEndedEvent(txStatus));
+    }
+
+}
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/main/UiBuilderFx.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/main/UiBuilderFx.java
index a883037..dfa8626 100644
--- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/main/UiBuilderFx.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/main/UiBuilderFx.java
@@ -27,19 +27,18 @@ import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Component;
 
 import org.apache.isis.commons.internal.debug._Probe;
-import org.apache.isis.core.interaction.events.IsisInteractionLifecycleEvent;
+import org.apache.isis.core.interaction.events.InteractionLifecycleEvent;
 import org.apache.isis.incubator.viewer.javafx.model.events.JavaFxViewerConfig;
 import org.apache.isis.incubator.viewer.javafx.model.events.PrimaryStageReadyEvent;
 
-import lombok.RequiredArgsConstructor;
-import lombok.SneakyThrows;
-import lombok.val;
-import lombok.extern.log4j.Log4j2;
-
 import javafx.fxml.FXMLLoader;
 import javafx.scene.Parent;
 import javafx.scene.Scene;
 import javafx.stage.Stage;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
 @Component
 @RequiredArgsConstructor(onConstructor_ = {@Inject})
@@ -66,8 +65,8 @@ public class UiBuilderFx {
         stage.show();
     }
 
-    @EventListener(IsisInteractionLifecycleEvent.class)
-    public void onIsisInteractionLifecycleEvent(IsisInteractionLifecycleEvent event) {
+    @EventListener(InteractionLifecycleEvent.class)
+    public void onInteractionLifecycleEvent(InteractionLifecycleEvent event) {
         switch(event.getEventType()) {
         case HAS_STARTED:
             //TODO this would be the place to indicate to the user, that a long running task has started  
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DnEntityDiscoveryListener.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DnEntityDiscoveryListener.java
index 1a7aad1..0cea8a9 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DnEntityDiscoveryListener.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DnEntityDiscoveryListener.java
@@ -33,6 +33,7 @@ import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.context._Context;
 import org.apache.isis.persistence.jdo.provider.config.JdoEntityDiscoveryListener;
 
+import lombok.NonNull;
 import lombok.val;
 
 @Component
@@ -40,9 +41,9 @@ public class DnEntityDiscoveryListener implements JdoEntityDiscoveryListener {
 
     @Override
     public void onEntitiesDiscovered(
-            final PersistenceManagerFactory persistenceManagerFactory, 
-            final Set<Class<?>> entityTypes,
-            final Map<String, String> dnSettings) {
+            final @NonNull PersistenceManagerFactory persistenceManagerFactory, 
+            final @NonNull Set<Class<?>> entityTypes,
+            final @NonNull Map<String, String> dnSettings) {
 
         if(_NullSafe.isEmpty(entityTypes)) {
             return; // skip
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/IsisModuleJdoIntegration.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/IsisModuleJdoIntegration.java
index 9ab6b86..617a8ff 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/IsisModuleJdoIntegration.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/IsisModuleJdoIntegration.java
@@ -25,8 +25,10 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.Primary;
 
+import org.apache.isis.applib.services.eventbus.EventBusService;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.runtime.IsisModuleCoreRuntime;
+import org.apache.isis.core.transaction.manager.ApplicationLayerAwareTransactionManager;
 import org.apache.isis.persistence.jdo.applib.IsisModulePersistenceJdoApplib;
 import org.apache.isis.persistence.jdo.datanucleus.IsisModuleJdoProviderDatanucleus;
 import org.apache.isis.persistence.jdo.datanucleus.config.DnSettings;
@@ -94,12 +96,15 @@ public class IsisModuleJdoIntegration {
         return lpmfBean; 
     }
 
-    @Bean @Primary
-    public JdoTransactionManager getJdoTransactionManager(LocalPersistenceManagerFactoryBean localPmfBean) {
+    @Bean @Primary @Named("jdo-platform-transaction-manager")
+    public ApplicationLayerAwareTransactionManager getApplicationLayerAwareTransactionManager(
+            LocalPersistenceManagerFactoryBean localPmfBean,
+            EventBusService eventBusService) {
         
         val pmf = localPmfBean.getObject(); // created once per application lifecycle
         
-        return new JdoTransactionManager(pmf);
+        return new ApplicationLayerAwareTransactionManager(
+                new JdoTransactionManager(pmf), eventBusService);    
     }
     
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/JdoPersistenceLifecycleService.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/JdoPersistenceLifecycleService.java
index b997178..3dd6880 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/JdoPersistenceLifecycleService.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/JdoPersistenceLifecycleService.java
@@ -29,11 +29,12 @@ import org.springframework.context.annotation.Primary;
 import org.springframework.context.event.EventListener;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.PlatformTransactionManager;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.beans.IsisBeanTypeRegistry;
-import org.apache.isis.core.interaction.events.IsisInteractionLifecycleEvent;
+import org.apache.isis.core.interaction.events.InteractionLifecycleEvent;
 import org.apache.isis.core.interaction.session.InteractionSession;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.runtime.events.AppLifecycleEvent;
@@ -58,6 +59,9 @@ public class JdoPersistenceLifecycleService {
     //@Inject LocalPersistenceManagerFactoryBean localPmfBean;
     @Inject TransactionAwarePersistenceManagerFactoryProxy txAwarePmfProxy;
     
+    @Named("jdo-platform-transaction-manager")
+    @Inject PlatformTransactionManager txManager;
+    
     @Inject IsisBeanTypeRegistry isisBeanTypeRegistry;
     @Inject DnSettings dnSettings;
 
@@ -87,8 +91,8 @@ public class JdoPersistenceLifecycleService {
 
     }
 
-    @EventListener(IsisInteractionLifecycleEvent.class)
-    public void onInteractionLifecycleEvent(IsisInteractionLifecycleEvent event) {
+    @EventListener(InteractionLifecycleEvent.class)
+    public void onInteractionLifecycleEvent(InteractionLifecycleEvent event) {
 
         val eventType = event.getEventType();
         val interactionSession = event.getInteractionSession();
@@ -114,7 +118,7 @@ public class JdoPersistenceLifecycleService {
     // -- HELPER
 
     private void onInteractionStarted(final InteractionSession interactionSession) {
-        val persistenceSession = new JdoPersistenceSession5(metaModelContext, txAwarePmfProxy.getObject());
+        val persistenceSession = new JdoPersistenceSession5(metaModelContext, txManager, txAwarePmfProxy);
         interactionSession.putAttribute(JdoPersistenceSession.class, persistenceSession);
         persistenceSession.open();
     }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/metamodel/facets/entity/JdoEntityFacet.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/metamodel/facets/entity/JdoEntityFacet.java
index ab07a31..de736ca 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/metamodel/facets/entity/JdoEntityFacet.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/metamodel/facets/entity/JdoEntityFacet.java
@@ -67,7 +67,6 @@ implements EntityFacet {
     
     @Inject private TransactionAwarePersistenceManagerFactoryProxy pmf;
     @Inject private TransactionService txService;
-    @Inject private EntityChangeTracker entityChangeTracker;
     @Inject private ObjectManager objectManager;
 
     public JdoEntityFacet(
@@ -351,14 +350,17 @@ implements EntityFacet {
     // -- HELPER
     
     private Can<ManagedObject> fetchWithinTransaction(Supplier<List<?>> fetcher) {
+        
+        val entityChangeTracker = getFacetHolder().getServiceRegistry().lookupServiceElseFail(EntityChangeTracker.class); 
+        
         return getTransactionalProcessor().callWithinCurrentTransactionElseCreateNew(
                 ()->_NullSafe.stream(fetcher.get())
-                    .map(fetchedObject->adopt(fetchedObject))
+                    .map(fetchedObject->adopt(entityChangeTracker, fetchedObject))
                     .collect(Can.toCan()))
                 .orElseFail();
     }
     
-    private ManagedObject adopt(final Object fetchedObject) {
+    private ManagedObject adopt(final EntityChangeTracker entityChangeTracker, 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/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession5.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession5.java
index 544417f..7c5bdf3 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession5.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession5.java
@@ -18,15 +18,23 @@
  */
 package org.apache.isis.persistence.jdo.integration.persistence;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.enterprise.inject.Vetoed;
 import javax.jdo.PersistenceManager;
-import javax.jdo.PersistenceManagerFactory;
+
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionTemplate;
 
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.transaction.changetracking.EntityChangeTracker;
 import org.apache.isis.persistence.jdo.integration.lifecycles.IsisLifecycleListener;
 import org.apache.isis.persistence.jdo.integration.lifecycles.JdoStoreLifecycleListenerForIsis;
 import org.apache.isis.persistence.jdo.integration.lifecycles.LoadLifecycleListenerForIsis;
+import org.apache.isis.persistence.jdo.spring.integration.TransactionAwarePersistenceManagerFactoryProxy;
 
 import lombok.Getter;
 import lombok.val;
@@ -45,8 +53,10 @@ implements
     @Getter(onMethod_ = {@Override}) private PersistenceManager persistenceManager;
     @Getter(onMethod_ = {@Override}) private final MetaModelContext metaModelContext;
 
-    private final PersistenceManagerFactory pmf;
-    private Runnable unregisterLifecycleListeners;
+    private final PlatformTransactionManager txManager;
+    private final TransactionAwarePersistenceManagerFactoryProxy pmf;
+    private final List<Runnable> onCloseTasks = new ArrayList<>();
+    private TransactionStatus nonParticipatingTransactionalBoundary;
 
     // -- CONSTRUCTOR
     
@@ -57,13 +67,15 @@ implements
      */
     public JdoPersistenceSession5(
             final MetaModelContext metaModelContext, 
-            final PersistenceManagerFactory pmf) {
+            final PlatformTransactionManager txManager,
+            final TransactionAwarePersistenceManagerFactoryProxy pmf) {
 
         if (log.isDebugEnabled()) {
             log.debug("creating {}", this);
         }
 
         this.metaModelContext = metaModelContext;
+        this.txManager = txManager;
         this.pmf = pmf;
                 
         this.state = State.NOT_INITIALIZED;
@@ -102,38 +114,27 @@ implements
             log.debug("opening {}", this);
         }
 
-        this.persistenceManager = pmf.getPersistenceManager();
-        
-        val entityChangeTracker = metaModelContext.getServiceRegistry()
-                .lookupServiceElseFail(EntityChangeTracker.class);
-        
-        val entityChangeEmitter = 
-                new JdoEntityChangeEmitter(getMetaModelContext(), persistenceManager, entityChangeTracker);
-        
-        val isisLifecycleListener = new IsisLifecycleListener(entityChangeEmitter);
-        persistenceManager.addInstanceLifecycleListener(isisLifecycleListener, (Class[]) null);
+        val txTemplate = new TransactionTemplate(txManager);
+        txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 
-        // install JDO specific entity change listeners ...
-        
-        val loadLifecycleListener = new LoadLifecycleListenerForIsis();
-        val storeLifecycleListener = new JdoStoreLifecycleListenerForIsis();
+        // either reuse existing or create new
+        val txStatus = txManager.getTransaction(txTemplate);
+        if(txStatus.isNewTransaction()) {
+            // we have created a new transaction, 
+            nonParticipatingTransactionalBoundary = txStatus;
+        } else {
+            // we are participating in an exiting transaction
+        }
         
-        getServiceInjector().injectServicesInto(loadLifecycleListener);
-        getServiceInjector().injectServicesInto(storeLifecycleListener);
-            
-        persistenceManager.addInstanceLifecycleListener(loadLifecycleListener, (Class[]) null);
-        persistenceManager.addInstanceLifecycleListener(storeLifecycleListener, (Class[]) null);
+        this.persistenceManager = integrateWithApplicationLayer(pmf.getPersistenceManager());
         
-        this.unregisterLifecycleListeners = ()->{
-            persistenceManager.removeInstanceLifecycleListener(loadLifecycleListener);
-            persistenceManager.removeInstanceLifecycleListener(storeLifecycleListener);
-        };
-
         this.state = State.OPEN;
     }
 
     // -- CLOSE
 
+
+
     @Override
     public void close() {
 
@@ -142,31 +143,66 @@ implements
             return;
         }
 
-        unregisterLifecycleListeners.run();
-        unregisterLifecycleListeners = null;
-        
         try {
             
-//            if (!participate) {
-//                
-//                val pmf = pmf;
-//                
-//                PersistenceManagerHolder pmHolder = (PersistenceManagerHolder)
-//                        TransactionSynchronizationManager.unbindResource(pmf);
-//                log.debug("Closing JDO PersistenceManager in PersistenceSession");
-//                PersistenceManagerFactoryUtils.releasePersistenceManager(pmHolder.getPersistenceManager(), pmf);
-//            }
-            
-            persistenceManager = null; // detach
+            if (nonParticipatingTransactionalBoundary!=null) {
+                
+                if(nonParticipatingTransactionalBoundary.isRollbackOnly()) {
+                    txManager.rollback(nonParticipatingTransactionalBoundary);
+                } else {
+                    txManager.commit(nonParticipatingTransactionalBoundary);
+                }
+            }
+        
+            onCloseTasks.removeIf(task->{
+                if(!persistenceManager.isClosed()) {
+                    task.run();    
+                }
+                return true; 
+             });
             
         } catch(final Throwable ex) {
             // ignore
             log.error(
-                    "close: failed to close JDO persistenceManager; continuing to avoid memory leakage");
+                    "close: failed to close JDO persistenceManager; continuing to avoid memory leakage", ex);
         }
+        
+        persistenceManager = null; // detach
 
         this.state = State.CLOSED;
     }
+    
+    // -- HELPER
+    
+    private PersistenceManager integrateWithApplicationLayer(final PersistenceManager persistenceManager) {
+        
+        val entityChangeTracker = metaModelContext.getServiceRegistry()
+                .lookupServiceElseFail(EntityChangeTracker.class);
+        
+        val entityChangeEmitter = 
+                new JdoEntityChangeEmitter(getMetaModelContext(), persistenceManager, entityChangeTracker);
+        
+        val isisLifecycleListener = new IsisLifecycleListener(entityChangeEmitter);
+        persistenceManager.addInstanceLifecycleListener(isisLifecycleListener, (Class[]) null);
+
+        // install JDO specific entity change listeners ...
+        
+        val loadLifecycleListener = new LoadLifecycleListenerForIsis();
+        val storeLifecycleListener = new JdoStoreLifecycleListenerForIsis();
+        
+        getServiceInjector().injectServicesInto(loadLifecycleListener);
+        getServiceInjector().injectServicesInto(storeLifecycleListener);
+            
+        persistenceManager.addInstanceLifecycleListener(loadLifecycleListener, (Class[]) null);
+        persistenceManager.addInstanceLifecycleListener(storeLifecycleListener, (Class[]) null);
+        
+        onCloseTasks.add(()->{
+            persistenceManager.removeInstanceLifecycleListener(loadLifecycleListener);
+            persistenceManager.removeInstanceLifecycleListener(storeLifecycleListener);
+        });
+        
+        return persistenceManager;
+    }
 
 
 }
diff --git a/persistence/jdo/lightweight/src/main/java/org/apache/isis/persistence/jdo/lightweight/IsisModuleJdoLightweight.java b/persistence/jdo/lightweight/src/main/java/org/apache/isis/persistence/jdo/lightweight/IsisModuleJdoLightweight.java
index 0d51281..d07683f 100644
--- a/persistence/jdo/lightweight/src/main/java/org/apache/isis/persistence/jdo/lightweight/IsisModuleJdoLightweight.java
+++ b/persistence/jdo/lightweight/src/main/java/org/apache/isis/persistence/jdo/lightweight/IsisModuleJdoLightweight.java
@@ -27,9 +27,11 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.Primary;
 
+import org.apache.isis.applib.services.eventbus.EventBusService;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.core.config.beans.IsisBeanTypeRegistry;
 import org.apache.isis.core.runtime.IsisModuleCoreRuntime;
+import org.apache.isis.core.transaction.manager.ApplicationLayerAwareTransactionManager;
 import org.apache.isis.persistence.jdo.applib.IsisModulePersistenceJdoApplib;
 import org.apache.isis.persistence.jdo.datanucleus.IsisModuleJdoProviderDatanucleus;
 import org.apache.isis.persistence.jdo.datanucleus.config.DnSettings;
@@ -97,9 +99,12 @@ public class IsisModuleJdoLightweight {
         return lpmfBean; 
     }
 
-    @Bean @Primary
-    public JdoTransactionManager getJdoTransactionManager(LocalPersistenceManagerFactoryBean localPmf) {
-        return new JdoTransactionManager(localPmf.getObject());
+    @Bean @Primary @Named("jdo-platform-transaction-manager")
+    public ApplicationLayerAwareTransactionManager getApplicationLayerAwareTransactionManager(
+            LocalPersistenceManagerFactoryBean localPmf,
+            EventBusService eventBusService) {
+        return new ApplicationLayerAwareTransactionManager(
+                new JdoTransactionManager(localPmf.getObject()), eventBusService);    
     }
     
 }
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/applayer/ApplicationLayerTestFactory.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/applayer/ApplicationLayerTestFactory.java
index 37091c0..896064a 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/applayer/ApplicationLayerTestFactory.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/applayer/ApplicationLayerTestFactory.java
@@ -18,6 +18,11 @@
  */
 package org.apache.isis.testdomain.applayer;
 
+import static org.apache.isis.applib.services.wrapper.control.AsyncControl.returningVoid;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.DynamicTest.dynamicTest;
+
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -28,34 +33,27 @@ import javax.inject.Inject;
 
 import org.junit.jupiter.api.DynamicTest;
 import org.springframework.context.annotation.Import;
+import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.junit.jupiter.api.DynamicTest.dynamicTest;
-
 import org.apache.isis.applib.annotation.Where;
-import org.apache.isis.applib.services.TransactionScopeListener;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.services.wrapper.DisabledException;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
 import org.apache.isis.applib.services.wrapper.control.SyncControl;
 import org.apache.isis.applib.services.xactn.TransactionService;
-import org.apache.isis.applib.services.xactn.TransactionState;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.interaction.session.InteractionFactory;
 import org.apache.isis.core.metamodel.interactions.managed.PropertyInteraction;
 import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.transaction.events.TransactionEndingEvent;
 import org.apache.isis.testdomain.jdo.JdoTestDomainPersona;
 import org.apache.isis.testdomain.jdo.entities.JdoBook;
 import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScripts;
 
-import static org.apache.isis.applib.services.wrapper.control.AsyncControl.returningVoid;
-
 import lombok.RequiredArgsConstructor;
 import lombok.Setter;
 import lombok.val;
@@ -85,20 +83,15 @@ public class ApplicationLayerTestFactory {
     }
     
     @Service
-    public static class PreCommitListener implements TransactionScopeListener {
+    public static class PreCommitListener {
         
         @Setter private Consumer<VerificationStage> verifier;
         
-        @Override
-        public void onPreCommit(PreCommitPhase preCommitPhase) {
-            switch (preCommitPhase) {
-            case PRE_PUBLISHING:
-                if(verifier!=null) {
-                    verifier.accept(VerificationStage.PRE_COMMIT);
-                }
-                break;
-            default:
-                break;
+        /** TRANSACTION END BOUNDARY */
+        @EventListener(TransactionEndingEvent.class)
+        public void onPreCommit(TransactionEndingEvent event) {
+            if(verifier!=null) {
+                verifier.accept(VerificationStage.PRE_COMMIT);
             }
         }
     }
@@ -107,8 +100,8 @@ public class ApplicationLayerTestFactory {
             final Runnable given,
             final Consumer<VerificationStage> verifier) {
         return _Lists.of(
-                dynamicTest("No initial Transaction with Test Execution", 
-                        this::no_initial_tx_context),
+//                dynamicTest("No initial Transaction with Test Execution", 
+//                        this::no_initial_tx_context),
                 programmaticTest("Programmatic Execution", 
                         given, verifier, this::programmaticExecution),
                 interactionTest("Interaction Api Execution", 
@@ -172,11 +165,11 @@ public class ApplicationLayerTestFactory {
 
     // -- TESTS - ENSURE TESTS ARE CORRECTLY INVOKED 
 
-    boolean no_initial_tx_context() {
-        val txState = transactionService.currentTransactionState();
-        assertEquals(TransactionState.NONE, txState);
-        return true;
-    }
+//    boolean no_initial_tx_context() {
+//        val txState = transactionService.currentTransactionState();
+//        assertEquals(TransactionState.NONE, txState);
+//        return true;
+//    }
 
     // -- TESTS - WRAPPER SYNC
 
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
index d74981a..efdc873 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
@@ -38,7 +38,7 @@ import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.metrics.MetricsService;
 import org.apache.isis.commons.internal.debug._Probe;
 import org.apache.isis.core.config.presets.IsisPresets;
-import org.apache.isis.core.interaction.events.IsisInteractionLifecycleEvent;
+import org.apache.isis.core.interaction.events.InteractionLifecycleEvent;
 import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
 import org.apache.isis.extensions.modelannotation.metamodel.IsisModuleExtModelAnnotation;
 import org.apache.isis.security.bypass.IsisModuleSecurityBypass;
@@ -59,45 +59,6 @@ import lombok.RequiredArgsConstructor;
 })
 public class Configuration_headless {
 
-//    @Bean @Singleton
-//    public TransactionService transactionService() {
-//        return new TransactionService() {
-//
-//            @Override
-//            public TransactionId currentTransactionId() {
-//                return null;
-//            }
-//
-//            @Override
-//            public void flushTransaction() {
-//            }
-//
-//            @Override
-//            public TransactionState currentTransactionState() {
-//                return null;
-//            }
-//
-//            @Override
-//            public void executeWithinTransaction(Runnable task) {
-//            }
-//
-//            @Override
-//            public <T> T executeWithinTransaction(Supplier<T> task) {
-//                return null;
-//            }
-//
-//            @Override
-//            public void executeWithinNewTransaction(Runnable task) {
-//            }
-//
-//            @Override
-//            public <T> T executeWithinNewTransaction(Supplier<T> task) {
-//                return null;
-//            }
-//
-//        };
-//    }
-    
     @Service
     @Order(OrderPrecedence.MIDPOINT)
     @RequiredArgsConstructor(onConstructor_ = {@Inject})
@@ -107,8 +68,8 @@ public class Configuration_headless {
 //        private final CommandDispatcher commandDispatcher;
 
 
-        @EventListener(IsisInteractionLifecycleEvent.class)
-        public void onIsisInteractionLifecycleEvent(IsisInteractionLifecycleEvent event) {
+        @EventListener(InteractionLifecycleEvent.class)
+        public void onIsisInteractionLifecycleEvent(InteractionLifecycleEvent event) {
             switch(event.getEventType()) {
             case HAS_STARTED:
                 _Probe.errOut("Interaction HAS_STARTED conversationId=%s", event.getConversationId());
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/InteractionBoundaryProbe.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/InteractionBoundaryProbe.java
index dac6bbf..a19f465 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/InteractionBoundaryProbe.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/InteractionBoundaryProbe.java
@@ -20,53 +20,64 @@ package org.apache.isis.testdomain.util.interaction;
 
 import java.util.function.Supplier;
 
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 
 import org.junit.jupiter.api.Assertions;
-import org.springframework.stereotype.Component;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Service;
 
-import org.apache.isis.applib.annotation.IsisInteractionScope;
-import org.apache.isis.applib.services.TransactionScopeListener;
+import org.apache.isis.core.interaction.events.InteractionLifecycleEvent;
+import org.apache.isis.core.transaction.events.TransactionBeginEvent;
+import org.apache.isis.core.transaction.events.TransactionEndingEvent;
 import org.apache.isis.testdomain.util.kv.KVStoreForTesting;
 
 import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
-@Component
-@IsisInteractionScope
-public class InteractionBoundaryProbe implements TransactionScopeListener {
+@Service
+@Log4j2
+public class InteractionBoundaryProbe {
 
     @Inject private KVStoreForTesting kvStoreForTesting;
+    
+    
+    @EventListener(InteractionLifecycleEvent.class)
+    public void onIsisInteractionLifecycleEvent(InteractionLifecycleEvent event) {
+        switch(event.getEventType()) {
+        case HAS_STARTED:
+            onIaStarted();
+            break;
+        case IS_ENDING:
+            onIaEnded();
+            break;
+        default:
+            break;
+        }
+    }
 
     /** INTERACTION BEGIN BOUNDARY */
-    @PostConstruct
-    public void init() {
+    public void onIaStarted() {
+        log.debug("iaStarted");
         kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, "iaStarted");
     }
 
     /** INTERACTION END BOUNDARY */
-    @PreDestroy
-    public void destroy() {
+    public void onIaEnded() {
+        log.debug("iaEnded");
         kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, "iaEnded");
     }
 
     /** TRANSACTION BEGIN BOUNDARY */
-    @Override
-    public void onTransactionStarted() {
+    @EventListener(TransactionBeginEvent.class)
+    public void onTransactionStarted(TransactionBeginEvent event) {
+        log.debug("txStarted");
         kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, "txStarted");
     }
 
     /** TRANSACTION END BOUNDARY */
-    @Override
-    public void onPreCommit(PreCommitPhase preCommitPhase) {
-        switch (preCommitPhase) {
-        case POST_PUBLISHING:
-            kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, "txEnding");
-            break;
-        default:
-            break;
-        }
+    @EventListener(TransactionEndingEvent.class)
+    public void onPreCommit(TransactionEndingEvent event) {
+        kvStoreForTesting.incrementCounter(InteractionBoundaryProbe.class, "txEnding");
     }
     
     // -- ACCESS TO COUNTERS
diff --git a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/applayer/publishing/jdo/isis/JdoIsisEntityChangesPublishingTest.java b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/applayer/publishing/jdo/isis/JdoIsisEntityChangesPublishingTest.java
index 94ed007..171421e 100644
--- a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/applayer/publishing/jdo/isis/JdoIsisEntityChangesPublishingTest.java
+++ b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/applayer/publishing/jdo/isis/JdoIsisEntityChangesPublishingTest.java
@@ -18,6 +18,14 @@
  */
 package org.apache.isis.testdomain.applayer.publishing.jdo.isis;
 
+import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.clearPublishedEntries;
+import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getCreated;
+import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getDeleted;
+import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getLoaded;
+import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getModified;
+import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getUpdated;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
 import java.util.List;
 
 import javax.inject.Inject;
@@ -28,8 +36,6 @@ import org.junit.jupiter.api.TestFactory;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.testdomain.applayer.ApplicationLayerTestFactory;
 import org.apache.isis.testdomain.applayer.ApplicationLayerTestFactory.VerificationStage;
@@ -38,13 +44,6 @@ import org.apache.isis.testdomain.conf.Configuration_usingJdoIsis;
 import org.apache.isis.testdomain.util.kv.KVStoreForTesting;
 import org.apache.isis.testing.integtestsupport.applib.IsisIntegrationTestAbstract;
 
-import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.clearPublishedEntries;
-import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getCreated;
-import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getDeleted;
-import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getLoaded;
-import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getModified;
-import static org.apache.isis.testdomain.applayer.publishing.EntityChangesSubscriberForTesting.getUpdated;
-
 @SpringBootTest(
         classes = {
                 Configuration_usingJdoIsis.class,
diff --git a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/persistence/jdo/spring/JdoSpringBootstrappingTest.java b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/persistence/jdo/spring/JdoSpringBootstrappingTest.java
index 42f8624..2cd7b95 100644
--- a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/persistence/jdo/spring/JdoSpringBootstrappingTest.java
+++ b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/persistence/jdo/spring/JdoSpringBootstrappingTest.java
@@ -18,6 +18,11 @@
  */
 package org.apache.isis.testdomain.persistence.jdo.spring;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import java.sql.SQLException;
 import java.util.HashSet;
 import java.util.Optional;
@@ -38,11 +43,6 @@ import org.springframework.transaction.PlatformTransactionManager;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.support.DefaultTransactionDefinition;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-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;
@@ -99,7 +99,7 @@ class JdoSpringBootstrappingTest extends IsisIntegrationTestAbstract {
     void platformTransactionManager_shouldBeAvailable() {
         assertTrue(platformTransactionManager.isPresent());
         platformTransactionManager.ifPresent(ptm->{
-            assertEquals("JdoTransactionManager", ptm.getClass().getSimpleName());
+            assertEquals("ApplicationLayerAwareTransactionManager", ptm.getClass().getSimpleName());
         });
     }
     
diff --git a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/transactions/jdo/spring/JdoSpringTransactionScopeListenerTest.java b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/transactions/jdo/spring/JdoSpringTransactionScopeListenerTest.java
index a5cc41e..845db60 100644
--- a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/transactions/jdo/spring/JdoSpringTransactionScopeListenerTest.java
+++ b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/transactions/jdo/spring/JdoSpringTransactionScopeListenerTest.java
@@ -26,7 +26,9 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
+import org.springframework.transaction.annotation.Transactional;
 
+import org.apache.isis.applib.services.registry.ServiceRegistry;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.services.xactn.TransactionService;
 import org.apache.isis.core.config.presets.IsisPresets;
@@ -42,13 +44,19 @@ import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScripts;
         classes = { 
                 Configuration_usingJdoSpring.class,
                 InteractionBoundaryProbe.class
+        },
+        properties = {
+                "logging.level.org.apache.isis.testdomain.util.interaction.InteractionBoundaryProbe=DEBUG",
+                "logging.level.org.apache.isis.core.interaction.scope.IsisInteractionScope=DEBUG",
         })
 @TestPropertySource(IsisPresets.UseLog4j2Test)
+@Transactional
 /**
  * With this test we manage IsisInteractions ourselves. (not sub-classing IsisIntegrationTestAbstract)
  */
 class JdoSpringTransactionScopeListenerTest {
     
+    @Inject private ServiceRegistry serviceRegistry;
     @Inject private FixtureScripts fixtureScripts;
     @Inject private TransactionService transactionService;
     @Inject private RepositoryService repository;
@@ -67,6 +75,9 @@ class JdoSpringTransactionScopeListenerTest {
     @BeforeEach
     void setUp() {
         
+        // request a InteractionBoundaryProbe for the current interaction
+        serviceRegistry.lookupServiceElseFail(InteractionBoundaryProbe.class);
+        
         // new IsisInteractionScope with a new transaction (#1)
         isisInteractionFactory.runAnonymous(()->{
         
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/acceptheader/AcceptHeaderServiceForRest.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/acceptheader/AcceptHeaderServiceForRest.java
index 10fa052..2d50479 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/acceptheader/AcceptHeaderServiceForRest.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/acceptheader/AcceptHeaderServiceForRest.java
@@ -18,12 +18,12 @@
  */
 package org.apache.isis.viewer.restfulobjects.rendering.service.acceptheader;
 
+import static org.apache.isis.commons.internal.base._NullSafe.stream;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
 import javax.inject.Named;
 import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.container.ContainerRequestFilter;
@@ -38,22 +38,18 @@ import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 
+import org.apache.isis.applib.annotation.InteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.annotation.IsisInteractionScope;
 import org.apache.isis.applib.services.acceptheader.AcceptHeaderService;
 import org.apache.isis.commons.internal.base._NullSafe;
 
-import static org.apache.isis.commons.internal.base._NullSafe.stream;
-
-import lombok.extern.log4j.Log4j2;
-
 @Service
 @Named("isisRoRendering.AcceptHeaderServiceForRest")
 @Order(OrderPrecedence.MIDPOINT)
 @Primary
 @Qualifier("ForRest")
-@IsisInteractionScope
-@Log4j2
+@InteractionScope
+//@Log4j2
 public class AcceptHeaderServiceForRest implements AcceptHeaderService {
 
     private static ThreadLocal<List<MediaType>> mediaTypesByThread = new ThreadLocal<>();
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/WebRequestCycleForIsis.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/WebRequestCycleForIsis.java
index e9311ce..c4001fe 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/WebRequestCycleForIsis.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/WebRequestCycleForIsis.java
@@ -105,8 +105,7 @@ public class WebRequestCycleForIsis implements IRequestCycleListener {
         }
 
         val isisRequestCycle = IsisRequestCycle.next(
-                commonContext.lookupServiceElseFail(InteractionFactory.class),
-                commonContext.createTransactionTemplate());
+                commonContext.lookupServiceElseFail(InteractionFactory.class));
         
         requestCycle.setMetaData(REQ_CYCLE_HANDLE_KEY, isisRequestCycle);