From 799ee62f15dff07534beaf98a3ac551deffade38 Mon Sep 17 00:00:00 2001
From: Maros Marsalek <mmarsale@cisco.com>
Date: Tue, 17 May 2016 09:10:39 +0200
Subject: HONEYCOMB-61: Add BA broker for context data tree

With broker, context data can be accessed in a transactional
manner, same as config data

+ Renamed data-api concepts to not include DataTree
+ Renamed context related concepts to better distinguish between them
+ Now passing full ReadContext to read customizers
+ Naming context is backed by context data broker

Change-Id: I0b2876dd74a31a9ced7d9b5145672868e12f8b82
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
---
 .../honeycomb/v3po/data/impl/ConfigDataTree.java   | 173 ------------
 .../io/fd/honeycomb/v3po/data/impl/DataBroker.java | 134 +++++++--
 .../fd/honeycomb/v3po/data/impl/DataTreeUtils.java |   1 +
 .../data/impl/ModifiableDataTreeDelegator.java     | 174 ++++++++++++
 .../v3po/data/impl/ModifiableDataTreeManager.java  | 122 ++++++++
 .../v3po/data/impl/OperationalDataTree.java        | 239 ----------------
 .../v3po/data/impl/PersistingDataTreeAdapter.java  |   4 +
 .../v3po/data/impl/ReadOnlyTransaction.java        |  68 +++--
 .../v3po/data/impl/ReadableDataTreeDelegator.java  | 280 +++++++++++++++++++
 .../honeycomb/v3po/data/impl/WriteTransaction.java | 103 ++++---
 .../data/impl/rev160411/ConfigDataTreeModule.java  |  37 +--
 .../impl/rev160411/OperationalDataTreeModule.java  |  14 +-
 v3po/data-impl/src/main/yang/data-impl.yang        |  17 ++
 .../v3po/data/impl/ConfigDataTreeTest.java         | 271 ------------------
 .../honeycomb/v3po/data/impl/DataBrokerTest.java   |  20 +-
 .../data/impl/ModifiableDataTreeDelegatorTest.java | 308 +++++++++++++++++++++
 .../v3po/data/impl/OperationalDataTreeTest.java    | 185 -------------
 .../v3po/data/impl/ReadOnlyTransactionTest.java    |  10 +-
 .../data/impl/ReadableDataTreeDelegatorTest.java   | 193 +++++++++++++
 .../v3po/data/impl/WriteTransactionTest.java       |  30 +-
 20 files changed, 1372 insertions(+), 1011 deletions(-)
 delete mode 100644 v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java
 create mode 100644 v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
 create mode 100644 v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java
 delete mode 100644 v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java
 create mode 100644 v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java
 delete mode 100644 v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTreeTest.java
 create mode 100644 v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java
 delete mode 100644 v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTreeTest.java
 create mode 100644 v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java

(limited to 'v3po/data-impl/src')

diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java
deleted file mode 100644
index 1e118ff2a..000000000
--- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTree.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.data.impl;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static io.fd.honeycomb.v3po.data.impl.DataTreeUtils.childrenFromNormalized;
-
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.Futures;
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
-import io.fd.honeycomb.v3po.data.ModifiableDataTree;
-import io.fd.honeycomb.v3po.data.ReadableDataTree;
-import io.fd.honeycomb.v3po.translate.TranslationException;
-import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
-import java.util.Collections;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
-import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * DataTree implementation for configuration data.
- */
-public final class ConfigDataTree implements ModifiableDataTree {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ConfigDataTree.class);
-
-    private final BindingNormalizedNodeSerializer serializer;
-    private final DataTree dataTree;
-    private final WriterRegistry writerRegistry;
-    public static final ReadableDataTree EMPTY_OPERATIONAL = new ReadableDataTree() {
-        @Override
-        public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
-                @Nonnull final YangInstanceIdentifier path) {
-            return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>absent());
-        }
-    };
-
-    /**
-     * Creates configuration data tree instance.
-     *
-     * @param serializer     service for serialization between Java Binding Data representation and NormalizedNode
-     *                       representation.
-     * @param dataTree       data tree for configuration data representation
-     * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing
-     *                       bulk updates.
-     */
-    public ConfigDataTree(@Nonnull final BindingNormalizedNodeSerializer serializer,
-                          @Nonnull final DataTree dataTree, @Nonnull final WriterRegistry writerRegistry) {
-        this.serializer = checkNotNull(serializer, "serializer should not be null");
-        this.dataTree = checkNotNull(dataTree, "dataTree should not be null");
-        this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null");
-    }
-
-    @Override
-    public DataTreeSnapshot takeSnapshot() {
-        return new ConfigSnapshot(dataTree.takeSnapshot());
-    }
-
-    @Override
-    public void modify(final DataTreeModification modification)
-            throws DataValidationFailedException, TranslationException {
-        LOG.debug("ConfigDataTree.modify");
-
-        dataTree.validate(modification);
-
-        final DataTreeCandidate candidate = dataTree.prepare(modification);
-
-        final DataTreeCandidateNode rootNode = candidate.getRootNode();
-        final YangInstanceIdentifier rootPath = candidate.getRootPath();
-        final Optional<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore();
-        final Optional<NormalizedNode<?, ?>> normalizedDataAfter = rootNode.getDataAfter();
-        LOG.debug("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}",
-                rootPath, rootNode, normalizedDataBefore, normalizedDataAfter);
-
-        final Map<InstanceIdentifier<?>, DataObject> nodesBefore = extractNetconfData(normalizedDataBefore);
-        LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet());
-
-        final Map<InstanceIdentifier<?>, DataObject> nodesAfter = extractNetconfData(normalizedDataAfter);
-        LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet());
-
-        final DOMDataReadOnlyTransaction beforeTx = new ReadOnlyTransaction(EMPTY_OPERATIONAL, takeSnapshot());
-        final ConfigSnapshot modificationSnapshot = new ConfigSnapshot(modification);
-        final DOMDataReadOnlyTransaction afterTx = new ReadOnlyTransaction(EMPTY_OPERATIONAL, modificationSnapshot);
-        try (final WriteContext ctx = new TransactionWriteContext(serializer, beforeTx, afterTx)) {
-            writerRegistry.update(nodesBefore, nodesAfter, ctx);
-        } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) {
-            LOG.warn("Failed to apply all changes", e);
-            LOG.info("Trying to revert successful changes for current transaction");
-
-            try {
-                e.revertChanges();
-                LOG.info("Changes successfully reverted");
-            } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException) {
-                // fail with failed revert
-                LOG.error("Failed to revert successful changes", revertFailedException);
-                throw revertFailedException;
-            }
-
-            throw e; // fail with success revert
-        } catch (TranslationException e) {
-            LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e);
-            throw e;
-        }
-
-        dataTree.commit(candidate);
-    }
-
-    private Map<InstanceIdentifier<?>, DataObject> extractNetconfData(
-            final Optional<NormalizedNode<?, ?>> parentOptional) {
-        if (parentOptional.isPresent()) {
-            final DataContainerNode parent = (DataContainerNode) parentOptional.get();
-            return childrenFromNormalized(parent, serializer);
-        }
-        return Collections.emptyMap();
-    }
-
-    private final static class ConfigSnapshot implements DataTreeSnapshot {
-        private final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot;
-
-        ConfigSnapshot(@Nonnull final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot snapshot) {
-            this.snapshot = snapshot;
-        }
-
-        @Override
-        public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
-                @Nonnull final YangInstanceIdentifier path) {
-            final Optional<NormalizedNode<?, ?>> node = snapshot.readNode(path);
-            if (LOG.isTraceEnabled() && node.isPresent()) {
-                LOG.trace("ConfigSnapshot.read: {}", node.get());
-            }
-            return Futures.immediateCheckedFuture(node);
-        }
-
-        @Override
-        public DataTreeModification newModification() {
-            return snapshot.newModification();
-        }
-    }
-}
-
-
-
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java
index a0b585143..c418ed332 100644
--- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java
@@ -16,10 +16,13 @@
 
 package io.fd.honeycomb.v3po.data.impl;
 
-import com.google.common.base.Preconditions;
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
-import io.fd.honeycomb.v3po.data.ModifiableDataTree;
-import io.fd.honeycomb.v3po.data.ReadableDataTree;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.data.ModifiableDataManager;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import java.io.Closeable;
+import java.io.IOException;
 import java.util.Collections;
 import java.util.Map;
 import javax.annotation.Nonnull;
@@ -42,45 +45,36 @@ import org.slf4j.LoggerFactory;
  * Data Broker which provides data transaction functionality for YANG capable data provider using {@link NormalizedNode}
  * data format.
  */
-public class DataBroker implements DOMDataBroker {
+public class DataBroker implements DOMDataBroker, Closeable {
 
     private static final Logger LOG = LoggerFactory.getLogger(DataBroker.class);
-    private final ReadableDataTree operationalDataTree;
-    private final ModifiableDataTree configDataTree;
+    private final TransactionFactory transactionFactory;
 
     /**
      * Creates DataBroker instance.
      *
-     * @param operationalDataTree operational data
-     * @param configDataTree      configuration data
+     * @param transactionFactory transaction producing factory
      */
-    public DataBroker(@Nonnull final ReadableDataTree operationalDataTree,
-                      @Nonnull final ModifiableDataTree configDataTree) {
-        this.operationalDataTree =
-                Preconditions.checkNotNull(operationalDataTree, "operationalDataTree should not be null");
-        this.configDataTree = Preconditions.checkNotNull(configDataTree, "configDataTree should not be null");
-        LOG.trace("DataBroker({}).init() operationalDataTree={}, configDataTree={}", this, operationalDataTree, configDataTree);
+    public DataBroker(final TransactionFactory transactionFactory) {
+        this.transactionFactory = transactionFactory;
     }
 
     @Override
     public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
         LOG.trace("DataBroker({}).newReadOnlyTransaction()", this);
-        return new ReadOnlyTransaction(operationalDataTree, configDataTree.takeSnapshot());
+        return transactionFactory.newReadOnlyTransaction();
     }
 
     @Override
     public DOMDataReadWriteTransaction newReadWriteTransaction() {
         LOG.trace("DataBroker({}).newReadWriteTransaction()", this);
-        final DataTreeSnapshot configSnapshot = configDataTree.takeSnapshot();
-        final DOMDataReadOnlyTransaction readOnlyTx = new ReadOnlyTransaction(operationalDataTree, configSnapshot);
-        final DOMDataWriteTransaction writeOnlyTx = new WriteTransaction(configDataTree, configSnapshot);
-        return new ReadWriteTransaction(readOnlyTx, writeOnlyTx);
+        return transactionFactory.newReadWriteTransaction();
     }
 
     @Override
     public DOMDataWriteTransaction newWriteOnlyTransaction() {
         LOG.trace("DataBroker({}).newWriteOnlyTransaction()", this);
-        return new WriteTransaction(configDataTree);
+        return transactionFactory.newWriteOnlyTransaction();
     }
 
     @Override
@@ -101,6 +95,104 @@ public class DataBroker implements DOMDataBroker {
     public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
         return Collections.emptyMap();
     }
+
+    /**
+     * Create DataBroker for a modifiable config DT, but only readable Operational
+     */
+    @Nonnull
+    public static DataBroker create(@Nonnull final ModifiableDataManager configDataTree,
+                             @Nonnull final ReadableDataManager operationalDataTree) {
+        checkNotNull(operationalDataTree, "operationalDataTree should not be null");
+        checkNotNull(configDataTree, "configDataTree should not be null");
+        return new DataBroker(new MainPipelineTxFactory(configDataTree, operationalDataTree));
+    }
+
+    /**
+     * Create DataBroker for modifiable operational DT, but no support for config
+     */
+    @Nonnull
+    public static DataBroker create(@Nonnull final ModifiableDataManager operationalDataTree) {
+        checkNotNull(operationalDataTree, "operationalDataTree should not be null");
+        return new DataBroker(new ContextPipelineTxFactory(operationalDataTree));
+    }
+
+    @Override
+    public void close() throws IOException {
+        // NOOP
+    }
+
+    /**
+     * Transaction provider factory to be used by {@link DataBroker}
+     */
+    public interface TransactionFactory {
+
+        DOMDataReadOnlyTransaction newReadOnlyTransaction();
+
+        DOMDataReadWriteTransaction newReadWriteTransaction();
+
+        DOMDataWriteTransaction newWriteOnlyTransaction();
+    }
+
+    /**
+     * Transaction factory specific for Honeycomb's main pipeline (config: read+write, operational: read-only)
+     */
+    private static class MainPipelineTxFactory implements TransactionFactory {
+        private final ReadableDataManager operationalDataTree;
+        private final ModifiableDataManager configDataTree;
+
+        MainPipelineTxFactory(@Nonnull final ModifiableDataManager configDataTree,
+                              @Nonnull final ReadableDataManager operationalDataTree) {
+            this.operationalDataTree = operationalDataTree;
+            this.configDataTree = configDataTree;
+        }
+
+        @Override
+        public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+            return ReadOnlyTransaction.create(configDataTree.newModification(), operationalDataTree);
+        }
+
+        @Override
+        public DOMDataReadWriteTransaction newReadWriteTransaction() {
+            final DataModification configModification = configDataTree.newModification();
+            return new ReadWriteTransaction(
+                ReadOnlyTransaction.create(configModification, operationalDataTree),
+                WriteTransaction.createConfigOnly(configModification));
+        }
+
+        @Override
+        public DOMDataWriteTransaction newWriteOnlyTransaction() {
+            return WriteTransaction.createConfigOnly(configDataTree.newModification());
+        }
+    }
+
+    /**
+     * Transaction factory specific for Honeycomb's context pipeline (config: none, operational: read+write)
+     */
+    private static class ContextPipelineTxFactory implements TransactionFactory {
+        private final ModifiableDataManager operationalDataTree;
+
+        ContextPipelineTxFactory(@Nonnull final ModifiableDataManager operationalDataTree) {
+            this.operationalDataTree = operationalDataTree;
+        }
+
+        @Override
+        public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+            return ReadOnlyTransaction.createOperationalOnly(operationalDataTree);
+        }
+
+        @Override
+        public DOMDataReadWriteTransaction newReadWriteTransaction() {
+            final DataModification dataModification = operationalDataTree.newModification();
+            return new ReadWriteTransaction(
+                ReadOnlyTransaction.createOperationalOnly(dataModification),
+                WriteTransaction.createOperationalOnly(dataModification));
+        }
+
+        @Override
+        public DOMDataWriteTransaction newWriteOnlyTransaction() {
+            return WriteTransaction.createOperationalOnly(operationalDataTree.newModification());
+        }
+    }
 }
 
 
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java
index de83a198b..5833459ea 100644
--- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java
@@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory;
  * Utility class for various operations on DataTree.
  */
 final class DataTreeUtils {
+
     private static final Logger LOG = LoggerFactory.getLogger(DataTreeUtils.class);
 
     private DataTreeUtils() {
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
new file mode 100644
index 000000000..2b9010747
--- /dev/null
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.immediateCheckedFuture;
+import static io.fd.honeycomb.v3po.data.impl.DataTreeUtils.childrenFromNormalized;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.util.write.TransactionMappingContext;
+import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import java.util.Collections;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extension of {@link ModifiableDataTreeManager} that propagates data changes to underlying writer layer before they
+ * are fully committed in the backing data tree. Data changes are propagated in BA format.
+ */
+public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeDelegator.class);
+    private static final ReadableDataManager EMPTY_OPERATIONAL = p -> immediateCheckedFuture(Optional.absent());
+
+    // TODO what to use instead of deprecated BindingNormalizedNodeSerializer ?
+    private final BindingNormalizedNodeSerializer serializer;
+    private final WriterRegistry writerRegistry;
+    private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker;
+
+    /**
+     * Creates configuration data tree instance.
+     *  @param serializer     service for serialization between Java Binding Data representation and NormalizedNode
+     *                       representation.
+     * @param dataTree       data tree for configuration data representation
+     * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing
+     * @param contextBroker BA broker providing full access to mapping context data
+     */
+    public ModifiableDataTreeDelegator(@Nonnull final BindingNormalizedNodeSerializer serializer,
+                                       @Nonnull final DataTree dataTree,
+                                       @Nonnull final WriterRegistry writerRegistry,
+                                       @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) {
+        super(dataTree);
+        this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null");
+        this.serializer = checkNotNull(serializer, "serializer should not be null");
+        this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null");
+    }
+
+    @Override
+    public DataModification newModification() {
+        return new ConfigSnapshot(super.newModification());
+    }
+
+    private final class ConfigSnapshot extends ModifiableDataTreeManager.ConfigSnapshot {
+
+        private final DataModification untouchedModification;
+
+        /**
+         * @param untouchedModification DataModification captured while this modification/snapshot was created.
+         *                              To be used later while invoking writers to provide them with before state
+         *                              (state without current modifications).
+         *                              It must be captured as close as possible to when current modification started.
+         */
+        ConfigSnapshot(final DataModification untouchedModification) {
+            this.untouchedModification = untouchedModification;
+        }
+
+        /**
+         * Pass the changes to underlying writer layer.
+         * Transform from BI to BA.
+         * Revert(Write data before to subtrees that have been successfully modified before failure) in case of failure.
+         */
+        @Override
+        protected void processCandidate(final DataTreeCandidate candidate)
+            throws TranslationException {
+            final DataTreeCandidateNode rootNode = candidate.getRootNode();
+            final YangInstanceIdentifier rootPath = candidate.getRootPath();
+            final Optional<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore();
+            final Optional<NormalizedNode<?, ?>> normalizedDataAfter = rootNode.getDataAfter();
+            LOG.debug("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}",
+                rootPath, rootNode, normalizedDataBefore, normalizedDataAfter);
+
+            final Map<InstanceIdentifier<?>, DataObject> nodesBefore = toBindingAware(normalizedDataBefore);
+            LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet());
+            final Map<InstanceIdentifier<?>, DataObject> nodesAfter = toBindingAware(normalizedDataAfter);
+            LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet());
+
+            try (final WriteContext ctx = getTransactionWriteContext()) {
+                writerRegistry.update(nodesBefore, nodesAfter, ctx);
+
+                final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult =
+                    ((TransactionMappingContext) ctx.getMappingContext()).submit();
+                // Blocking on context data update
+                contextUpdateResult.checkedGet();
+
+            } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) {
+                LOG.warn("Failed to apply all changes", e);
+                LOG.info("Trying to revert successful changes for current transaction");
+
+                try {
+                    e.revertChanges();
+                    LOG.info("Changes successfully reverted");
+                } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException) {
+                    // fail with failed revert
+                    LOG.error("Failed to revert successful changes", revertFailedException);
+                    throw revertFailedException;
+                }
+
+                throw e; // fail with success revert
+            } catch (TransactionCommitFailedException e) {
+                // FIXME revert should probably occur when context is not written successfully, but can that even happen ?
+                final String msg = "Error while updating mapping context data";
+                LOG.error(msg, e);
+                throw new TranslationException(msg, e);
+            } catch (TranslationException e) {
+                LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e);
+                throw e;
+            }
+        }
+
+        private TransactionWriteContext getTransactionWriteContext() {
+            // Before Tx must use modification
+            final DOMDataReadOnlyTransaction beforeTx = ReadOnlyTransaction.create(untouchedModification, EMPTY_OPERATIONAL);
+            // After Tx must use current modification
+            final DOMDataReadOnlyTransaction afterTx = ReadOnlyTransaction.create(this, EMPTY_OPERATIONAL);
+            final TransactionMappingContext mappingContext = new TransactionMappingContext(
+                contextBroker.newReadWriteTransaction());
+            return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext);
+        }
+
+        private Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Optional<NormalizedNode<?, ?>> parentOptional) {
+            if (parentOptional.isPresent()) {
+                final DataContainerNode parent = (DataContainerNode) parentOptional.get();
+                return childrenFromNormalized(parent, serializer);
+            }
+            return Collections.emptyMap();
+        }
+    }
+}
+
+
+
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java
new file mode 100644
index 000000000..1082c479b
--- /dev/null
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.immediateCheckedFuture;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.data.ModifiableDataManager;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * DataTree backed implementation for modifiable data manager.
+ */
+public class ModifiableDataTreeManager implements ModifiableDataManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeManager.class);
+
+    private final DataTree dataTree;
+
+    public ModifiableDataTreeManager(@Nonnull final DataTree dataTree) {
+        this.dataTree = checkNotNull(dataTree, "dataTree should not be null");
+    }
+
+    @Override
+    public DataModification newModification() {
+        return new ConfigSnapshot();
+    }
+
+    @Override
+    public final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path) {
+        return newModification().read(path);
+    }
+
+    protected class ConfigSnapshot implements DataModification {
+        private final DataTreeModification modification;
+        private boolean validated = false;
+
+        ConfigSnapshot() {
+            this(dataTree.takeSnapshot().newModification());
+        }
+
+        protected ConfigSnapshot(final DataTreeModification modification) {
+            this.modification = modification;
+        }
+
+        @Override
+        public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
+                @Nonnull final YangInstanceIdentifier path) {
+            final Optional<NormalizedNode<?, ?>> node = modification.readNode(path);
+            if (LOG.isTraceEnabled() && node.isPresent()) {
+                LOG.trace("ConfigSnapshot.read: {}", node.get());
+            }
+            return immediateCheckedFuture(node);
+        }
+
+        @Override
+        public final void delete(final YangInstanceIdentifier path) {
+            modification.delete(path);
+        }
+
+        @Override
+        public final void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+            modification.merge(path, data);
+        }
+
+        @Override
+        public final void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+            modification.write(path, data);
+        }
+
+        @Override
+        public final void commit() throws DataValidationFailedException, TranslationException {
+            if(!validated) {
+                validate();
+            }
+            final DataTreeCandidate candidate = dataTree.prepare(modification);
+            processCandidate(candidate);
+            dataTree.commit(candidate);
+        }
+
+        protected void processCandidate(final DataTreeCandidate candidate) throws TranslationException {
+            // NOOP
+        }
+
+        @Override
+        public final void validate() throws DataValidationFailedException {
+            modification.ready();
+            dataTree.validate(modification);
+            validated = true;
+        }
+    }
+}
+
+
+
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java
deleted file mode 100644
index 175f22d34..000000000
--- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTree.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.data.impl;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.getOnlyElement;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Multimap;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.Futures;
-import io.fd.honeycomb.v3po.data.ReadableDataTree;
-import io.fd.honeycomb.v3po.translate.Context;
-import io.fd.honeycomb.v3po.translate.read.ReadContext;
-import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
-import io.fd.honeycomb.v3po.translate.read.ReaderRegistry;
-import java.util.Collection;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
-import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * ReadableDataTree implementation for operational data.
- */
-public final class OperationalDataTree implements ReadableDataTree {
-    private static final Logger LOG = LoggerFactory.getLogger(OperationalDataTree.class);
-
-    private final BindingNormalizedNodeSerializer serializer;
-    private final ReaderRegistry readerRegistry;
-    private final SchemaContext globalContext;
-    private final DOMDataBroker netconfMonitoringDomDataBrokerDependency;
-
-    /**
-     * Creates operational data tree instance.
-     *  @param serializer     service for serialization between Java Binding Data representation and NormalizedNode
-     *                       representation.
-     * @param globalContext  service for obtaining top level context data from all yang modules.
-     * @param readerRegistry service responsible for translation between DataObjects and data provider.
-     * @param netconfMonitoringDomDataBrokerDependency
-     */
-    public OperationalDataTree(@Nonnull BindingNormalizedNodeSerializer serializer,
-                               @Nonnull final SchemaContext globalContext, @Nonnull ReaderRegistry readerRegistry,
-                               final DOMDataBroker netconfMonitoringDomDataBrokerDependency) {
-        this.netconfMonitoringDomDataBrokerDependency = netconfMonitoringDomDataBrokerDependency;
-        this.globalContext = checkNotNull(globalContext, "globalContext should not be null");
-        this.serializer = checkNotNull(serializer, "serializer should not be null");
-        this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null");
-    }
-
-    @Override
-    public CheckedFuture<Optional<NormalizedNode<?, ?>>,
-            org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read(
-            @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) {
-
-        try(ReadContext ctx = new ReadContextImpl()) {
-            if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
-                return Futures.immediateCheckedFuture(readRoot(ctx));
-            } else {
-                return Futures.immediateCheckedFuture(readNode(yangInstanceIdentifier, ctx));
-            }
-        } catch (ReadFailedException e) {
-            return Futures.immediateFailedCheckedFuture(
-                    new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(
-                            "Failed to read VPP data", e));
-        }
-    }
-
-    private Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier yangInstanceIdentifier,
-                                                    final ReadContext ctx) throws ReadFailedException {
-
-        // FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/
-        if(yangInstanceIdentifier.getPathArguments().size() > 0 &&
-            yangInstanceIdentifier.getPathArguments().get(0).getNodeType().equals(NetconfState.QNAME)) {
-            return readFromNetconfDs(yangInstanceIdentifier);
-        }
-
-        LOG.debug("OperationalDataTree.readNode(), yangInstanceIdentifier={}", yangInstanceIdentifier);
-        final InstanceIdentifier<?> path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier);
-        checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier);
-        LOG.debug("OperationalDataTree.readNode(), path={}", path);
-
-        final Optional<? extends DataObject> dataObject;
-
-        dataObject = readerRegistry.read(path, ctx);
-        if (dataObject.isPresent()) {
-            final NormalizedNode<?, ?> value = toNormalizedNodeFunction(path).apply(dataObject.get());
-            return Optional.<NormalizedNode<?, ?>>fromNullable(value);
-        } else {
-            return Optional.absent();
-        }
-    }
-
-    // FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/
-    private Optional<NormalizedNode<?, ?>> readFromNetconfDs(final YangInstanceIdentifier yangInstanceIdentifier)
-        throws ReadFailedException {
-        try(final DOMDataReadOnlyTransaction domDataReadOnlyTransaction =
-            netconfMonitoringDomDataBrokerDependency.newReadOnlyTransaction()) {
-            try {
-                return domDataReadOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, yangInstanceIdentifier).checkedGet();
-            } catch (org.opendaylight.controller.md.sal.common.api.data.ReadFailedException e) {
-                throw new ReadFailedException(InstanceIdentifier.create(NetconfState.class), e);
-            }
-        }
-    }
-
-    private Optional<NormalizedNode<?, ?>> readRoot(final ReadContext ctx) throws ReadFailedException {
-        LOG.debug("OperationalDataTree.readRoot()");
-
-        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> dataNodeBuilder =
-                Builders.containerBuilder()
-                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME));
-
-        final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects =
-                readerRegistry.readAll(ctx);
-
-        for (final InstanceIdentifier<? extends DataObject> instanceIdentifier : dataObjects.keySet()) {
-            final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier);
-            final NormalizedNode<?, ?> node =
-                    wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier));
-            dataNodeBuilder.withChild((DataContainerChild<?, ?>) node);
-        }
-
-        // FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/
-        final Optional<NormalizedNode<?, ?>> normalizedNodeOptional =
-            readFromNetconfDs(YangInstanceIdentifier.builder().node(NetconfState.QNAME).build());
-        if(normalizedNodeOptional.isPresent()) {
-            dataNodeBuilder.withChild((DataContainerChild<?, ?>) normalizedNodeOptional.get());
-        }
-
-        return Optional.<NormalizedNode<?, ?>>of(dataNodeBuilder.build());
-    }
-
-    private NormalizedNode<?, ?> wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier,
-                                                 final InstanceIdentifier<? extends DataObject> instanceIdentifier,
-                                                 final Collection<? extends DataObject> dataObjects) {
-        final Collection<NormalizedNode<?, ?>> normalizedRootElements = Collections2
-                .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier));
-
-        final DataSchemaNode schemaNode =
-                globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
-        if (schemaNode instanceof ListSchemaNode) {
-            // In case of a list, wrap all the values in a Mixin parent node
-            final ListSchemaNode listSchema = (ListSchemaNode) schemaNode;
-            return wrapListIntoMixinNode(normalizedRootElements, listSchema);
-        } else {
-            Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected");
-            return getOnlyElement(normalizedRootElements);
-        }
-    }
-
-    private static DataContainerChild<?, ?> wrapListIntoMixinNode(
-            final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
-        if (listSchema.getKeyDefinition().isEmpty()) {
-            final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> listBuilder =
-                    Builders.unkeyedListBuilder();
-            for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
-                listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement);
-            }
-            return listBuilder.build();
-        } else {
-            final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> listBuilder =
-                    listSchema.isUserOrdered()
-                            ? Builders.orderedMapBuilder()
-                            : Builders.mapBuilder();
-
-            for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
-                listBuilder.withChild((MapEntryNode) normalizedRootElement);
-            }
-            return listBuilder.build();
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private Function<DataObject, NormalizedNode<?, ?>> toNormalizedNodeFunction(final InstanceIdentifier path) {
-        return dataObject -> {
-            LOG.trace("OperationalDataTree.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
-            final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
-                    serializer.toNormalizedNode(path, dataObject);
-
-            LOG.trace("OperationalDataTree.toNormalizedNode(), normalizedNodeEntry={}", entry);
-            return entry.getValue();
-        };
-    }
-
-    private static final class ReadContextImpl implements ReadContext {
-        public final Context ctx = new Context();
-
-        @Nonnull
-        @Override
-        public Context getContext() {
-            return ctx;
-        }
-
-        @Override
-        public void close() {
-            // Make sure to clear the storage in case some customizer stored it  to prevent memory leaks
-            ctx.close();
-        }
-    }
-}
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java
index 25d1d8dda..a54b7f148 100644
--- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java
@@ -127,6 +127,10 @@ public class PersistingDataTreeAdapter implements org.opendaylight.yangtools.yan
         if(currentRoot.isPresent()) {
             try {
                 LOG.trace("Persisting current data: {} into: {}", currentRoot.get(), path);
+                // Make sure the file gets overwritten
+                if(Files.exists(path)) {
+                    Files.delete(path);
+                }
                 // TODO once we are in static environment, do the writer, streamWriter and NNWriter initialization only once
                 final JsonWriter
                     jsonWriter = createJsonWriter(Files.newOutputStream(path, StandardOpenOption.CREATE), true);
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java
index 83c9e30bd..c38fa27ca 100644
--- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java
@@ -16,14 +16,16 @@
 
 package io.fd.honeycomb.v3po.data.impl;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import io.fd.honeycomb.v3po.data.ReadableDataTree;
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -37,17 +39,27 @@ import org.slf4j.LoggerFactory;
 final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction {
 
     private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTransaction.class);
-    private volatile ReadableDataTree operationalData;
-    private volatile DataTreeSnapshot configSnapshot;
 
-    ReadOnlyTransaction(@Nonnull final ReadableDataTree operationalData,
-                        @Nonnull final DataTreeSnapshot configSnapshot) {
-        this.operationalData = Preconditions.checkNotNull(operationalData, "operationalData should not be null");
-        this.configSnapshot = Preconditions.checkNotNull(configSnapshot, "config should not be null");
+    @Nullable
+    private volatile ReadableDataManager operationalData;
+    @Nullable
+    private volatile ReadableDataManager configSnapshot;
+
+    private volatile boolean closed = false;
+
+    /**
+     * @param configData config data tree manager. Null if config reads are not to be supported
+     * @param operationalData operational data tree manager. Null if operational reads are not to be supported
+     */
+    private ReadOnlyTransaction(@Nullable final ReadableDataManager configData,
+                                @Nullable final ReadableDataManager operationalData) {
+        this.configSnapshot = configData;
+        this.operationalData = operationalData;
     }
 
     @Override
     public void close() {
+        closed = true;
         configSnapshot = null;
         operationalData = null;
     }
@@ -57,12 +69,13 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction {
             final LogicalDatastoreType store,
             final YangInstanceIdentifier path) {
         LOG.debug("ReadOnlyTransaction.read(), store={}, path={}", store, path);
-
-        Preconditions.checkState(configSnapshot != null, "Transaction was closed");
+        checkState(!closed, "Transaction has been closed");
 
         if (store == LogicalDatastoreType.OPERATIONAL) {
+            checkArgument(operationalData != null, "{} reads not supported", store);
             return operationalData.read(path);
         } else {
+            checkArgument(configSnapshot != null, "{} reads not supported", store);
             return configSnapshot.read(path);
         }
     }
@@ -73,7 +86,6 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction {
         LOG.debug("ReadOnlyTransaction.exists() store={}, path={}", store, path);
 
         ListenableFuture<Boolean> listenableFuture = Futures.transform(read(store, path), IS_NODE_PRESENT);
-
         return Futures.makeChecked(listenableFuture, ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER);
     }
 
@@ -83,23 +95,25 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction {
         return this;
     }
 
+    @Nonnull
+    static ReadOnlyTransaction createOperationalOnly(@Nonnull final ReadableDataManager operationalData) {
+        return new ReadOnlyTransaction(null, requireNonNull(operationalData));
+    }
+
+    @Nonnull
+    static ReadOnlyTransaction createConfigOnly(@Nonnull final ReadableDataManager configData) {
+        return new ReadOnlyTransaction(requireNonNull(configData), null);
+    }
+
+    @Nonnull
+    static ReadOnlyTransaction create(@Nonnull final ReadableDataManager configData,
+                                      @Nonnull final ReadableDataManager operationalData) {
+        return new ReadOnlyTransaction(requireNonNull(configData), requireNonNull(operationalData));
+    }
 
     private static final Function<? super Optional<NormalizedNode<?, ?>>, ? extends Boolean> IS_NODE_PRESENT =
-            new Function<Optional<NormalizedNode<?, ?>>, Boolean>() {
-                @Nullable
-                @Override
-                public Boolean apply(@Nullable final Optional<NormalizedNode<?, ?>> input) {
-                    return input == null
-                            ? Boolean.FALSE
-                            : input.isPresent();
-                }
-            };
+        (Function<Optional<NormalizedNode<?, ?>>, Boolean>) input -> input == null ? Boolean.FALSE : input.isPresent();
 
     private static final Function<? super Exception, ReadFailedException> ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER =
-            new Function<Exception, ReadFailedException>() {
-                @Override
-                public ReadFailedException apply(@Nullable final Exception e) {
-                    return new ReadFailedException("Exists failed", e);
-                }
-            };
+        (Function<Exception, ReadFailedException>) e -> new ReadFailedException("Exists failed", e);
 }
\ No newline at end of file
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java
new file mode 100644
index 000000000..e660ee429
--- /dev/null
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.ModificationCache;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.util.write.TransactionMappingContext;
+import java.util.Collection;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ReadableDataTree implementation for operational data.
+ */
+public final class ReadableDataTreeDelegator implements ReadableDataManager {
+    private static final Logger LOG = LoggerFactory.getLogger(ReadableDataTreeDelegator.class);
+
+    private final BindingNormalizedNodeSerializer serializer;
+    private final ReaderRegistry readerRegistry;
+    private final SchemaContext globalContext;
+    private final DOMDataBroker netconfMonitoringDomDataBrokerDependency;
+    private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker;
+
+    /**
+     * Creates operational data tree instance.
+     *  @param serializer     service for serialization between Java Binding Data representation and NormalizedNode
+     *                       representation.
+     * @param globalContext  service for obtaining top level context data from all yang modules.
+     * @param readerRegistry service responsible for translation between DataObjects and data provider.
+     * @param netconfMonitoringDomDataBrokerDependency TODO remove
+     * @param contextBroker BA broker for context data
+     */
+    public ReadableDataTreeDelegator(@Nonnull BindingNormalizedNodeSerializer serializer,
+                                     @Nonnull final SchemaContext globalContext,
+                                     @Nonnull final ReaderRegistry readerRegistry,
+                                     @Nonnull final DOMDataBroker netconfMonitoringDomDataBrokerDependency,
+                                     @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) {
+        this.netconfMonitoringDomDataBrokerDependency = netconfMonitoringDomDataBrokerDependency;
+        this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null");
+        this.globalContext = checkNotNull(globalContext, "globalContext should not be null");
+        this.serializer = checkNotNull(serializer, "serializer should not be null");
+        this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null");
+    }
+
+    @Override
+    public CheckedFuture<Optional<NormalizedNode<?, ?>>,
+            org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read(
+            @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) {
+
+        try(TransactionMappingContext mappingContext = new TransactionMappingContext(contextBroker.newReadWriteTransaction());
+            ReadContext ctx = new ReadContextImpl(mappingContext)) {
+
+            final Optional<NormalizedNode<?, ?>> value;
+            if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
+                value = readRoot(ctx);
+            } else {
+                value = readNode(yangInstanceIdentifier, ctx);
+            }
+
+            // Submit context mapping updates
+            final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult =
+                ((TransactionMappingContext) ctx.getMappingContext()).submit();
+            // Blocking on context data update
+            contextUpdateResult.checkedGet();
+
+            return Futures.immediateCheckedFuture(value);
+
+        } catch (ReadFailedException e) {
+            return Futures.immediateFailedCheckedFuture(
+                new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(
+                    "Failed to read VPP data", e));
+        } catch (TransactionCommitFailedException e) {
+            // FIXME revert should probably occur when context is not written successfully, but can that even happen ?
+            final String msg = "Error while updating mapping context data";
+            LOG.error(msg, e);
+            return Futures.immediateFailedCheckedFuture(
+                new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(msg, e)
+            );
+        }
+    }
+
+    private Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier yangInstanceIdentifier,
+                                                    final ReadContext ctx) throws ReadFailedException {
+
+        // FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/
+        // Just delete, dedicated reader from NetconfMonitoringReaderModule takes care of netconf state data
+        // TODO test connecting with netconf and issuing a get (netconf-state) data should be provided
+        if(yangInstanceIdentifier.getPathArguments().size() > 0 &&
+            yangInstanceIdentifier.getPathArguments().get(0).getNodeType().equals(NetconfState.QNAME)) {
+            return readFromNetconfDs(yangInstanceIdentifier);
+        }
+
+        LOG.debug("OperationalDataTree.readNode(), yangInstanceIdentifier={}", yangInstanceIdentifier);
+        final InstanceIdentifier<?> path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier);
+        checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier);
+        LOG.debug("OperationalDataTree.readNode(), path={}", path);
+
+        final Optional<? extends DataObject> dataObject;
+
+        dataObject = readerRegistry.read(path, ctx);
+        if (dataObject.isPresent()) {
+            final NormalizedNode<?, ?> value = toNormalizedNodeFunction(path).apply(dataObject.get());
+            return Optional.<NormalizedNode<?, ?>>fromNullable(value);
+        } else {
+            return Optional.absent();
+        }
+    }
+
+    // FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/
+    private Optional<NormalizedNode<?, ?>> readFromNetconfDs(final YangInstanceIdentifier yangInstanceIdentifier)
+        throws ReadFailedException {
+        try(final DOMDataReadOnlyTransaction domDataReadOnlyTransaction =
+            netconfMonitoringDomDataBrokerDependency.newReadOnlyTransaction()) {
+            try {
+                return domDataReadOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, yangInstanceIdentifier).checkedGet();
+            } catch (org.opendaylight.controller.md.sal.common.api.data.ReadFailedException e) {
+                throw new ReadFailedException(InstanceIdentifier.create(NetconfState.class), e);
+            }
+        }
+    }
+
+    private Optional<NormalizedNode<?, ?>> readRoot(final ReadContext ctx) throws ReadFailedException {
+        LOG.debug("OperationalDataTree.readRoot()");
+
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> dataNodeBuilder =
+                Builders.containerBuilder()
+                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME));
+
+        final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects =
+                readerRegistry.readAll(ctx);
+
+        for (final InstanceIdentifier<? extends DataObject> instanceIdentifier : dataObjects.keySet()) {
+            final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier);
+            final NormalizedNode<?, ?> node =
+                    wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier));
+            dataNodeBuilder.withChild((DataContainerChild<?, ?>) node);
+        }
+
+        // FIXME workaround for: https://git.opendaylight.org/gerrit/#/c/37499/
+        final Optional<NormalizedNode<?, ?>> normalizedNodeOptional =
+            readFromNetconfDs(YangInstanceIdentifier.builder().node(NetconfState.QNAME).build());
+        if(normalizedNodeOptional.isPresent()) {
+            dataNodeBuilder.withChild((DataContainerChild<?, ?>) normalizedNodeOptional.get());
+        }
+
+        return Optional.<NormalizedNode<?, ?>>of(dataNodeBuilder.build());
+    }
+
+    private NormalizedNode<?, ?> wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier,
+                                                 final InstanceIdentifier<? extends DataObject> instanceIdentifier,
+                                                 final Collection<? extends DataObject> dataObjects) {
+        final Collection<NormalizedNode<?, ?>> normalizedRootElements = Collections2
+                .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier));
+
+        final DataSchemaNode schemaNode =
+                globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
+        if (schemaNode instanceof ListSchemaNode) {
+            // In case of a list, wrap all the values in a Mixin parent node
+            final ListSchemaNode listSchema = (ListSchemaNode) schemaNode;
+            return wrapListIntoMixinNode(normalizedRootElements, listSchema);
+        } else {
+            Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected");
+            return getOnlyElement(normalizedRootElements);
+        }
+    }
+
+    private static DataContainerChild<?, ?> wrapListIntoMixinNode(
+            final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
+        if (listSchema.getKeyDefinition().isEmpty()) {
+            final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> listBuilder =
+                    Builders.unkeyedListBuilder();
+            for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
+                listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement);
+            }
+            return listBuilder.build();
+        } else {
+            final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> listBuilder =
+                    listSchema.isUserOrdered()
+                            ? Builders.orderedMapBuilder()
+                            : Builders.mapBuilder();
+
+            for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
+                listBuilder.withChild((MapEntryNode) normalizedRootElement);
+            }
+            return listBuilder.build();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Function<DataObject, NormalizedNode<?, ?>> toNormalizedNodeFunction(final InstanceIdentifier path) {
+        return dataObject -> {
+            LOG.trace("OperationalDataTree.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
+            final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
+                    serializer.toNormalizedNode(path, dataObject);
+
+            LOG.trace("OperationalDataTree.toNormalizedNode(), normalizedNodeEntry={}", entry);
+            return entry.getValue();
+        };
+    }
+
+    private static final class ReadContextImpl implements ReadContext {
+
+        public final ModificationCache ctx = new ModificationCache();
+        private final MappingContext mappingContext;
+
+        private ReadContextImpl(final MappingContext mappingContext) {
+            this.mappingContext = mappingContext;
+        }
+
+        @Nonnull
+        @Override
+        public ModificationCache getModificationCache() {
+            return ctx;
+        }
+
+        @Nonnull
+        @Override
+        public MappingContext getMappingContext() {
+            return mappingContext;
+        }
+
+        @Override
+        public void close() {
+            // Make sure to clear the storage in case some customizer stored a reference to it to prevent memory leaks
+            ctx.close();
+        }
+    }
+}
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java
index fbeba7e07..3644a9fe7 100644
--- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java
@@ -16,6 +16,8 @@
 
 package io.fd.honeycomb.v3po.data.impl;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
 import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.CANCELED;
 import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.COMMITED;
 import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.FAILED;
@@ -26,10 +28,10 @@ import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import io.fd.honeycomb.v3po.data.ModifiableDataTree;
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
+import io.fd.honeycomb.v3po.data.DataModification;
 import io.fd.honeycomb.v3po.translate.TranslationException;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.annotation.concurrent.NotThreadSafe;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -38,7 +40,6 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -47,30 +48,21 @@ import org.slf4j.LoggerFactory;
 final class WriteTransaction implements DOMDataWriteTransaction {
 
     private static final Logger LOG = LoggerFactory.getLogger(WriteTransaction.class);
-    private final ModifiableDataTree configDataTree;
-    private DataTreeModification modification;
-    private TransactionStatus status;
-
-    WriteTransaction(@Nonnull final ModifiableDataTree configDataTree,
-                     @Nonnull final DataTreeSnapshot configSnapshot) {
-        this.configDataTree = Preconditions.checkNotNull(configDataTree, "configDataTree should not be null");
-        Preconditions.checkNotNull(configSnapshot, "configSnapshot should not be null");
-        // initialize transaction state:
-        modification = configSnapshot.newModification();
-        status = NEW;
-    }
 
-    WriteTransaction(@Nonnull final ModifiableDataTree configDataTree) {
-        this(configDataTree, configDataTree.takeSnapshot());
-    }
+    @Nullable
+    private DataModification operationalModification;
+    @Nullable
+    private DataModification configModification;
+    private TransactionStatus status = NEW;
 
-    private static void checkConfigurationWrite(final LogicalDatastoreType store) {
-        Preconditions.checkArgument(LogicalDatastoreType.CONFIGURATION == store, "Write is not supported for operational data store");
+    private WriteTransaction(@Nullable final DataModification configModification,
+                             @Nullable final DataModification operationalModification) {
+        this.operationalModification = operationalModification;
+        this.configModification = configModification;
     }
 
     private void checkIsNew() {
         Preconditions.checkState(status == NEW, "Transaction was submitted or canceled");
-        Preconditions.checkState(modification != null, "DataTree modification should not be null");
     }
 
     @Override
@@ -78,8 +70,21 @@ final class WriteTransaction implements DOMDataWriteTransaction {
                     final NormalizedNode<?, ?> data) {
         LOG.debug("WriteTransaction.put() store={}, path={}, data={}", store, path, data);
         checkIsNew();
-        checkConfigurationWrite(store);
-        modification.write(path, data);
+        handleOperation(store, (modification) -> modification.write(path, data));
+    }
+
+    private void handleOperation(final LogicalDatastoreType store,
+                                 final java.util.function.Consumer<DataModification> r) {
+        switch (store) {
+            case CONFIGURATION:
+                checkArgument(configModification != null, "Modification of {} is not supported", store);
+                r.accept(configModification);
+                break;
+            case OPERATIONAL:
+                checkArgument(operationalModification != null, "Modification of {} is not supported", store);
+                r.accept(operationalModification);
+                break;
+        }
     }
 
     @Override
@@ -87,8 +92,7 @@ final class WriteTransaction implements DOMDataWriteTransaction {
                       final NormalizedNode<?, ?> data) {
         LOG.debug("WriteTransaction.merge() store={}, path={}, data={}", store, path, data);
         checkIsNew();
-        checkConfigurationWrite(store);
-        modification.merge(path, data);
+        handleOperation(store, (modification) -> modification.merge(path, data));
     }
 
     @Override
@@ -98,7 +102,6 @@ final class WriteTransaction implements DOMDataWriteTransaction {
             return false;
         } else {
             status = CANCELED;
-            modification = null;
             return true;
         }
     }
@@ -107,29 +110,38 @@ final class WriteTransaction implements DOMDataWriteTransaction {
     public void delete(LogicalDatastoreType store, final YangInstanceIdentifier path) {
         LOG.debug("WriteTransaction.delete() store={}, path={}", store, path);
         checkIsNew();
-        checkConfigurationWrite(store);
-        modification.delete(path);
+        handleOperation(store, (modification) -> modification.delete(path));
     }
 
     @Override
     public CheckedFuture<Void, TransactionCommitFailedException> submit() {
-        LOG.debug("WriteTransaction.submit()");
+        LOG.trace("WriteTransaction.submit()");
         checkIsNew();
 
-        // seal transaction:
-        modification.ready();
-        status = SUBMITED;
-
         try {
-            configDataTree.modify(modification);
+            status = SUBMITED;
+
+            // Validate first to catch any issues before attempting commit
+            if (configModification != null) {
+                configModification.validate();
+            }
+            if (operationalModification != null) {
+                operationalModification.validate();
+            }
+
+            if(configModification != null) {
+                configModification.commit();
+            }
+            if(operationalModification != null) {
+                operationalModification.commit();
+            }
+
             status = COMMITED;
         } catch (DataValidationFailedException | TranslationException e) {
             status = FAILED;
             LOG.error("Failed modify data tree", e);
             return Futures.immediateFailedCheckedFuture(
-                    new TransactionCommitFailedException("Failed to validate DataTreeModification", e));
-        } finally {
-            modification = null;
+                new TransactionCommitFailedException("Failed to validate DataTreeModification", e));
         }
         return Futures.immediateCheckedFuture(null);
     }
@@ -144,4 +156,21 @@ final class WriteTransaction implements DOMDataWriteTransaction {
     public Object getIdentifier() {
         return this;
     }
+
+
+    @Nonnull
+    static WriteTransaction createOperationalOnly(@Nonnull final DataModification operationalData) {
+        return new WriteTransaction(null, requireNonNull(operationalData));
+    }
+
+    @Nonnull
+    static WriteTransaction createConfigOnly(@Nonnull final DataModification configData) {
+        return new WriteTransaction(requireNonNull(configData), null);
+    }
+
+    @Nonnull
+    static WriteTransaction create(@Nonnull final DataModification configData,
+                            @Nonnull final DataModification operationalData) {
+        return new WriteTransaction(requireNonNull(configData), requireNonNull(operationalData));
+    }
 }
diff --git a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
index 86a3f9db4..3e871e09a 100644
--- a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
+++ b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
@@ -1,11 +1,14 @@
 package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
 
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
-import io.fd.honeycomb.v3po.data.ModifiableDataTree;
-import io.fd.honeycomb.v3po.data.impl.ConfigDataTree;
-import io.fd.honeycomb.v3po.translate.TranslationException;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.data.ModifiableDataManager;
+import io.fd.honeycomb.v3po.data.impl.ModifiableDataTreeDelegator;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,14 +38,15 @@ public class ConfigDataTreeModule extends
     public java.lang.AutoCloseable createInstance() {
         LOG.debug("ConfigDataTreeModule.createInstance()");
         return new CloseableConfigDataTree(
-                new ConfigDataTree(getSerializerDependency(), getDataTreeDependency(), getWriterRegistryDependency()));
+                new ModifiableDataTreeDelegator(getSerializerDependency(), getDataTreeDependency(), getWriterRegistryDependency(),
+                    getContextBindingBrokerDependency()));
     }
 
-    private static final class CloseableConfigDataTree implements ModifiableDataTree, AutoCloseable {
+    private static final class CloseableConfigDataTree implements ModifiableDataManager, AutoCloseable {
 
-        private final ConfigDataTree delegate;
+        private final ModifiableDataTreeDelegator delegate;
 
-        CloseableConfigDataTree(final ConfigDataTree delegate) {
+        CloseableConfigDataTree(final ModifiableDataTreeDelegator delegate) {
             this.delegate = delegate;
         }
 
@@ -53,16 +57,15 @@ public class ConfigDataTreeModule extends
         }
 
         @Override
-        public void modify(final DataTreeModification modification)
-                throws DataValidationFailedException, TranslationException {
-            LOG.trace("CloseableConfigDataTree.modify modification={}", modification);
-            delegate.modify(modification);
+        public DataModification newModification() {
+            LOG.trace("CloseableConfigDataTree.newModification");
+            return delegate.newModification();
         }
 
         @Override
-        public DataTreeSnapshot takeSnapshot() {
-            LOG.trace("CloseableConfigDataTree.takeSnapshot");
-            return delegate.takeSnapshot();
+        public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
+            @Nonnull final YangInstanceIdentifier path) {
+            return delegate.read(path);
         }
     }
 }
diff --git a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java
index 2fbba75fa..286eaf664 100644
--- a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java
+++ b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java
@@ -2,8 +2,8 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.
 
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
-import io.fd.honeycomb.v3po.data.ReadableDataTree;
-import io.fd.honeycomb.v3po.data.impl.OperationalDataTree;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.data.impl.ReadableDataTreeDelegator;
 import javax.annotation.Nonnull;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -37,15 +37,15 @@ public class OperationalDataTreeModule extends
     public java.lang.AutoCloseable createInstance() {
         LOG.debug("OperationalDataTreeModule.createInstance()");
         return new CloseableOperationalDataTree(
-                new OperationalDataTree(getSerializerDependency(), getSchemaServiceDependency().getGlobalContext(),
-                        getReaderRegistryDependency(), getNetconfMonitoringDomDataBrokerDependency()));
+                new ReadableDataTreeDelegator(getSerializerDependency(), getSchemaServiceDependency().getGlobalContext(),
+                        getReaderRegistryDependency(), getNetconfMonitoringDomDataBrokerDependency(), getContextBindingBrokerDependency()));
     }
 
-    private static final class CloseableOperationalDataTree implements ReadableDataTree, AutoCloseable {
+    private static final class CloseableOperationalDataTree implements ReadableDataManager, AutoCloseable {
 
-        private final OperationalDataTree delegate;
+        private final ReadableDataTreeDelegator delegate;
 
-        CloseableOperationalDataTree(final OperationalDataTree delegate) {
+        CloseableOperationalDataTree(final ReadableDataTreeDelegator delegate) {
             this.delegate = delegate;
         }
 
diff --git a/v3po/data-impl/src/main/yang/data-impl.yang b/v3po/data-impl/src/main/yang/data-impl.yang
index 454e99a66..3af9d8d3f 100644
--- a/v3po/data-impl/src/main/yang/data-impl.yang
+++ b/v3po/data-impl/src/main/yang/data-impl.yang
@@ -121,6 +121,14 @@ module data-impl {
                 }
             }
 
+            container context-binding-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity md-sal-binding:binding-async-data-broker;
+                    }
+                }
+            }
         }
     }
 
@@ -171,6 +179,15 @@ module data-impl {
                 }
             }
 
+            container context-binding-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity md-sal-binding:binding-async-data-broker;
+                    }
+                }
+            }
+
         }
     }
 }
\ No newline at end of file
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTreeTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTreeTest.java
deleted file mode 100644
index b7f8b9d2c..000000000
--- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ConfigDataTreeTest.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.data.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyMap;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.CheckedFuture;
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
-import io.fd.honeycomb.v3po.translate.TranslationException;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
-
-public class ConfigDataTreeTest {
-
-    @Mock
-    private WriterRegistry writer;
-    @Mock
-    private BindingNormalizedNodeSerializer serializer;
-    @Mock
-    private DataTree dataTree;
-    @Mock
-    private DataTreeModification modification;
-
-    private ConfigDataTree configDataTree;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        configDataTree = new ConfigDataTree(serializer, dataTree, writer);
-    }
-
-    @Test
-    public void testRead() throws Exception {
-        final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
-                snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
-        when(dataTree.takeSnapshot()).thenReturn(snapshot);
-
-        final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class);
-        final Optional node = mock(Optional.class);
-        doReturn(node).when(snapshot).readNode(path);
-
-        final DataTreeSnapshot dataTreeSnapshot = configDataTree.takeSnapshot();
-        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
-                dataTreeSnapshot.read(path);
-
-        verify(dataTree).takeSnapshot();
-        verify(snapshot).readNode(path);
-
-        assertTrue(future.isDone());
-        assertEquals(node, future.get());
-    }
-
-    @Test
-    public void testNewModification() throws Exception {
-        final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
-                snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
-        when(dataTree.takeSnapshot()).thenReturn(snapshot);
-
-        when(snapshot.newModification()).thenReturn(modification);
-
-        final DataTreeSnapshot dataTreeSnapshot = configDataTree.takeSnapshot();
-        final DataTreeModification newModification = dataTreeSnapshot.newModification();
-        verify(dataTree).takeSnapshot();
-        verify(snapshot).newModification();
-
-        assertEquals(modification, newModification);
-    }
-
-    @Test
-    public void testCommitSuccessful() throws Exception {
-        final DataObject dataBefore = mockDataObject("before", Ethernet.class);
-        final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
-        // Prepare modification:
-        final DataTreeCandidateNode rootNode = mockRootNode();
-        // data before:
-        final ContainerNode nodeBefore = mockContainerNode(dataBefore);
-        when(rootNode.getDataBefore()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeBefore));
-        // data after:
-        final ContainerNode nodeAfter = mockContainerNode(dataAfter);
-        when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter));
-
-        // Run the test
-        configDataTree.modify(modification);
-
-        // Verify all changes were processed:
-        verify(writer).update(
-                mapOf(dataBefore, Ethernet.class),
-                mapOf(dataAfter, Ethernet.class),
-                any(WriteContext.class));
-
-        // Verify modification was validated
-        verify(dataTree).validate(modification);
-    }
-
-    private Map<InstanceIdentifier<?>, DataObject> mapOf(final DataObject dataBefore, final Class<Ethernet> type) {
-        return eq(
-                Collections.<InstanceIdentifier<?>, DataObject>singletonMap(InstanceIdentifier.create(type),
-                        dataBefore));
-    }
-
-    private DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
-        final DataObject dataBefore = mock(classToMock, name);
-        doReturn(classToMock).when(dataBefore).getImplementedInterface();
-        return dataBefore;
-    }
-
-    @Test
-    public void testCommitUndoSuccessful() throws Exception {
-        // Prepare data changes:
-        final DataObject dataBefore = mockDataObject("before", Ethernet.class);
-        final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
-        final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter reverter = mock(
-            io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.class);
-
-        // Fail on update:
-        final TranslationException failedOnUpdateException = new TranslationException("update failed");
-        doThrow(new io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
-                failedOnUpdateException)).when(writer).update(anyMap(), anyMap(), any(WriteContext.class));
-
-        // Prepare modification:
-        final DataTreeCandidateNode rootNode = mockRootNode();
-        // data before:
-        final ContainerNode nodeBefore = mockContainerNode(dataBefore);
-        when(rootNode.getDataBefore()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeBefore));
-        // data after:
-        final ContainerNode nodeAfter = mockContainerNode(dataAfter);
-        when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter));
-
-        // Run the test
-        try {
-            configDataTree.modify(modification);
-        } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) {
-            verify(writer).update(anyMap(), anyMap(), any(WriteContext.class));
-            verify(reverter).revert();
-            assertEquals(failedOnUpdateException, e.getCause());
-            return;
-        }
-
-        fail("WriterRegistry.BulkUpdateException was expected");
-    }
-
-    @Test
-    public void testCommitUndoFailed() throws Exception {
-        // Prepare data changes:
-        final DataObject dataBefore = mockDataObject("before", Ethernet.class);
-        final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
-        final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter reverter = mock(
-            io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.class);
-
-        // Fail on update:
-        doThrow(new io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
-                new TranslationException("update failed"))).when(writer).update(anyMap(), anyMap(), any(WriteContext.class));
-
-        // Fail on revert:
-        final TranslationException failedOnRevertException = new TranslationException("update failed");
-        final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException =
-                new io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException(Collections.<InstanceIdentifier<?>>emptyList(),
-                        failedOnRevertException);
-        doThrow(revertFailedException).when(reverter).revert();
-
-        // Prepare modification:
-        final DataTreeCandidateNode rootNode = mockRootNode();
-        // data before:
-        final ContainerNode nodeBefore = mockContainerNode(dataBefore);
-        when(rootNode.getDataBefore()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeBefore));
-        // data after:
-        final ContainerNode nodeAfter = mockContainerNode(dataAfter);
-        when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter));
-
-        // Run the test
-        try {
-            configDataTree.modify(modification);
-        } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException e) {
-            verify(writer).update(anyMap(), anyMap(), any(WriteContext.class));
-            verify(reverter).revert();
-            assertEquals(failedOnRevertException, e.getCause());
-            return;
-        }
-
-        fail("RevertFailedException was expected");
-    }
-
-    private DataTreeCandidateNode mockRootNode() {
-        final DataTreeCandidate candidate = mock(DataTreeCandidate.class);
-        when(dataTree.prepare(modification)).thenReturn(candidate);
-
-        final DataTreeCandidateNode rootNode = mock(DataTreeCandidateNode.class);
-        when(candidate.getRootNode()).thenReturn(rootNode);
-
-        return rootNode;
-    }
-
-    private ContainerNode mockContainerNode(DataObject... modifications) {
-        final int numberOfChildren = modifications.length;
-
-        final YangInstanceIdentifier.NodeIdentifier identifier =
-                YangInstanceIdentifier.NodeIdentifier.create(QName.create("/"));
-
-        final ContainerNode node = mock(ContainerNode.class);
-        when(node.getIdentifier()).thenReturn(identifier);
-
-        final List<DataContainerChild> list = new ArrayList<>(numberOfChildren);
-        doReturn(list).when(node).getValue();
-
-        for (DataObject modification : modifications) {
-            final DataContainerChild child = mock(DataContainerChild.class);
-            when(child.getIdentifier()).thenReturn(mock(YangInstanceIdentifier.PathArgument.class));
-            list.add(child);
-
-            final Map.Entry entry = mock(Map.Entry.class);
-            final Class<? extends DataObject> implementedInterface =
-                    (Class<? extends DataObject>) modification.getImplementedInterface();
-            final InstanceIdentifier<?> id = InstanceIdentifier.create(implementedInterface);
-
-            doReturn(id).when(entry).getKey();
-            doReturn(modification).when(entry).getValue();
-            doReturn(entry).when(serializer).fromNormalizedNode(any(YangInstanceIdentifier.class), eq(child));
-        }
-        return node;
-    }
-}
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java
index a2908d591..55b92b50b 100644
--- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java
+++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java
@@ -22,9 +22,9 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
-import io.fd.honeycomb.v3po.data.ReadableDataTree;
-import io.fd.honeycomb.v3po.data.ModifiableDataTree;
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.data.ModifiableDataManager;
+import io.fd.honeycomb.v3po.data.DataModification;
 import java.util.Map;
 import org.junit.Before;
 import org.junit.Test;
@@ -42,18 +42,18 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 public class DataBrokerTest {
 
     @Mock
-    private ReadableDataTree operationalData;
+    private ReadableDataManager operationalData;
     @Mock
-    private ModifiableDataTree confiDataTree;
+    private ModifiableDataManager confiDataTree;
     @Mock
-    private DataTreeSnapshot configSnapshot;
+    private DataModification configSnapshot;
     private DataBroker broker;
 
     @Before
     public void setUp() {
         initMocks(this);
-        when(confiDataTree.takeSnapshot()).thenReturn(configSnapshot);
-        broker = new DataBroker(operationalData, confiDataTree);
+        when(confiDataTree.newModification()).thenReturn(configSnapshot);
+        broker = DataBroker.create(confiDataTree, operationalData);
     }
 
     @Test
@@ -64,7 +64,7 @@ public class DataBrokerTest {
 
         // verify that read and write transactions use the same config snapshot
         verify(configSnapshot).read(path);
-        verify(configSnapshot).newModification();
+        verify(confiDataTree).newModification();
     }
 
     @Test
@@ -72,7 +72,7 @@ public class DataBrokerTest {
         final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction();
 
         // verify that write transactions use config snapshot
-        verify(configSnapshot).newModification();
+        verify(confiDataTree).newModification();
     }
 
     @Test
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java
new file mode 100644
index 000000000..fed32da8a
--- /dev/null
+++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+
+public class ModifiableDataTreeDelegatorTest {
+
+    @Mock
+    private WriterRegistry writer;
+    @Mock
+    private BindingNormalizedNodeSerializer serializer;
+    @Mock
+    private DataTree dataTree;
+    @Mock
+    private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification;
+    @Mock
+    private DataBroker contextBroker;
+
+    private ModifiableDataTreeManager configDataTree;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+        configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker);
+    }
+
+    @Test
+    public void testRead() throws Exception {
+        final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
+                snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
+        when(dataTree.takeSnapshot()).thenReturn(snapshot);
+        when(snapshot.newModification()).thenReturn(modification);
+
+        final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class);
+        final Optional node = mock(Optional.class);
+        doReturn(node).when(modification).readNode(path);
+
+        final DataModification dataTreeSnapshot = configDataTree.newModification();
+        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+                dataTreeSnapshot.read(path);
+
+        verify(dataTree, times(2)).takeSnapshot();
+        verify(modification).readNode(path);
+
+        assertTrue(future.isDone());
+        assertEquals(node, future.get());
+    }
+
+    @Test
+    public void testNewModification() throws Exception {
+        final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
+                snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
+        when(dataTree.takeSnapshot()).thenReturn(snapshot);
+        when(snapshot.newModification()).thenReturn(modification);
+
+        final DataModification dataTreeSnapshot = configDataTree.newModification();
+        // Snapshot captured twice, so that original data could be provided to translation layer without any possible
+        // modification
+        verify(dataTree, times(2)).takeSnapshot();
+        verify(snapshot, times(2)).newModification();
+    }
+
+    @Test
+    public void testCommitSuccessful() throws Exception {
+        final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
+            snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
+        when(dataTree.takeSnapshot()).thenReturn(snapshot);
+        when(snapshot.newModification()).thenReturn(modification);
+        final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
+        doReturn(prepare).when(dataTree).prepare(modification);
+
+        final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock(
+            org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class);
+        doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction();
+        doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit();
+
+        final DataObject dataBefore = mockDataObject("before", Ethernet.class);
+        final DataObject dataAfter = mockDataObject("after", Ethernet.class);
+
+        // Prepare modification:
+        final DataTreeCandidateNode rootNode = mockRootNode();
+        // data before:
+        final ContainerNode nodeBefore = mockContainerNode(dataBefore);
+        when(rootNode.getDataBefore()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeBefore));
+        // data after:
+        final ContainerNode nodeAfter = mockContainerNode(dataAfter);
+        when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter));
+
+        // Run the test
+        doReturn(rootNode).when(prepare).getRootNode();
+        final DataModification dataModification = configDataTree.newModification();
+        dataModification.commit();
+
+        // Verify all changes were processed:
+        verify(writer).update(
+                mapOf(dataBefore, Ethernet.class),
+                mapOf(dataAfter, Ethernet.class),
+                any(WriteContext.class));
+
+        // Verify modification was validated
+        verify(dataTree).validate(modification);
+        // Verify context transaction was finished
+        verify(ctxTransaction).submit();
+    }
+
+    private Map<InstanceIdentifier<?>, DataObject> mapOf(final DataObject dataBefore, final Class<Ethernet> type) {
+        return eq(
+                Collections.<InstanceIdentifier<?>, DataObject>singletonMap(InstanceIdentifier.create(type),
+                        dataBefore));
+    }
+
+    private DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
+        final DataObject dataBefore = mock(classToMock, name);
+        doReturn(classToMock).when(dataBefore).getImplementedInterface();
+        return dataBefore;
+    }
+
+    @Test
+    public void testCommitUndoSuccessful() throws Exception {
+        final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
+            snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
+        when(dataTree.takeSnapshot()).thenReturn(snapshot);
+        when(snapshot.newModification()).thenReturn(modification);
+        final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
+        doReturn(prepare).when(dataTree).prepare(modification);
+
+        // Prepare data changes:
+        final DataObject dataBefore = mockDataObject("before", Ethernet.class);
+        final DataObject dataAfter = mockDataObject("after", Ethernet.class);
+
+        final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter reverter = mock(
+            io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.class);
+
+        // Fail on update:
+        final TranslationException failedOnUpdateException = new TranslationException("update failed");
+        doThrow(new io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
+                failedOnUpdateException)).when(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+
+        // Prepare modification:
+        final DataTreeCandidateNode rootNode = mockRootNode();
+        // data before:
+        final ContainerNode nodeBefore = mockContainerNode(dataBefore);
+        when(rootNode.getDataBefore()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeBefore));
+        // data after:
+        final ContainerNode nodeAfter = mockContainerNode(dataAfter);
+        when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter));
+
+        // Run the test
+        try {
+            doReturn(rootNode).when(prepare).getRootNode();
+            final DataModification dataModification = configDataTree.newModification();
+            dataModification.commit();
+        } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) {
+            verify(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+            verify(reverter).revert();
+            assertEquals(failedOnUpdateException, e.getCause());
+            return;
+        }
+
+        fail("WriterRegistry.BulkUpdateException was expected");
+    }
+
+    @Test
+    public void testCommitUndoFailed() throws Exception {
+        final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
+            snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
+        when(dataTree.takeSnapshot()).thenReturn(snapshot);
+        when(snapshot.newModification()).thenReturn(modification);
+        final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
+        doReturn(prepare).when(dataTree).prepare(modification);
+
+        // Prepare data changes:
+        final DataObject dataBefore = mockDataObject("before", Ethernet.class);
+        final DataObject dataAfter = mockDataObject("after", Ethernet.class);
+
+        final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter reverter = mock(
+            io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.class);
+
+        // Fail on update:
+        doThrow(new io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
+                new TranslationException("update failed"))).when(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+
+        // Fail on revert:
+        final TranslationException failedOnRevertException = new TranslationException("update failed");
+        final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException =
+                new io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException(Collections.<InstanceIdentifier<?>>emptyList(),
+                        failedOnRevertException);
+        doThrow(revertFailedException).when(reverter).revert();
+
+        // Prepare modification:
+        final DataTreeCandidateNode rootNode = mockRootNode();
+        // data before:
+        final ContainerNode nodeBefore = mockContainerNode(dataBefore);
+        when(rootNode.getDataBefore()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeBefore));
+        // data after:
+        final ContainerNode nodeAfter = mockContainerNode(dataAfter);
+        when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>fromNullable(nodeAfter));
+
+        // Run the test
+        try {
+            doReturn(rootNode).when(prepare).getRootNode();
+            final DataModification dataModification = configDataTree.newModification();
+            dataModification.commit();
+        } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException e) {
+            verify(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+            verify(reverter).revert();
+            assertEquals(failedOnRevertException, e.getCause());
+            return;
+        }
+
+        fail("RevertFailedException was expected");
+    }
+
+    private DataTreeCandidateNode mockRootNode() {
+        final DataTreeCandidate candidate = mock(DataTreeCandidate.class);
+        when(dataTree.prepare(modification)).thenReturn(candidate);
+
+        final DataTreeCandidateNode rootNode = mock(DataTreeCandidateNode.class);
+        when(candidate.getRootNode()).thenReturn(rootNode);
+
+        return rootNode;
+    }
+
+    private ContainerNode mockContainerNode(DataObject... modifications) {
+        final int numberOfChildren = modifications.length;
+
+        final YangInstanceIdentifier.NodeIdentifier identifier =
+                YangInstanceIdentifier.NodeIdentifier.create(QName.create("/"));
+
+        final ContainerNode node = mock(ContainerNode.class);
+        when(node.getIdentifier()).thenReturn(identifier);
+
+        final List<DataContainerChild> list = new ArrayList<>(numberOfChildren);
+        doReturn(list).when(node).getValue();
+
+        for (DataObject modification : modifications) {
+            final DataContainerChild child = mock(DataContainerChild.class);
+            when(child.getIdentifier()).thenReturn(mock(YangInstanceIdentifier.PathArgument.class));
+            list.add(child);
+
+            final Map.Entry entry = mock(Map.Entry.class);
+            final Class<? extends DataObject> implementedInterface =
+                    (Class<? extends DataObject>) modification.getImplementedInterface();
+            final InstanceIdentifier<?> id = InstanceIdentifier.create(implementedInterface);
+
+            doReturn(id).when(entry).getKey();
+            doReturn(modification).when(entry).getValue();
+            doReturn(entry).when(serializer).fromNormalizedNode(any(YangInstanceIdentifier.class), eq(child));
+        }
+        return node;
+    }
+}
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTreeTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTreeTest.java
deleted file mode 100644
index 7e6abcdcd..000000000
--- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/OperationalDataTreeTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.data.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.Futures;
-import io.fd.honeycomb.v3po.translate.read.ReadContext;
-import io.fd.honeycomb.v3po.translate.read.ReaderRegistry;
-import java.util.Collections;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState;
-import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-
-public class OperationalDataTreeTest {
-
-    @Mock
-    private BindingNormalizedNodeSerializer serializer;
-    @Mock
-    private ReaderRegistry reader;
-
-    private OperationalDataTree operationalData;
-
-    @Mock
-    private InstanceIdentifier<DataObject> id;
-    @Mock
-    private Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry;
-    @Mock
-    private SchemaContext globalContext;
-    @Mock
-    private DataSchemaNode schemaNode;
-    @Mock
-    private ReadContext readCtx;
-    @Mock
-    private DOMDataBroker netconfMonitoringBroker;
-    @Mock
-    private DOMDataReadOnlyTransaction domDataReadOnlyTransaction;
-
-    @Before
-    public void setUp() {
-        initMocks(this);
-        operationalData = new OperationalDataTree(serializer, globalContext, reader, netconfMonitoringBroker);
-        doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class));
-
-        doReturn(domDataReadOnlyTransaction).when(netconfMonitoringBroker).newReadOnlyTransaction();
-        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(domDataReadOnlyTransaction)
-            .read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class));
-    }
-
-    @Test
-    public void testReadNode() throws Exception {
-        final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
-        final YangInstanceIdentifier.PathArgument pArg = mock(YangInstanceIdentifier.PathArgument.class);
-        doReturn(pArg).when(yangId).getLastPathArgument();
-        doReturn(Collections.singletonList(pArg)).when(yangId).getPathArguments();
-
-        doReturn(QName.create("namespace", "2012-12-12", "local")).when(pArg).getNodeType();
-        doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
-
-        final DataObject dataObject = mock(DataObject.class);
-        doReturn(Optional.of(dataObject)).when(reader).read(same(id), any(ReadContext.class));
-
-        when(serializer.toNormalizedNode(id, dataObject)).thenReturn(entry);
-        final DataContainerChild<?, ?> expectedValue = mock(DataContainerChild.class);
-        doReturn(expectedValue).when(entry).getValue();
-
-        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = operationalData.read(yangId);
-
-        verify(serializer).fromYangInstanceIdentifier(yangId);
-        verify(reader).read(same(id), any(ReadContext.class));
-        final Optional<NormalizedNode<?, ?>> result = future.get();
-        assertTrue(result.isPresent());
-        assertEquals(expectedValue, result.get());
-    }
-
-    @Test
-    public void testReadNonExistingNode() throws Exception {
-        final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
-        doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
-        doReturn(Optional.absent()).when(reader).read(same(id), any(ReadContext.class));
-
-        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = operationalData.read(yangId);
-
-        verify(serializer).fromYangInstanceIdentifier(yangId);
-        verify(reader).read(same(id), any(ReadContext.class));
-        final Optional<NormalizedNode<?, ?>> result = future.get();
-        assertFalse(result.isPresent());
-    }
-
-    @Test
-    public void testReadFailed() throws Exception{
-        doThrow(io.fd.honeycomb.v3po.translate.read.ReadFailedException.class).when(reader).readAll(any(ReadContext.class));
-
-        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
-                operationalData.read( YangInstanceIdentifier.EMPTY);
-
-        try {
-            future.checkedGet();
-        } catch (ReadFailedException e) {
-            assertTrue(e.getCause() instanceof io.fd.honeycomb.v3po.translate.read.ReadFailedException);
-            return;
-        }
-        fail("ReadFailedException was expected");
-    }
-
-    @Test
-    public void testReadRootWithOneNonListElement() throws Exception {
-        // Prepare data
-        final InstanceIdentifier<VppState> vppStateII = InstanceIdentifier.create(VppState.class);
-        final VppState vppState = mock(VppState.class);
-        Multimap<InstanceIdentifier<?>, DataObject> dataObjects = LinkedListMultimap.create();
-        dataObjects.put(vppStateII, vppState);
-        doReturn(dataObjects).when(reader).readAll(any(ReadContext.class));
-
-        // Init serializer
-        final YangInstanceIdentifier vppYangId = YangInstanceIdentifier.builder().node(VppState.QNAME).build();
-        when(serializer.toYangInstanceIdentifier(vppStateII)).thenReturn(vppYangId);
-        when(serializer.toNormalizedNode(vppStateII, vppState)).thenReturn(entry);
-        final DataContainerChild<?, ?> vppStateContainer = mock(DataContainerChild.class);
-        doReturn(vppStateContainer).when(entry).getValue();
-        doReturn(vppYangId.getLastPathArgument()).when(vppStateContainer).getIdentifier();
-
-        // Read root
-        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
-                operationalData.read(YangInstanceIdentifier.EMPTY);
-
-        verify(reader).readAll(any(ReadContext.class));
-        verify(serializer).toYangInstanceIdentifier(vppStateII);
-        verify(serializer).toNormalizedNode(vppStateII, vppState);
-
-        // Check the result is an ContainerNode with only one child
-        final Optional<NormalizedNode<?, ?>> result = future.get();
-        assertTrue(result.isPresent());
-
-        final ContainerNode rootNode = (ContainerNode) result.get();
-        assertEquals(SchemaContext.NAME, rootNode.getIdentifier().getNodeType());
-        assertEquals(vppStateContainer, Iterables.getOnlyElement(rootNode.getValue()));
-    }
-}
\ No newline at end of file
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java
index 30fdd1392..a13621725 100644
--- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java
+++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java
@@ -24,8 +24,8 @@ import static org.mockito.MockitoAnnotations.initMocks;
 
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
-import io.fd.honeycomb.v3po.data.ReadableDataTree;
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.data.DataModification;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -37,16 +37,16 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 public class ReadOnlyTransactionTest {
 
     @Mock
-    private ReadableDataTree operationalData;
+    private ReadableDataManager operationalData;
     @Mock
-    private DataTreeSnapshot configSnapshot;
+    private DataModification configSnapshot;
 
     private ReadOnlyTransaction readOnlyTx;
 
     @Before
     public void setUp() {
         initMocks(this);
-        readOnlyTx = new ReadOnlyTransaction(operationalData, configSnapshot);
+        readOnlyTx = ReadOnlyTransaction.create(configSnapshot, operationalData);
     }
 
     @Test
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java
new file mode 100644
index 000000000..4492c70a2
--- /dev/null
+++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReaderRegistry;
+import java.util.Collections;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class ReadableDataTreeDelegatorTest {
+
+    @Mock
+    private BindingNormalizedNodeSerializer serializer;
+    @Mock
+    private ReaderRegistry reader;
+
+    private ReadableDataTreeDelegator operationalData;
+
+    @Mock
+    private InstanceIdentifier<DataObject> id;
+    @Mock
+    private Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry;
+    @Mock
+    private SchemaContext globalContext;
+    @Mock
+    private DataSchemaNode schemaNode;
+    @Mock
+    private ReadContext readCtx;
+    @Mock
+    private DOMDataBroker netconfMonitoringBroker;
+    @Mock
+    private DOMDataReadOnlyTransaction domDataReadOnlyTransaction;
+    @Mock
+    private DataBroker contextBroker;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+        operationalData = new ReadableDataTreeDelegator(serializer, globalContext, reader, netconfMonitoringBroker, contextBroker);
+        doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class));
+
+        doReturn(domDataReadOnlyTransaction).when(netconfMonitoringBroker).newReadOnlyTransaction();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(domDataReadOnlyTransaction)
+            .read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class));
+
+        final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock(
+            org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class);
+        doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction();
+        doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit();
+    }
+
+    @Test
+    public void testReadNode() throws Exception {
+        final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
+        final YangInstanceIdentifier.PathArgument pArg = mock(YangInstanceIdentifier.PathArgument.class);
+        doReturn(pArg).when(yangId).getLastPathArgument();
+        doReturn(Collections.singletonList(pArg)).when(yangId).getPathArguments();
+
+        doReturn(QName.create("namespace", "2012-12-12", "local")).when(pArg).getNodeType();
+        doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
+
+        final DataObject dataObject = mock(DataObject.class);
+        doReturn(Optional.of(dataObject)).when(reader).read(same(id), any(ReadContext.class));
+
+        when(serializer.toNormalizedNode(id, dataObject)).thenReturn(entry);
+        final DataContainerChild<?, ?> expectedValue = mock(DataContainerChild.class);
+        doReturn(expectedValue).when(entry).getValue();
+
+        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = operationalData.read(yangId);
+
+        verify(serializer).fromYangInstanceIdentifier(yangId);
+        verify(reader).read(same(id), any(ReadContext.class));
+        final Optional<NormalizedNode<?, ?>> result = future.get();
+        assertTrue(result.isPresent());
+        assertEquals(expectedValue, result.get());
+    }
+
+    @Test
+    public void testReadNonExistingNode() throws Exception {
+        final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
+        doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
+        doReturn(Optional.absent()).when(reader).read(same(id), any(ReadContext.class));
+
+        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = operationalData.read(yangId);
+
+        verify(serializer).fromYangInstanceIdentifier(yangId);
+        verify(reader).read(same(id), any(ReadContext.class));
+        final Optional<NormalizedNode<?, ?>> result = future.get();
+        assertFalse(result.isPresent());
+    }
+
+    @Test
+    public void testReadFailed() throws Exception{
+        doThrow(io.fd.honeycomb.v3po.translate.read.ReadFailedException.class).when(reader).readAll(any(ReadContext.class));
+
+        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+                operationalData.read( YangInstanceIdentifier.EMPTY);
+
+        try {
+            future.checkedGet();
+        } catch (ReadFailedException e) {
+            assertTrue(e.getCause() instanceof io.fd.honeycomb.v3po.translate.read.ReadFailedException);
+            return;
+        }
+        fail("ReadFailedException was expected");
+    }
+
+    @Test
+    public void testReadRootWithOneNonListElement() throws Exception {
+        // Prepare data
+        final InstanceIdentifier<VppState> vppStateII = InstanceIdentifier.create(VppState.class);
+        final VppState vppState = mock(VppState.class);
+        Multimap<InstanceIdentifier<?>, DataObject> dataObjects = LinkedListMultimap.create();
+        dataObjects.put(vppStateII, vppState);
+        doReturn(dataObjects).when(reader).readAll(any(ReadContext.class));
+
+        // Init serializer
+        final YangInstanceIdentifier vppYangId = YangInstanceIdentifier.builder().node(VppState.QNAME).build();
+        when(serializer.toYangInstanceIdentifier(vppStateII)).thenReturn(vppYangId);
+        when(serializer.toNormalizedNode(vppStateII, vppState)).thenReturn(entry);
+        final DataContainerChild<?, ?> vppStateContainer = mock(DataContainerChild.class);
+        doReturn(vppStateContainer).when(entry).getValue();
+        doReturn(vppYangId.getLastPathArgument()).when(vppStateContainer).getIdentifier();
+
+        // Read root
+        final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+                operationalData.read(YangInstanceIdentifier.EMPTY);
+
+        verify(reader).readAll(any(ReadContext.class));
+        verify(serializer).toYangInstanceIdentifier(vppStateII);
+        verify(serializer).toNormalizedNode(vppStateII, vppState);
+
+        // Check the result is an ContainerNode with only one child
+        final Optional<NormalizedNode<?, ?>> result = future.get();
+        assertTrue(result.isPresent());
+
+        final ContainerNode rootNode = (ContainerNode) result.get();
+        assertEquals(SchemaContext.NAME, rootNode.getIdentifier().getNodeType());
+        assertEquals(vppStateContainer, Iterables.getOnlyElement(rootNode.getValue()));
+    }
+}
\ No newline at end of file
diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java
index d0360dbd8..9cde27d2b 100644
--- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java
+++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java
@@ -23,12 +23,10 @@ import static org.junit.Assert.fail;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
 import com.google.common.util.concurrent.CheckedFuture;
-import io.fd.honeycomb.v3po.data.ModifiableDataTree;
-import io.fd.honeycomb.v3po.data.DataTreeSnapshot;
+import io.fd.honeycomb.v3po.data.DataModification;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -36,54 +34,48 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
 
 public class WriteTransactionTest {
 
     @Mock
-    private ModifiableDataTree configDataTree;
-    @Mock
-    private DataTreeSnapshot configSnapshot;
+    private DataModification configSnapshot;
     @Mock
     private YangInstanceIdentifier path;
     @Mock
     private NormalizedNode<?,?> data;
-    @Mock
-    private DataTreeModification dataTreeModification;
 
     private WriteTransaction writeTx;
 
     @Before
     public void setUp() {
         initMocks(this);
-        when(configSnapshot.newModification()).thenReturn(dataTreeModification);
-        writeTx = new WriteTransaction(configDataTree, configSnapshot);
+        writeTx = WriteTransaction.createConfigOnly(configSnapshot);
     }
 
     @Test
     public void testPut() {
         writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
-        verify(dataTreeModification).write(path, data);
+        verify(configSnapshot).write(path, data);
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testPutOperational() {
         writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data);
-        verify(dataTreeModification).write(path, data);
+        verify(configSnapshot).write(path, data);
     }
 
     @Test(expected = IllegalStateException.class)
     public void testOnFinishedTx() {
         writeTx.submit();
         writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
-        verify(dataTreeModification).write(path, data);
+        verify(configSnapshot).write(path, data);
     }
 
     @Test
     public void testMerge() {
         writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, data);
-        verify(dataTreeModification).merge(path, data);
+        verify(configSnapshot).merge(path, data);
     }
 
     @Test
@@ -100,19 +92,19 @@ public class WriteTransactionTest {
     @Test
     public void testDelete() {
         writeTx.delete(LogicalDatastoreType.CONFIGURATION, path);
-        verify(dataTreeModification).delete(path);
+        verify(configSnapshot).delete(path);
     }
 
     @Test
     public void testSubmit() throws Exception {
         writeTx.submit();
-        verify(dataTreeModification).ready();
-        verify(configDataTree).modify(dataTreeModification);
+        verify(configSnapshot).validate();
+        verify(configSnapshot).commit();
     }
 
     @Test
     public void testSubmitFailed() throws Exception {
-        doThrow(mock(DataValidationFailedException.class)).when(configDataTree).modify(dataTreeModification);
+        doThrow(mock(DataValidationFailedException.class)).when(configSnapshot).commit();
         final CheckedFuture<Void, TransactionCommitFailedException> future = writeTx.submit();
         try {
             future.get();
-- 
cgit