diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml
index cfd47ea146697a6e2dffaf2c1342bc7374c08a33..eb97aa454f6e8c63583bfd9cd4b6fec32b6d67e1 100644
--- a/app/code/Magento/CatalogInventory/etc/di.xml
+++ b/app/code/Magento/CatalogInventory/etc/di.xml
@@ -72,6 +72,6 @@
         <plugin name="catalogInventoryAfterLoad" type="\Magento\CatalogInventory\Model\Plugin\AfterProductLoad"/>
     </type>
     <type name="Magento\Catalog\Api\ProductRepositoryInterface">
-        <plugin name="catalogInventoryAroundSave" type="\Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave"/>
+        <plugin name="catalogInventoryAroundSave" sortOrder="20" type="Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave"/>
     </type>
 </config>
diff --git a/app/code/Magento/CatalogSearch/Model/Search/ReaderPlugin.php b/app/code/Magento/CatalogSearch/Model/Search/ReaderPlugin.php
index 21dd2f62495b2b7bf9b41c80beeed612a07ae47b..307b34c008fb238e6e79b87ec21a6a3a4099d431 100644
--- a/app/code/Magento/CatalogSearch/Model/Search/ReaderPlugin.php
+++ b/app/code/Magento/CatalogSearch/Model/Search/ReaderPlugin.php
@@ -24,18 +24,17 @@ class ReaderPlugin
     /**
      * Merge reader's value with generated
      *
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      * @param \Magento\Framework\Config\ReaderInterface $subject
-     * @param \Closure $proceed
-     * @param string $scope
+     * @param array $result
+     * @param string|null $scope
      * @return array
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundRead(
+    public function afterRead(
         \Magento\Framework\Config\ReaderInterface $subject,
-        \Closure $proceed,
+        array $result,
         $scope = null
     ) {
-        $result = $proceed($scope);
         $result = array_merge_recursive($result, $this->requestGenerator->generate());
         return $result;
     }
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/ReaderPluginTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/ReaderPluginTest.php
index 69644eee90222f09e7a3d9e74ee23292c4801070..afae3ccb4f4b595ed04141927b1e85279689988b 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/ReaderPluginTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/ReaderPluginTest.php
@@ -29,18 +29,18 @@ class ReaderPluginTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    public function testAroundRead()
+    public function testAfterRead()
     {
+        $readerConfig = ['test' => 'b', 'd' => 'e'];
         $this->requestGenerator->expects($this->once())
             ->method('generate')
             ->will($this->returnValue(['test' => 'a']));
 
-        $result = $this->object->aroundRead(
+        $result = $this->object->afterRead(
             $this->getMockBuilder(\Magento\Framework\Config\ReaderInterface::class)
                 ->disableOriginalConstructor()->getMock(),
-            function () {
-                return ['test' => 'b', 'd' => 'e'];
-            }
+            $readerConfig,
+            null
         );
 
         $this->assertEquals(['test' => ['b', 'a'], 'd' => 'e'], $result);
diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Category/Move.php b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Category/Move.php
index 1477a10655472d3e88543e0567dd2d6733af3d3c..4175a7ddb4ef9d9fd0f05d92d0d3d5bd53cfa374 100644
--- a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Category/Move.php
+++ b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Category/Move.php
@@ -14,6 +14,11 @@ class Move
     /** @var CategoryUrlPathGenerator */
     protected $categoryUrlPathGenerator;
 
+    /**
+     * @var ChildrenCategoriesProvider
+     */
+    private $childrenCategoriesProvider;
+
     /**
      * @param CategoryUrlPathGenerator $categoryUrlPathGenerator
      * @param ChildrenCategoriesProvider $childrenCategoriesProvider
@@ -27,22 +32,23 @@ class Move
     }
 
     /**
+     * Perform url updating for children categories
+     *
      * @param \Magento\Catalog\Model\ResourceModel\Category $subject
-     * @param callable $proceed
+     * @param \Magento\Catalog\Model\ResourceModel\Category $result
      * @param Category $category
      * @param Category $newParent
      * @param null|int $afterCategoryId
-     * @return callable
+     * @return \Magento\Catalog\Model\ResourceModel\Category
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundChangeParent(
+    public function afterChangeParent(
         \Magento\Catalog\Model\ResourceModel\Category $subject,
-        \Closure $proceed,
-        $category,
-        $newParent,
+        \Magento\Catalog\Model\ResourceModel\Category $result,
+        Category $category,
+        Category $newParent,
         $afterCategoryId
     ) {
-        $result = $proceed($category, $newParent, $afterCategoryId);
         $category->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category));
         $category->getResource()->saveAttribute($category, 'url_path');
         $this->updateUrlPathForChildren($category);
diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Storage.php b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Storage.php
index 768660e56daf5c25b6f8953bb2477aab97659d74..740c32762690e72eb588d7387f2a71467a15e2f7 100644
--- a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Storage.php
+++ b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Storage.php
@@ -33,14 +33,13 @@ class Storage
 
     /**
      * @param \Magento\UrlRewrite\Model\StorageInterface $object
-     * @param callable $proceed
-     * @param array $urls
+     * @param null $result
+     * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] $urls
      * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundReplace(StorageInterface $object, \Closure $proceed, array $urls)
+    public function afterReplace(StorageInterface $object, $result, array $urls)
     {
-        $proceed($urls);
         $toSave = [];
         foreach ($this->filterUrls($urls) as $record) {
             $metadata = $record->getMetadata();
diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/Group.php b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/Group.php
index f3c86bad49936bc044d05a9fe5b6ca07ba293dad..1b18385d4a6fd8e9da1e693cf181fad8adfbd328 100644
--- a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/Group.php
+++ b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/Group.php
@@ -63,19 +63,19 @@ class Group
     }
 
     /**
-     * @param \Magento\Store\Model\ResourceModel\Group $object
-     * @param callable $proceed
+     * Perform updating url for categories and products assigned to the group
+     *
+     * @param \Magento\Store\Model\ResourceModel\Group $subject
+     * @param \Magento\Store\Model\ResourceModel\Group $result
      * @param AbstractModel $group
      * @return \Magento\Store\Model\ResourceModel\Group
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
-        \Magento\Store\Model\ResourceModel\Group $object,
-        \Closure $proceed,
+    public function afterSave(
+        \Magento\Store\Model\ResourceModel\Group $subject,
+        \Magento\Store\Model\ResourceModel\Group $result,
         AbstractModel $group
     ) {
-        $originGroup = $group;
-        $result = $proceed($originGroup);
         if (!$group->isObjectNew()
             && ($group->dataHasChangedFor('website_id')
                 || $group->dataHasChangedFor('root_category_id'))
diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/View.php b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/View.php
index ade02f8446921f74e34cb8bd3b58bc836575fe54..ea28b20f3f8f6d58211377bd9b16034d8f04a23c 100644
--- a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/View.php
+++ b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Store/View.php
@@ -5,7 +5,6 @@
  */
 namespace Magento\CatalogUrlRewrite\Model\Category\Plugin\Store;
 
-use Magento\Catalog\Model\Category;
 use Magento\Catalog\Model\CategoryFactory;
 use Magento\Catalog\Model\ProductFactory;
 use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator;
@@ -53,19 +52,19 @@ class View
     }
 
     /**
-     * @param \Magento\Store\Model\ResourceModel\Store $object
-     * @param callable $proceed
+     * Perform updating url for categories and products assigned to the store view
+     *
+     * @param \Magento\Store\Model\ResourceModel\Store $subject
+     * @param \Magento\Store\Model\ResourceModel\Store $result
      * @param AbstractModel $store
      * @return \Magento\Store\Model\ResourceModel\Store
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
-        \Magento\Store\Model\ResourceModel\Store $object,
-        \Closure $proceed,
+    public function afterSave(
+        \Magento\Store\Model\ResourceModel\Store $subject,
+        \Magento\Store\Model\ResourceModel\Store $result,
         AbstractModel $store
     ) {
-        $originStore = $store;
-        $result = $proceed($originStore);
         if ($store->isObjectNew() || $store->dataHasChangedFor('group_id')) {
             if (!$store->isObjectNew()) {
                 $this->urlPersist->deleteByData([UrlRewrite::STORE_ID => $store->getId()]);
@@ -102,6 +101,7 @@ class View
             ->addCategoryIds()
             ->addAttributeToSelect(['name', 'url_path', 'url_key', 'visibility'])
             ->addWebsiteFilter($websiteIds);
+
         foreach ($collection as $product) {
             $product->setStoreId($storeId);
             /** @var \Magento\Catalog\Model\Product $product */
@@ -110,6 +110,7 @@ class View
                 $this->productUrlRewriteGenerator->generate($product)
             );
         }
+
         return $urls;
     }
 
@@ -130,23 +131,26 @@ class View
                 $this->categoryUrlRewriteGenerator->generate($category)
             );
         }
+
         return $urls;
     }
 
     /**
-     * @param \Magento\Store\Model\ResourceModel\Store $object
-     * @param callable $proceed
+     * Delete unused url rewrites
+     *
+     * @param \Magento\Store\Model\ResourceModel\Store $subject
+     * @param \Magento\Store\Model\ResourceModel\Store $result
      * @param AbstractModel $store
-     * @return mixed
+     * @return \Magento\Store\Model\ResourceModel\Store
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDelete(
-        \Magento\Store\Model\ResourceModel\Store $object,
-        \Closure $proceed,
+    public function afterDelete(
+        \Magento\Store\Model\ResourceModel\Store $subject,
+        \Magento\Store\Model\ResourceModel\Store $result,
         AbstractModel $store
     ) {
-        $result = $proceed($store);
         $this->urlPersist->deleteByData([UrlRewrite::STORE_ID => $store->getId()]);
+
         return $result;
     }
 }
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Category/MoveTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Category/MoveTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7fe042232b82d85146365136f54b02f1a1842818
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Category/MoveTest.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogUrlRewrite\Test\Unit\Model\Category\Plugin\Category;
+
+use Magento\CatalogUrlRewrite\Model\Category\Plugin\Category\Move as CategoryMovePlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator;
+use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider;
+use Magento\Catalog\Model\ResourceModel\Category as CategoryResourceModel;
+use Magento\Catalog\Model\Category;
+
+class MoveTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var ChildrenCategoriesProvider|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $childrenCategoriesProviderMock;
+
+    /**
+     * @var CategoryUrlPathGenerator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryUrlPathGeneratorMock;
+
+    /**
+     * @var CategoryResourceModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var Category|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryMock;
+
+    /**
+     * @var CategoryMovePlugin
+     */
+    private $plugin;
+
+    protected function setUp()
+    {
+        $this->objectManager = new ObjectManager($this);
+        $this->categoryUrlPathGeneratorMock = $this->getMockBuilder(CategoryUrlPathGenerator::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getUrlPath'])
+            ->getMock();
+        $this->childrenCategoriesProviderMock = $this->getMockBuilder(ChildrenCategoriesProvider::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getChildren'])
+            ->getMock();
+        $this->subjectMock = $this->getMockBuilder(CategoryResourceModel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->categoryMock = $this->getMockBuilder(Category::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getResource', 'setUrlPath'])
+            ->getMock();
+        $this->plugin = $this->objectManager->getObject(
+            CategoryMovePlugin::class,
+            [
+                'categoryUrlPathGenerator' => $this->categoryUrlPathGeneratorMock,
+                'childrenCategoriesProvider' => $this->childrenCategoriesProviderMock
+            ]
+        );
+    }
+
+    public function testAfterChangeParent()
+    {
+        $urlPath = 'test/path';
+        $this->categoryMock->expects($this->once())
+            ->method('getResource')
+            ->willReturn($this->subjectMock);
+        $this->childrenCategoriesProviderMock->expects($this->once())
+            ->method('getChildren')
+            ->with($this->categoryMock, true)
+            ->willReturn([]);
+        $this->categoryUrlPathGeneratorMock->expects($this->once())
+            ->method('getUrlPath')
+            ->with($this->categoryMock)
+            ->willReturn($urlPath);
+        $this->categoryMock->expects($this->once())
+            ->method('getResource')
+            ->willReturn($this->subjectMock);
+        $this->categoryMock->expects($this->once())
+            ->method('setUrlPath')
+            ->with($urlPath);
+        $this->assertSame(
+            $this->subjectMock,
+            $this->plugin->afterChangeParent(
+                $this->subjectMock,
+                $this->subjectMock,
+                $this->categoryMock,
+                $this->categoryMock,
+                null
+            )
+        );
+    }
+}
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Category/RemoveTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Category/RemoveTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..520975871bf4b657a408b399c144b49353998959
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Category/RemoveTest.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogUrlRewrite\Test\Unit\Model\Category\Plugin\Category;
+
+use Magento\CatalogUrlRewrite\Model\Category\Plugin\Category\Remove as CategoryRemovePlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\UrlRewrite\Model\UrlPersistInterface;
+use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider;
+use Magento\Catalog\Model\ResourceModel\Category as CategoryResourceModel;
+use Magento\Catalog\Model\Category;
+
+class RemoveTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $urlPersistMock;
+
+    /**
+     * @var ChildrenCategoriesProvider|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $childrenCategoriesProviderMock;
+
+    /**
+     * @var CategoryResourceModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var Category|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $objectMock;
+
+    protected function setUp()
+    {
+        $this->objectManager = new ObjectManager($this);
+        $this->urlPersistMock = $this->getMockBuilder(UrlPersistInterface::class)
+            ->getMockForAbstractClass();
+        $this->childrenCategoriesProviderMock = $this->getMockBuilder(ChildrenCategoriesProvider::class)
+            ->getMock();
+        $this->subjectMock = $this->getMockBuilder(CategoryResourceModel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->objectMock = $this->getMockBuilder(Category::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+
+    public function testAroundDelete()
+    {
+        $closureSubject = $this->subjectMock;
+        $proceed  = function () use ($closureSubject) {
+            return $closureSubject;
+        };
+        $plugin = $this->objectManager->getObject(
+            CategoryRemovePlugin::class,
+            [
+                'urlPersist' => $this->urlPersistMock,
+                'childrenCategoriesProvider' => $this->childrenCategoriesProviderMock
+            ]
+        );
+        $this->childrenCategoriesProviderMock->expects($this->once())
+            ->method('getChildrenIds')
+            ->with($this->objectMock, true)
+            ->willReturn([]);
+        $this->objectMock->expects($this->once())
+            ->method('getId')
+            ->willReturn(1);
+        $this->urlPersistMock->expects($this->exactly(2))
+            ->method('deleteByData');
+        $this->assertSame(
+            $this->subjectMock,
+            $plugin->aroundDelete($this->subjectMock, $proceed, $this->objectMock)
+        );
+    }
+}
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/StorageTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/StorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..21ab961dcbdcb3b1c5eb08d5d59bd176b87c321a
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/StorageTest.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogUrlRewrite\Test\Unit\Model\Category\Plugin;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\CatalogUrlRewrite\Model\Category\ProductFactory;
+use Magento\UrlRewrite\Model\StorageInterface;
+use Magento\CatalogUrlRewrite\Model\Category\Plugin\Storage as CategoryStoragePlugin;
+use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
+use Magento\UrlRewrite\Model\UrlFinderInterface;
+use Magento\CatalogUrlRewrite\Model\Category\Product;
+use Magento\CatalogUrlRewrite\Model\ResourceModel\Category\Product as ProductResourceModel;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class StorageTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var CategoryStoragePlugin
+     */
+    private $plugin;
+
+    /**
+     * @var ProductFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productFactory;
+
+    /**
+     * @var UrlFinderInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $urlFinder;
+
+    /**
+     * @var StorageInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storage;
+
+    /**
+     * @var Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $product;
+
+    /**
+     * @var ProductResourceModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productResourceModel;
+
+    /**
+     * @var UrlRewrite|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $urlRewrite;
+
+    protected function setUp()
+    {
+        $this->productFactory = $this->getMockBuilder(ProductFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $this->storage = $this->getMockBuilder(StorageInterface::class)
+            ->getMockForAbstractClass();
+        $this->urlFinder = $this->getMockBuilder(UrlFinderInterface::class)
+            ->getMockForAbstractClass();
+        $this->product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->productResourceModel = $this->getMockBuilder(ProductResourceModel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->urlRewrite = $this->getMockBuilder(UrlRewrite::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getMetadata', 'getEntityType', 'getIsAutogenerated', 'getUrlRewriteId', 'getEntityId'])
+            ->getMock();
+
+        $this->plugin = (new ObjectManager($this))->getObject(
+            CategoryStoragePlugin::class,
+            [
+                'productFactory' => $this->productFactory,
+                'urlFinder' => $this->urlFinder
+            ]
+        );
+    }
+
+    public function testAfterReplace()
+    {
+        $this->urlRewrite->expects(static::any())->method('getMetadata')->willReturn(['category_id' => '5']);
+        $this->urlRewrite->expects(static::once())->method('getEntityTYpe')->willReturn('product');
+        $this->urlRewrite->expects(static::once())->method('getIsAutogenerated')->willReturn(1);
+        $this->urlRewrite->expects(static::once())->method('getUrlRewriteId')->willReturn('4');
+        $this->urlRewrite->expects(static::once())->method('getEntityId')->willReturn('2');
+        $this->urlRewrite->setData('request_path', 'test');
+        $this->urlRewrite->setData('store_id', '1');
+        $productUrls = ['targetPath' => $this->urlRewrite];
+
+        $this->urlFinder->expects(static::once())->method('findAllByData')->willReturn([$this->urlRewrite]);
+
+        $this->productFactory->expects(static::once())->method('create')->willReturn($this->product);
+        $this->product->expects(static::once())->method('getResource')->willReturn($this->productResourceModel);
+        $this->productResourceModel->expects(static::once())->method('saveMultiple')->willReturnSelf();
+
+        $this->plugin->afterReplace($this->storage, null, $productUrls);
+    }
+}
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Store/GroupTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Store/GroupTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..dc69597ef6db9ccd1299c6e7887239c890bb05e5
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Store/GroupTest.php
@@ -0,0 +1,178 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogUrlRewrite\Test\Unit\Model\Category\Plugin\Store;
+
+use Magento\CatalogUrlRewrite\Model\Category\Plugin\Store\Group as GroupPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Store\Model\StoreManagerInterface;
+use Magento\Store\Model\ResourceModel\Group;
+use Magento\Catalog\Model\Category;
+use Magento\Catalog\Model\CategoryFactory;
+use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;
+use Magento\Catalog\Model\Product as Product;
+use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator;
+use Magento\Catalog\Model\ProductFactory;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class GroupTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var GroupPlugin
+     */
+    private $plugin;
+
+    /**
+     * @var AbstractModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $abstractModelMock;
+
+    /**
+     * @var Group|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeManagerMock;
+
+    /**
+     * @var CategoryFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryFactoryMock;
+
+    /**
+     * @var Category|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryMock;
+
+    /**
+     * @var ProductCollection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productCollectionMock;
+
+    /**
+     * @var Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productMock;
+
+    /**
+     * @var ProductFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productFactoryMock;
+
+    /**
+     * @var ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productUrlRewriteGeneratorMock;
+
+    protected function setUp()
+    {
+        $this->objectManager = new ObjectManager($this);
+        $this->abstractModelMock = $this->getMockBuilder(AbstractModel::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['isObjectNew', 'dataHasChangedFor', 'getStoreIds'])
+            ->getMockForAbstractClass();
+        $this->abstractModelMock->expects($this->any())
+            ->method('getStoreIds')
+            ->willReturn([]);
+        $this->subjectMock = $this->getMockBuilder(Group::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['reinitStores'])
+            ->getMockForAbstractClass();
+        $this->categoryMock = $this->getMockBuilder(Category::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getCategories'])
+            ->getMock();
+        $this->categoryFactoryMock = $this->getMockBuilder(CategoryFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $this->productFactoryMock = $this->getMockBuilder(ProductFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $this->productCollectionMock = $this->getMockBuilder(ProductCollection::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['addCategoryIds', 'addAttributeToSelect', 'addWebsiteFilter', 'getIterator'])
+            ->getMock();
+        $this->productMock = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getCollection'])
+            ->getMock();
+        $this->productUrlRewriteGeneratorMock = $this->getMockBuilder(ProductUrlRewriteGenerator::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['generate'])
+            ->getMock();
+        $this->plugin = $this->objectManager->getObject(
+            GroupPlugin::class,
+            [
+                'storeManager' => $this->storeManagerMock,
+                'categoryFactory' => $this->categoryFactoryMock,
+                'productFactory' => $this->productFactoryMock,
+                'productUrlRewriteGenerator' => $this->productUrlRewriteGeneratorMock
+            ]
+        );
+    }
+
+    public function testAfterSave()
+    {
+        $this->abstractModelMock->expects($this->once())
+            ->method('isObjectNew')
+            ->willReturn(false);
+        $this->abstractModelMock->expects($this->once())
+            ->method('dataHasChangedFor')
+            ->with('website_id')
+            ->willReturn(true);
+        $this->storeManagerMock->expects($this->once())
+            ->method('reinitStores');
+        $this->categoryMock->expects($this->once())
+            ->method('getCategories')
+            ->willReturn([]);
+        $this->categoryFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($this->categoryMock);
+        $this->productFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getCollection')
+            ->willReturn($this->productCollectionMock);
+        $this->productCollectionMock->expects($this->once())
+            ->method('addCategoryIds')
+            ->willReturn($this->productCollectionMock);
+        $this->productCollectionMock->expects($this->once())
+            ->method('addAttributeToSelect')
+            ->willReturn($this->productCollectionMock);
+        $this->productCollectionMock->expects($this->once())
+            ->method('addWebsiteFilter')
+            ->willReturn($this->productCollectionMock);
+        $iterator = new \ArrayIterator([$this->productMock]);
+        $this->productCollectionMock->expects($this->once())
+            ->method('getIterator')
+            ->willReturn($iterator);
+        $this->productUrlRewriteGeneratorMock->expects($this->once())
+            ->method('generate')
+            ->with($this->productMock)
+            ->willReturn([]);
+
+        $this->assertSame(
+            $this->subjectMock,
+            $this->plugin->afterSave($this->subjectMock, $this->subjectMock, $this->abstractModelMock)
+        );
+    }
+}
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Store/ViewTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Store/ViewTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d30c2dde6e033fd3f3d10300ed59aa7a6c249916
--- /dev/null
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/Plugin/Store/ViewTest.php
@@ -0,0 +1,188 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogUrlRewrite\Test\Unit\Model\Category\Plugin\Store;
+
+use Magento\CatalogUrlRewrite\Model\Category\Plugin\Store\View as StoreViewPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Store\Model\ResourceModel\Store;
+use Magento\UrlRewrite\Model\UrlPersistInterface;
+use Magento\Catalog\Model\CategoryFactory;
+use Magento\Catalog\Model\ProductFactory;
+use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator;
+use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator;
+use Magento\Catalog\Model\Category;
+use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;
+use Magento\Catalog\Model\Product;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class ViewTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var StoreViewPlugin
+     */
+    private $plugin;
+
+    /**
+     * @var AbstractModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $abstractModelMock;
+
+    /**
+     * @var Store|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $urlPersistMock;
+
+    /**
+     * @var CategoryFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryFactoryMock;
+
+    /**
+     * @var ProductFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productFactoryMock;
+
+    /**
+     * @var CategoryUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryUrlRewriteGeneratorMock;
+
+    /**
+     * @var ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productUrlRewriteGeneratorMock;
+
+    /**
+     * @var Category|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryMock;
+
+    /**
+     * @var ProductCollection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productCollectionMock;
+
+    /**
+     * @var Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productMock;
+
+    protected function setUp()
+    {
+        $this->objectManager = new ObjectManager($this);
+        $this->abstractModelMock = $this->getMockBuilder(AbstractModel::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['isObjectNew'])
+            ->getMockForAbstractClass();
+        $this->subjectMock = $this->getMockBuilder(Store::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->urlPersistMock = $this->getMockBuilder(UrlPersistInterface::class)
+            ->setMethods(['deleteByData'])
+            ->getMockForAbstractClass();
+        $this->categoryMock = $this->getMockBuilder(Category::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getCategories'])
+            ->getMock();
+        $this->categoryFactoryMock = $this->getMockBuilder(CategoryFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $this->productFactoryMock = $this->getMockBuilder(ProductFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $this->categoryUrlRewriteGeneratorMock = $this->getMockBuilder(CategoryUrlRewriteGenerator::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->productUrlRewriteGeneratorMock = $this->getMockBuilder(ProductUrlRewriteGenerator::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['generate'])
+            ->getMock();
+        $this->productCollectionMock = $this->getMockBuilder(ProductCollection::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['addCategoryIds', 'addAttributeToSelect', 'addWebsiteFilter', 'getIterator'])
+            ->getMock();
+        $this->productMock = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getCollection'])
+            ->getMock();
+        $this->plugin = $this->objectManager->getObject(
+            StoreViewPlugin::class,
+            [
+                'urlPersist' => $this->urlPersistMock,
+                'categoryFactory' => $this->categoryFactoryMock,
+                'productFactory' => $this->productFactoryMock,
+                'categoryUrlRewriteGenerator' => $this->categoryUrlRewriteGeneratorMock,
+                'productUrlRewriteGenerator' => $this->productUrlRewriteGeneratorMock
+            ]
+        );
+    }
+
+    public function testAfterSave()
+    {
+        $this->abstractModelMock->expects($this->any())
+            ->method('isObjectNew')
+            ->willReturn(true);
+        $this->categoryMock->expects($this->once())
+            ->method('getCategories')
+            ->willReturn([]);
+        $this->categoryFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($this->categoryMock);
+        $this->productFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getCollection')
+            ->willReturn($this->productCollectionMock);
+        $this->productCollectionMock->expects($this->once())
+            ->method('addCategoryIds')
+            ->willReturn($this->productCollectionMock);
+        $this->productCollectionMock->expects($this->once())
+            ->method('addAttributeToSelect')
+            ->willReturn($this->productCollectionMock);
+        $this->productCollectionMock->expects($this->once())
+            ->method('addWebsiteFilter')
+            ->willReturn($this->productCollectionMock);
+        $iterator = new \ArrayIterator([$this->productMock]);
+        $this->productCollectionMock->expects($this->once())
+            ->method('getIterator')
+            ->willReturn($iterator);
+        $this->productUrlRewriteGeneratorMock->expects($this->once())
+            ->method('generate')
+            ->with($this->productMock)
+            ->willReturn([]);
+
+        $this->assertSame(
+            $this->subjectMock,
+            $this->plugin->afterSave($this->subjectMock, $this->subjectMock, $this->abstractModelMock)
+        );
+    }
+
+    public function testAfterDelete()
+    {
+        $this->urlPersistMock->expects($this->once())
+            ->method('deleteByData');
+        $this->assertSame(
+            $this->subjectMock,
+            $this->plugin->afterDelete($this->subjectMock, $this->subjectMock, $this->abstractModelMock)
+        );
+    }
+}
diff --git a/app/code/Magento/CmsUrlRewrite/Plugin/Cms/Model/ResourceModel/Page.php b/app/code/Magento/CmsUrlRewrite/Plugin/Cms/Model/ResourceModel/Page.php
index 45579f37b0c0cdeac82c14ff40f7e81eab227c9d..c605d3977d9f2a79f1f8d9d6d5b3cd04b241d14f 100644
--- a/app/code/Magento/CmsUrlRewrite/Plugin/Cms/Model/ResourceModel/Page.php
+++ b/app/code/Magento/CmsUrlRewrite/Plugin/Cms/Model/ResourceModel/Page.php
@@ -9,6 +9,8 @@ use Magento\UrlRewrite\Model\UrlPersistInterface;
 use Magento\CmsUrlRewrite\Model\CmsPageUrlPathGenerator;
 use Magento\CmsUrlRewrite\Model\CmsPageUrlRewriteGenerator;
 use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
 
 /**
  * Before save and around delete plugin for \Magento\Cms\Model\ResourceModel\Page:
@@ -63,18 +65,16 @@ class Page
      * On delete handler to remove related url rewrites
      *
      * @param \Magento\Cms\Model\ResourceModel\Page $subject
-     * @param \Closure $proceed
-     * @param \Magento\Framework\Model\AbstractModel $page
-     * @return \Magento\Cms\Model\ResourceModel\Page
-     *
+     * @param AbstractDb $result
+     * @param AbstractModel $page
+     * @return AbstractDb
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDelete(
+    public function afterDelete(
         \Magento\Cms\Model\ResourceModel\Page $subject,
-        \Closure $proceed,
-        \Magento\Framework\Model\AbstractModel $page
+        AbstractDb $result,
+        AbstractModel $page
     ) {
-        $result = $proceed($page);
         if ($page->isDeleted()) {
             $this->urlPersist->deleteByData(
                 [
diff --git a/app/code/Magento/CmsUrlRewrite/Test/Unit/Plugin/Cms/Model/ResourceModel/PageTest.php b/app/code/Magento/CmsUrlRewrite/Test/Unit/Plugin/Cms/Model/ResourceModel/PageTest.php
index 42b1b42469d5e0ee748a9795b8d3ac91ab54adc9..277e36b94fc8360c1a4bd4aec192e7cea94cc58d 100644
--- a/app/code/Magento/CmsUrlRewrite/Test/Unit/Plugin/Cms/Model/ResourceModel/PageTest.php
+++ b/app/code/Magento/CmsUrlRewrite/Test/Unit/Plugin/Cms/Model/ResourceModel/PageTest.php
@@ -30,19 +30,10 @@ class PageTest extends \PHPUnit_Framework_TestCase
      */
     protected $cmsPageResourceMock;
 
-    /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
     protected function setUp()
     {
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
 
-        $this->closureMock = function () {
-            return 'URL Rewrite Result';
-        };
-
         $this->urlPersistMock = $this->getMockBuilder(\Magento\UrlRewrite\Model\UrlPersistInterface::class)
             ->getMockForAbstractClass();
 
@@ -62,7 +53,7 @@ class PageTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    public function testAroundDeletePositive()
+    public function testAfterDeletePositive()
     {
         $productId = 100;
 
@@ -83,17 +74,17 @@ class PageTest extends \PHPUnit_Framework_TestCase
                 ]
             );
 
-        $this->assertEquals(
-            'URL Rewrite Result',
-            $this->pageObject->aroundDelete(
+        $this->assertSame(
+            $this->cmsPageResourceMock,
+            $this->pageObject->afterDelete(
+                $this->cmsPageResourceMock,
                 $this->cmsPageResourceMock,
-                $this->closureMock,
                 $this->cmsPageMock
             )
         );
     }
 
-    public function testAroundDeleteNegative()
+    public function testAfterDeleteNegative()
     {
         $this->cmsPageMock->expects($this->once())
             ->method('isDeleted')
@@ -102,11 +93,11 @@ class PageTest extends \PHPUnit_Framework_TestCase
         $this->urlPersistMock->expects($this->never())
             ->method('deleteByData');
 
-        $this->assertEquals(
-            'URL Rewrite Result',
-            $this->pageObject->aroundDelete(
+        $this->assertSame(
+            $this->cmsPageResourceMock,
+            $this->pageObject->afterDelete(
+                $this->cmsPageResourceMock,
                 $this->cmsPageResourceMock,
-                $this->closureMock,
                 $this->cmsPageMock
             )
         );
diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php
index ada1488e9cd9ce71e4b8b7b82afd795922845adb..354ebdb0ea7c94bcb639f0e4329d8bd2e6aa0ff7 100644
--- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php
+++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Builder/Plugin.php
@@ -8,6 +8,9 @@ namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Builder;
 
 use Magento\Catalog\Model\ProductFactory;
 use Magento\ConfigurableProduct\Model\Product\Type;
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Controller\Adminhtml\Product\Builder as CatalogProductBuilder;
+use Magento\Framework\App\RequestInterface;
 
 class Plugin
 {
@@ -32,21 +35,17 @@ class Plugin
     }
 
     /**
-     * @param \Magento\Catalog\Controller\Adminhtml\Product\Builder $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\App\RequestInterface $request
+     * Set type and data to configurable product
      *
-     * @return \Magento\Catalog\Model\Product
+     * @param CatalogProductBuilder $subject
+     * @param Product $product
+     * @param RequestInterface $request
+     * @return Product
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
-    public function aroundBuild(
-        \Magento\Catalog\Controller\Adminhtml\Product\Builder $subject,
-        \Closure $proceed,
-        \Magento\Framework\App\RequestInterface $request
-    ) {
-        $product = $proceed($request);
-
+    public function afterBuild(CatalogProductBuilder $subject, Product $product, RequestInterface $request)
+    {
         if ($request->has('attributes')) {
             $attributes = $request->getParam('attributes');
             if (!empty($attributes)) {
diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php
similarity index 93%
rename from app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php
rename to app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php
index be12fc8da2904165f22a16b5750e20ac8c3ef37d..cfcb5e41799e8fda8239c32f2a67dd3dc0ebcd04 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php
@@ -14,10 +14,7 @@ use Magento\ConfigurableProduct\Api\Data\OptionInterface;
 use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
 use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
 
-/**
- * Class AroundProductRepositorySave
- */
-class AroundProductRepositorySave
+class ProductRepositorySave
 {
     /**
      * @var ProductAttributeRepositoryInterface
@@ -42,8 +39,10 @@ class AroundProductRepositorySave
     }
 
     /**
+     * Validate product links and reset configurable attributes to configurable product
+     *
      * @param ProductRepositoryInterface $subject
-     * @param callable $proceed
+     * @param ProductInterface $result
      * @param ProductInterface $product
      * @param bool $saveOptions
      * @return ProductInterface
@@ -52,14 +51,12 @@ class AroundProductRepositorySave
      *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSave(
+    public function afterSave(
         ProductRepositoryInterface $subject,
-        \Closure $proceed,
+        ProductInterface $result,
         ProductInterface $product,
         $saveOptions = false
     ) {
-        /** @var ProductInterface $result */
-        $result = $proceed($product, $saveOptions);
         if ($product->getTypeId() !== Configurable::TYPE_CODE) {
             return $result;
         }
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Validator/Plugin.php b/app/code/Magento/ConfigurableProduct/Model/Product/Validator/Plugin.php
index c0b287214ea3d32ea80beebe2dbe4cff865812d8..b5970c902743ee8c5959dca2a2922502c169a2d2 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Validator/Plugin.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Validator/Plugin.php
@@ -5,12 +5,12 @@
  */
 namespace Magento\ConfigurableProduct\Model\Product\Validator;
 
-use Closure;
 use Magento\Catalog\Model\Product;
 use Magento\Catalog\Model\ProductFactory;
 use Magento\Framework\App\RequestInterface;
 use Magento\Framework\Event\Manager;
 use Magento\Framework\Json\Helper\Data;
+use Magento\Framework\DataObject;
 
 /**
  * Configurable product validation
@@ -48,27 +48,44 @@ class Plugin
     }
 
     /**
-     * Validate product data
+     * Set configurable type to product
      *
      * @param Product\Validator $subject
-     * @param Closure $proceed
      * @param Product $product
      * @param RequestInterface $request
-     * @param \Magento\Framework\DataObject $response
-     * @return bool
+     * @param DataObject $response
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundValidate(
+    public function beforeValidate(
         \Magento\Catalog\Model\Product\Validator $subject,
-        Closure $proceed,
         \Magento\Catalog\Model\Product $product,
-        \Magento\Framework\App\RequestInterface $request,
-        \Magento\Framework\DataObject $response
+        RequestInterface $request,
+        DataObject $response
     ) {
         if ($request->has('attributes')) {
             $product->setTypeId(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
         }
-        $result = $proceed($product, $request, $response);
+    }
+
+    /**
+     * Validate product data
+     *
+     * @param Product\Validator $subject
+     * @param bool|array $result
+     * @param Product $product
+     * @param RequestInterface $request
+     * @param \Magento\Framework\DataObject $response
+     * @return bool
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterValidate(
+        \Magento\Catalog\Model\Product\Validator $subject,
+        $result,
+        \Magento\Catalog\Model\Product $product,
+        RequestInterface $request,
+        DataObject $response
+    ) {
         $variationProducts = (array)$request->getPost('variations-matrix');
         if ($variationProducts) {
             $validationResult = $this->_validateProductVariations($product, $variationProducts, $request);
diff --git a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/QuantityValidator/Initializer/Option/Plugin/ConfigurableProduct.php b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/QuantityValidator/Initializer/Option/Plugin/ConfigurableProduct.php
index 73e88eb5333cb4ef62eef43e2aae5d02d9e53294..1657fb8aaae12788c217cd124a25a46d97ea3b0b 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/QuantityValidator/Initializer/Option/Plugin/ConfigurableProduct.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/QuantityValidator/Initializer/Option/Plugin/ConfigurableProduct.php
@@ -13,20 +13,19 @@ class ConfigurableProduct
      * Initialize stock item for configurable product type
      *
      * @param \Magento\CatalogInventory\Model\Quote\Item\QuantityValidator\Initializer\Option $subject
-     * @param callable $proceed
+     * @param \Magento\CatalogInventory\Model\Stock\Item $stockItem
      * @param \Magento\Quote\Model\Quote\Item\Option $option
      * @param \Magento\Quote\Model\Quote\Item $quoteItem
      *
      * @return \Magento\CatalogInventory\Api\Data\StockItemInterface
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundGetStockItem(
+    public function afterGetStockItem(
         \Magento\CatalogInventory\Model\Quote\Item\QuantityValidator\Initializer\Option $subject,
-        \Closure $proceed,
+        \Magento\CatalogInventory\Model\Stock\Item $stockItem,
         \Magento\Quote\Model\Quote\Item\Option $option,
         \Magento\Quote\Model\Quote\Item $quoteItem
     ) {
-        $stockItem = $proceed($option, $quoteItem);
         if ($quoteItem->getProductType() == \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) {
             $stockItem->setProductName($quoteItem->getName());
         }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php
index 7056224cffd6843ea3dd35729cd5add8c01dc6ea..e6c13a23541fad21e5d592d4b0b9dc7bb32e7ed0 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Builder/PluginTest.php
@@ -23,12 +23,12 @@ class PluginTest extends \PHPUnit_Framework_TestCase
     protected $configurableTypeMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $requestMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $productMock;
 
@@ -48,15 +48,10 @@ class PluginTest extends \PHPUnit_Framework_TestCase
     protected $frontendAttrMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Catalog\Controller\Adminhtml\Product\Builder|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $subjectMock;
 
-    /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
     protected function setUp()
     {
         $this->productFactoryMock = $this->getMock(
@@ -76,7 +71,6 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         $this->requestMock = $this->getMock(\Magento\Framework\App\Request\Http::class, [], [], '', false);
         $methods = ['setTypeId', 'getAttributes', 'addData', 'setWebsiteIds', '__wakeup'];
         $this->productMock = $this->getMock(\Magento\Catalog\Model\Product::class, $methods, [], '', false);
-        $product = $this->productMock;
         $attributeMethods = [
             'getId',
             'getFrontend',
@@ -124,9 +118,6 @@ class PluginTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $this->closureMock = function () use ($product) {
-            return $product;
-        };
         $this->plugin = new \Magento\ConfigurableProduct\Controller\Adminhtml\Product\Builder\Plugin(
             $this->productFactoryMock,
             $this->configurableTypeMock
@@ -137,7 +128,7 @@ class PluginTest extends \PHPUnit_Framework_TestCase
      * @return void
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
-    public function testAroundBuild()
+    public function testAfterBuild()
     {
         $this->requestMock->expects($this->once())->method('has')->with('attributes')->will($this->returnValue(true));
         $valueMap = [
@@ -256,11 +247,11 @@ class PluginTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             $this->productMock,
-            $this->plugin->aroundBuild($this->subjectMock, $this->closureMock, $this->requestMock)
+            $this->plugin->afterBuild($this->subjectMock, $this->productMock, $this->requestMock)
         );
     }
 
-    public function testAroundBuildWhenProductNotHaveAttributeAndRequiredParameters()
+    public function testAfterBuildWhenProductNotHaveAttributeAndRequiredParameters()
     {
         $valueMap = [
             ['attributes', null, null],
@@ -283,11 +274,11 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         $this->attributeMock->expects($this->never())->method('getAttributeCode');
         $this->assertEquals(
             $this->productMock,
-            $this->plugin->aroundBuild($this->subjectMock, $this->closureMock, $this->requestMock)
+            $this->plugin->afterBuild($this->subjectMock, $this->productMock, $this->requestMock)
         );
     }
 
-    public function testAroundBuildWhenAttributesAreEmpty()
+    public function testAfterBuildWhenAttributesAreEmpty()
     {
         $valueMap = [['popup', null, false], ['product', null, 'product'], ['id', false, false]];
         $this->requestMock->expects($this->once())->method('has')->with('attributes')->will($this->returnValue(false));
@@ -299,7 +290,7 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         $this->attributeMock->expects($this->never())->method('getAttributeCode');
         $this->assertEquals(
             $this->productMock,
-            $this->plugin->aroundBuild($this->subjectMock, $this->closureMock, $this->requestMock)
+            $this->plugin->afterBuild($this->subjectMock, $this->productMock, $this->requestMock)
         );
     }
 }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php
similarity index 87%
rename from app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
rename to app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php
index dee340fca9d8688aee08904078039ad3f82b4849..3b2069296db9a4e20ae75c010fdca7000e53e403 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php
@@ -11,16 +11,17 @@ use Magento\Catalog\Api\ProductRepositoryInterface;
 use Magento\Catalog\Model\Product;
 use Magento\Catalog\Model\ProductFactory;
 use Magento\ConfigurableProduct\Api\Data\OptionInterface;
-use Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave;
+use Magento\ConfigurableProduct\Model\Plugin\ProductRepositorySave;
 use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
 use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 use PHPUnit_Framework_MockObject_MockObject as MockObject;
 
 /**
- * Class AroundProductRepositorySaveTest
+ * Class ProductRepositorySaveTest
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
-class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
+class ProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * @var ProductAttributeRepositoryInterface|MockObject
@@ -32,11 +33,6 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
      */
     private $productFactory;
 
-    /**
-     * @var \Closure
-     */
-    private $closure;
-
     /**
      * @var Product|MockObject
      */
@@ -68,7 +64,7 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
     private $option;
 
     /**
-     * @var AroundProductRepositorySave
+     * @var ProductRepositorySave
      */
     private $plugin;
 
@@ -91,10 +87,6 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
             ->setMethods(['getExtensionAttributes'])
             ->getMock();
 
-        $this->closure = function () {
-            return $this->result;
-        };
-
         $this->productRepository = $this->getMockForAbstractClass(ProductRepositoryInterface::class);
 
         $this->extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
@@ -106,13 +98,16 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
 
         $this->option = $this->getMockForAbstractClass(OptionInterface::class);
 
-        $this->plugin = new AroundProductRepositorySave(
-            $this->productAttributeRepository,
-            $this->productFactory
+        $this->plugin = (new ObjectManager($this))->getObject(
+            ProductRepositorySave::class,
+            [
+                'productAttributeRepository' => $this->productAttributeRepository,
+                'productFactory' => $this->productFactory
+            ]
         );
     }
 
-    public function testAroundSaveWhenProductIsSimple()
+    public function testAfterSaveWhenProductIsSimple()
     {
         $this->product->expects(static::once())
             ->method('getTypeId')
@@ -122,11 +117,11 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             $this->result,
-            $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product)
+            $this->plugin->afterSave($this->productRepository, $this->result, $this->product)
         );
     }
 
-    public function testAroundSaveWithoutOptions()
+    public function testAfterSaveWithoutOptions()
     {
         $this->product->expects(static::once())
             ->method('getTypeId')
@@ -148,7 +143,7 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             $this->result,
-            $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product)
+            $this->plugin->afterSave($this->productRepository, $this->result, $this->product)
         );
     }
 
@@ -156,7 +151,7 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
      * @expectedException \Magento\Framework\Exception\InputException
      * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values.
      */
-    public function testAroundSaveWithLinks()
+    public function testAfterSaveWithLinks()
     {
         $links = [4, 5];
         $this->product->expects(static::once())
@@ -191,14 +186,14 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
         $product->expects(static::never())
             ->method('getData');
 
-        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
+        $this->plugin->afterSave($this->productRepository, $this->result, $this->product);
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\InputException
      * @expectedExceptionMessage Product with id "4" does not contain required attribute "color".
      */
-    public function testAroundSaveWithLinksWithMissingAttribute()
+    public function testAfterSaveWithLinksWithMissingAttribute()
     {
         $simpleProductId = 4;
         $links = [$simpleProductId, 5];
@@ -248,14 +243,14 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
             ->with($attributeCode)
             ->willReturn(false);
 
-        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
+        $this->plugin->afterSave($this->productRepository, $this->result, $this->product);
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\InputException
      * @expectedExceptionMessage Products "5" and "4" have the same set of attribute values.
      */
-    public function testAroundSaveWithLinksWithDuplicateAttributes()
+    public function testAfterSaveWithLinksWithDuplicateAttributes()
     {
         $links = [4, 5];
         $attributeCode = 'color';
@@ -303,6 +298,6 @@ class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
             ->with($attributeCode)
             ->willReturn($attributeId);
 
-        $this->plugin->aroundSave($this->productRepository, $this->closure, $this->product);
+        $this->plugin->afterSave($this->productRepository, $this->result, $this->product);
     }
 }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Validator/PluginTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Validator/PluginTest.php
index d66e32aa0e7b04ad4c39d0e767182656dd9ff75b..65d3f42767de5e136ac33f9c95fe702619c16613 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Validator/PluginTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Validator/PluginTest.php
@@ -28,17 +28,17 @@ class PluginTest extends \PHPUnit_Framework_TestCase
     protected $jsonHelperMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $productMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $requestMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $responseMock;
 
@@ -53,15 +53,10 @@ class PluginTest extends \PHPUnit_Framework_TestCase
     protected $proceedResult = [1, 2, 3];
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Catalog\Model\Product\Validator|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $subjectMock;
 
-    /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
     protected function setUp()
     {
         $this->eventManagerMock = $this->getMock(\Magento\Framework\Event\Manager::class, [], [], '', false);
@@ -82,14 +77,14 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         $this->jsonHelperMock->expects($this->any())->method('jsonDecode')->will($this->returnArgument(0));
         $this->productMock = $this->getMock(
             \Magento\Catalog\Model\Product::class,
-            ['getData', 'getAttributes'],
+            ['getData', 'getAttributes', 'setTypeId'],
             [],
             '',
             false
         );
         $this->requestMock = $this->getMock(
             \Magento\Framework\App\Request\Http::class,
-            ['getPost', 'getParam', '__wakeup'],
+            ['getPost', 'getParam', '__wakeup', 'has'],
             [],
             '',
             false
@@ -102,10 +97,7 @@ class PluginTest extends \PHPUnit_Framework_TestCase
             false
         );
         $this->arguments = [$this->productMock, $this->requestMock, $this->responseMock];
-        $proceedResult = $this->proceedResult;
-        $this->closureMock = function () use ($proceedResult) {
-            return $proceedResult;
-        };
+
         $this->subjectMock = $this->getMock(\Magento\Catalog\Model\Product\Validator::class, [], [], '', false);
         $this->plugin = new \Magento\ConfigurableProduct\Model\Product\Validator\Plugin(
             $this->eventManagerMock,
@@ -114,7 +106,20 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    public function testAroundValidateWithVariationsValid()
+    public function testBeforeValidate()
+    {
+        $this->requestMock->expects(static::once())->method('has')->with('attributes')->willReturn(true);
+        $this->productMock->expects(static::once())->method('setTypeId')->willReturnSelf();
+
+        $this->plugin->beforeValidate(
+            $this->subjectMock,
+            $this->productMock,
+            $this->requestMock,
+            $this->responseMock
+        );
+    }
+
+    public function testAfterValidateWithVariationsValid()
     {
         $matrix = ['products'];
 
@@ -150,9 +155,9 @@ class PluginTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             $this->proceedResult,
-            $plugin->aroundValidate(
+            $plugin->afterValidate(
                 $this->subjectMock,
-                $this->closureMock,
+                $this->proceedResult,
                 $this->productMock,
                 $this->requestMock,
                 $this->responseMock
@@ -160,7 +165,7 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    public function testAroundValidateWithVariationsInvalid()
+    public function testAfterValidateWithVariationsInvalid()
     {
         $matrix = ['products'];
 
@@ -197,9 +202,9 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         $this->responseMock->expects($this->once())->method('setAttributes')->will($this->returnSelf());
         $this->assertEquals(
             $this->proceedResult,
-            $plugin->aroundValidate(
+            $plugin->afterValidate(
                 $this->subjectMock,
-                $this->closureMock,
+                $this->proceedResult,
                 $this->productMock,
                 $this->requestMock,
                 $this->responseMock
@@ -207,7 +212,7 @@ class PluginTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    public function testAroundValidateIfVariationsNotExist()
+    public function testAfterValidateIfVariationsNotExist()
     {
         $this->requestMock->expects(
             $this->once()
@@ -219,16 +224,16 @@ class PluginTest extends \PHPUnit_Framework_TestCase
             $this->returnValue(null)
         );
         $this->eventManagerMock->expects($this->never())->method('dispatch');
-        $this->plugin->aroundValidate(
+        $this->plugin->afterValidate(
             $this->subjectMock,
-            $this->closureMock,
+            $this->proceedResult,
             $this->productMock,
             $this->requestMock,
             $this->responseMock
         );
     }
 
-    public function testAroundValidateWithVariationsAndRequiredAttributes()
+    public function testAfterValidateWithVariationsAndRequiredAttributes()
     {
         $matrix = [
             ['data1', 'data2', 'configurable_attribute' => ['data1']],
@@ -313,9 +318,9 @@ class PluginTest extends \PHPUnit_Framework_TestCase
 
         $this->responseMock->expects($this->never())->method('setError');
 
-        $result = $this->plugin->aroundValidate(
+        $result = $this->plugin->afterValidate(
             $this->subjectMock,
-            $this->closureMock,
+            $this->proceedResult,
             $this->productMock,
             $this->requestMock,
             $this->responseMock
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/Option/Plugin/ConfigurableProductTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/Option/Plugin/ConfigurableProductTest.php
index 7d10cb4511e13e60ff0c4d75cbf844d02c5d42ab..6b162bbde85c4ca46e2bdf27c4328b7b2deef144 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/Option/Plugin/ConfigurableProductTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/Option/Plugin/ConfigurableProductTest.php
@@ -3,20 +3,20 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
-// @codingStandardsIgnoreFile
-
 namespace Magento\ConfigurableProduct\Test\Unit\Model\Quote\Item\QuantityValidator\Initializer\Option\Plugin;
 
+use \Magento\ConfigurableProduct\Model\Quote\Item\QuantityValidator\Initializer\Option\Plugin\ConfigurableProduct
+    as InitializerOptionPlugin;
+
 class ConfigurableProductTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * @param array $data
-     * @dataProvider aroundGetStockItemDataProvider
+     * @dataProvider afterGetStockItemDataProvider
      */
-    public function testAroundGetStockItem(array $data)
+    public function testAfterGetStockItem(array $data)
     {
-        $subjectMock = $this->getMock(
+        $subjectMock = $this->getMock(
             \Magento\CatalogInventory\Model\Quote\Item\QuantityValidator\Initializer\Option::class,
             [],
             [],
@@ -24,36 +24,44 @@ class ConfigurableProductTest extends \PHPUnit_Framework_TestCase
             false
         );
 
-        $quoteItemMock = $this->getMock(
-            \Magento\Quote\Model\Quote\Item::class, ['getProductType', '__wakeup'], [], '', false
+        $quoteItemMock = $this->getMock(
+            \Magento\Quote\Model\Quote\Item::class,
+            ['getProductType', '__wakeup'],
+            [],
+            '',
+            false
         );
         $quoteItemMock->expects($this->once())
             ->method('getProductType')
             ->will($this->returnValue($data['product_type']));
 
-        $stockItemMock = $this->getMock(
-            \Magento\CatalogInventory\Model\Stock\Item::class, ['setProductName', '__wakeup'], [], '', false
+        $stockItemMock = $this->getMock(
+            \Magento\CatalogInventory\Model\Stock\Item::class,
+            ['setProductName', '__wakeup'],
+            [],
+            '',
+            false
         );
         $matcherMethod = $data['matcher_method'];
         $stockItemMock->expects($this->$matcherMethod())
             ->method('setProductName');
 
-        $optionMock = $this->getMock(
-            \Magento\Quote\Model\Quote\Item\Option::class, ['getProduct', '__wakeup'], [], '', false
+        $optionMock = $this->getMock(
+            \Magento\Quote\Model\Quote\Item\Option::class,
+            ['getProduct', '__wakeup'],
+            [],
+            '',
+            false
         );
 
-        $proceed = function () use ($stockItemMock) {
-            return $stockItemMock;
-        };
-
-        $model = new \Magento\ConfigurableProduct\Model\Quote\Item\QuantityValidator\Initializer\Option\Plugin\ConfigurableProduct();
-        $model->aroundGetStockItem($subjectMock, $proceed, $optionMock, $quoteItemMock, 0);
+        $model = new InitializerOptionPlugin();
+        $model->afterGetStockItem($subjectMock, $stockItemMock, $optionMock, $quoteItemMock, 0);
     }
 
     /**
      * @return array
      */
-    public function aroundGetStockItemDataProvider()
+    public function afterGetStockItemDataProvider()
     {
         return [
             [
diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml
index e32286764f56ec959044cb11a5e65c13928f4d7c..c3be4aaea189a63edb9091df2712d063eb8e09e1 100644
--- a/app/code/Magento/ConfigurableProduct/etc/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/di.xml
@@ -59,7 +59,7 @@
         </arguments>
     </type>
     <type name="Magento\Catalog\Api\ProductRepositoryInterface">
-        <plugin name="configurableProductSaveOptions" type="\Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave"/>
+        <plugin name="configurableProductSaveOptions" sortOrder="10" type="Magento\ConfigurableProduct\Model\Plugin\ProductRepositorySave"/>
     </type>
     <type name="Magento\Catalog\Model\Product">
         <plugin name="configurable_identity" type="Magento\ConfigurableProduct\Plugin\Model\Product" />
diff --git a/app/code/Magento/Customer/Controller/Plugin/Account.php b/app/code/Magento/Customer/Controller/Plugin/Account.php
index 0a19f87b5050c4b188be70f2f0f23850bc1bf61e..5da79b8aa46a21cb5847f1eb916994938e4698d4 100644
--- a/app/code/Magento/Customer/Controller/Plugin/Account.php
+++ b/app/code/Magento/Customer/Controller/Plugin/Account.php
@@ -8,6 +8,9 @@ namespace Magento\Customer\Controller\Plugin;
 use Magento\Customer\Model\Session;
 use Magento\Framework\App\ActionInterface;
 use Magento\Framework\App\RequestInterface;
+use Magento\Framework\App\ResponseInterface;
+use Magento\Framework\App\Action\AbstractAction;
+use Magento\Framework\Controller\ResultInterface;
 
 class Account
 {
@@ -36,16 +39,12 @@ class Account
     /**
      * Dispatch actions allowed for not authorized users
      *
-     * @param ActionInterface $subject
-     * @param \Closure $proceed
+     * @param AbstractAction $subject
      * @param RequestInterface $request
-     * @return mixed
+     * @return void
      */
-    public function aroundDispatch(
-        ActionInterface $subject,
-        \Closure $proceed,
-        RequestInterface $request
-    ) {
+    public function beforeDispatch(AbstractAction $subject, RequestInterface $request)
+    {
         $action = strtolower($request->getActionName());
         $pattern = '/^(' . implode('|', $this->allowedActions) . ')$/i';
 
@@ -56,8 +55,19 @@ class Account
         } else {
             $this->session->setNoReferer(true);
         }
+    }
 
-        $result = $proceed($request);
+    /**
+     * Remove No-referer flag from customer session
+     *
+     * @param AbstractAction $subject
+     * @param ResponseInterface|ResultInterface $result
+     * @param RequestInterface $request
+     * @return ResponseInterface|ResultInterface
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function afterDispatch(AbstractAction $subject, $result, RequestInterface $request)
+    {
         $this->session->unsNoReferer(false);
         return $result;
     }
diff --git a/app/code/Magento/Customer/Model/App/Action/ContextPlugin.php b/app/code/Magento/Customer/Model/App/Action/ContextPlugin.php
index f102664df537b846aa5376f198a04f8b06a2d5fe..7518c4b4783a04a488256d15fd6d107b13da2cc9 100644
--- a/app/code/Magento/Customer/Model/App/Action/ContextPlugin.php
+++ b/app/code/Magento/Customer/Model/App/Action/ContextPlugin.php
@@ -8,6 +8,10 @@ namespace Magento\Customer\Model\App\Action;
 
 use Magento\Customer\Model\Context;
 use Magento\Customer\Model\GroupManagement;
+use Magento\Framework\App\Action\AbstractAction;
+use Magento\Framework\App\RequestInterface;
+use Magento\Customer\Model\Session;
+use Magento\Framework\App\Http\Context as HttpContext;
 
 /**
  * Class ContextPlugin
@@ -15,39 +19,35 @@ use Magento\Customer\Model\GroupManagement;
 class ContextPlugin
 {
     /**
-     * @var \Magento\Customer\Model\Session
+     * @var Session
      */
     protected $customerSession;
 
     /**
-     * @var \Magento\Framework\App\Http\Context
+     * @var HttpContext
      */
     protected $httpContext;
 
     /**
-     * @param \Magento\Customer\Model\Session $customerSession
-     * @param \Magento\Framework\App\Http\Context $httpContext
+     * @param Session $customerSession
+     * @param HttpContext $httpContext
      */
-    public function __construct(
-        \Magento\Customer\Model\Session $customerSession,
-        \Magento\Framework\App\Http\Context $httpContext
-    ) {
+    public function __construct(Session $customerSession, HttpContext $httpContext)
+    {
         $this->customerSession = $customerSession;
         $this->httpContext = $httpContext;
     }
 
     /**
-     * @param \Magento\Framework\App\ActionInterface $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\App\RequestInterface $request
-     * @return mixed
+     * Set customer group and customer session id to HTTP context
+     *
+     * @param AbstractAction $subject
+     * @param RequestInterface $request
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDispatch(
-        \Magento\Framework\App\ActionInterface $subject,
-        \Closure $proceed,
-        \Magento\Framework\App\RequestInterface $request
-    ) {
+    public function beforeDispatch(AbstractAction $subject, RequestInterface $request)
+    {
         $this->httpContext->setValue(
             Context::CONTEXT_GROUP,
             $this->customerSession->getCustomerGroupId(),
@@ -58,6 +58,5 @@ class ContextPlugin
             $this->customerSession->isLoggedIn(),
             false
         );
-        return $proceed($request);
     }
 }
diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php
index b175e55f1d3be90b4b6b886c3db22eb361e8e904..b9c05d4005be5e6664f9a36b76db7814140a55b8 100644
--- a/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Controller/Plugin/AccountTest.php
@@ -9,7 +9,10 @@ use Magento\Customer\Controller\Plugin\Account;
 use Magento\Customer\Model\Session;
 use Magento\Framework\App\ActionFlag;
 use Magento\Framework\App\ActionInterface;
+use Magento\Framework\App\Action\AbstractAction;
 use Magento\Framework\App\Request\Http;
+use Magento\Framework\Controller\ResultInterface;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 class AccountTest extends \PHPUnit_Framework_TestCase
 {
@@ -29,12 +32,7 @@ class AccountTest extends \PHPUnit_Framework_TestCase
     protected $session;
 
     /**
-     * @var \Closure
-     */
-    protected $proceed;
-
-    /**
-     * @var ActionInterface | \PHPUnit_Framework_MockObject_MockObject
+     * @var AbstractAction | \PHPUnit_Framework_MockObject_MockObject
      */
     protected $subject;
 
@@ -48,6 +46,11 @@ class AccountTest extends \PHPUnit_Framework_TestCase
      */
     protected $actionFlag;
 
+    /**
+     * @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $resultInterface;
+
     protected function setUp()
     {
         $this->session = $this->getMockBuilder(\Magento\Customer\Model\Session::class)
@@ -59,16 +62,13 @@ class AccountTest extends \PHPUnit_Framework_TestCase
             ])
             ->getMock();
 
-        $this->subject = $this->getMockBuilder(\Magento\Framework\App\ActionInterface::class)
+        $this->subject = $this->getMockBuilder(AbstractAction::class)
             ->setMethods([
                 'getActionFlag',
             ])
+            ->disableOriginalConstructor()
             ->getMockForAbstractClass();
 
-        $this->proceed = function () {
-            return self::EXPECTED_VALUE;
-        };
-
         $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class)
             ->disableOriginalConstructor()
             ->setMethods([
@@ -76,6 +76,9 @@ class AccountTest extends \PHPUnit_Framework_TestCase
             ])
             ->getMock();
 
+        $this->resultInterface = $this->getMockBuilder(ResultInterface::class)
+            ->getMockForAbstractClass();
+
         $this->actionFlag = $this->getMockBuilder(\Magento\Framework\App\ActionFlag::class)
             ->disableOriginalConstructor()
             ->getMock();
@@ -87,9 +90,9 @@ class AccountTest extends \PHPUnit_Framework_TestCase
      * @param boolean $isActionAllowed
      * @param boolean $isAuthenticated
      *
-     * @dataProvider dataProviderAroundDispatch
+     * @dataProvider beforeDispatchDataProvider
      */
-    public function testAroundDispatch(
+    public function testBeforeDispatch(
         $action,
         $allowedActions,
         $isActionAllowed,
@@ -99,11 +102,6 @@ class AccountTest extends \PHPUnit_Framework_TestCase
             ->method('getActionName')
             ->willReturn($action);
 
-        $this->session->expects($this->once())
-            ->method('unsNoReferer')
-            ->with(false)
-            ->willReturnSelf();
-
         if ($isActionAllowed) {
             $this->session->expects($this->once())
                 ->method('setNoReferer')
@@ -126,13 +124,13 @@ class AccountTest extends \PHPUnit_Framework_TestCase
         }
 
         $plugin = new Account($this->session, $allowedActions);
-        $this->assertEquals(
-            self::EXPECTED_VALUE,
-            $plugin->aroundDispatch($this->subject, $this->proceed, $this->request)
-        );
+        $plugin->beforeDispatch($this->subject, $this->request);
     }
 
-    public function dataProviderAroundDispatch()
+    /**
+     * @return array
+     */
+    public function beforeDispatchDataProvider()
     {
         return [
             [
@@ -167,4 +165,24 @@ class AccountTest extends \PHPUnit_Framework_TestCase
             ],
         ];
     }
+
+    public function testAfterDispatch()
+    {
+        $this->session->expects($this->once())
+            ->method('unsNoReferer')
+            ->with(false)
+            ->willReturnSelf();
+
+        $plugin = (new ObjectManager($this))->getObject(
+            Account::class,
+            [
+                'session' => $this->session,
+                'allowedActions' => ['testaction']
+            ]
+        );
+        $this->assertSame(
+            $this->resultInterface,
+            $plugin->afterDispatch($this->subject, $this->resultInterface, $this->request)
+        );
+    }
 }
diff --git a/app/code/Magento/Customer/Test/Unit/Model/App/Action/ContextPluginTest.php b/app/code/Magento/Customer/Test/Unit/Model/App/Action/ContextPluginTest.php
index c5ab448f976474dac910b7d090fe84a448f385a4..985e2ecb7efd3a25c333dc9ecd24fe7e9b1dc11d 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/App/Action/ContextPluginTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/App/Action/ContextPluginTest.php
@@ -28,11 +28,6 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
      */
     protected $httpContextMock;
 
-    /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject
      */
@@ -62,9 +57,6 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $this->closureMock = function () {
-            return 'ExpectedValue';
-        };
         $this->subjectMock = $this->getMock(\Magento\Framework\App\Action\Action::class, [], [], '', false);
         $this->requestMock = $this->getMock(\Magento\Framework\App\RequestInterface::class);
         $this->plugin = new \Magento\Customer\Model\App\Action\ContextPlugin(
@@ -76,7 +68,7 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
     /**
      * Test aroundDispatch
      */
-    public function testAroundDispatch()
+    public function testBeforeDispatch()
     {
         $this->customerSessionMock->expects($this->once())
             ->method('getCustomerGroupId')
@@ -94,9 +86,6 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
                     ]
                 )
             );
-        $this->assertEquals(
-            'ExpectedValue',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 }
diff --git a/app/code/Magento/GiftMessage/Block/Message/Multishipping/Plugin/ItemsBox.php b/app/code/Magento/GiftMessage/Block/Message/Multishipping/Plugin/ItemsBox.php
index e79052a1fb3e3826977f625e167384bd8127fdb7..acc5fb484ada7a2ccd76b1d426b9d1eed5a398ae 100644
--- a/app/code/Magento/GiftMessage/Block/Message/Multishipping/Plugin/ItemsBox.php
+++ b/app/code/Magento/GiftMessage/Block/Message/Multishipping/Plugin/ItemsBox.php
@@ -5,6 +5,10 @@
  */
 namespace Magento\GiftMessage\Block\Message\Multishipping\Plugin;
 
+use Magento\Multishipping\Block\Checkout\Shipping as ShippingBlock;
+use Magento\GiftMessage\Helper\Message as MessageHelper;
+use Magento\Framework\DataObject;
+
 /**
  * Multishipping items box plugin
  */
@@ -13,16 +17,16 @@ class ItemsBox
     /**
      * Gift message helper
      *
-     * @var \Magento\GiftMessage\Helper\Message
+     * @var MessageHelper
      */
     protected $helper;
 
     /**
      * Construct
      *
-     * @param \Magento\GiftMessage\Helper\Message $helper
+     * @param MessageHelper $helper
      */
-    public function __construct(\Magento\GiftMessage\Helper\Message $helper)
+    public function __construct(MessageHelper $helper)
     {
         $this->helper = $helper;
     }
@@ -30,19 +34,15 @@ class ItemsBox
     /**
      * Get items box message text for multishipping
      *
-     * @param \Magento\Multishipping\Block\Checkout\Shipping $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\DataObject $addressEntity
+     * @param ShippingBlock $subject
+     * @param string $itemsBoxText
+     * @param DataObject $addressEntity
      *
      * @return string
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundGetItemsBoxTextAfter(
-        \Magento\Multishipping\Block\Checkout\Shipping $subject,
-        \Closure $proceed,
-        \Magento\Framework\DataObject $addressEntity
-    ) {
-        $itemsBoxText = $proceed($addressEntity);
+    public function afterGetItemsBoxTextAfter(ShippingBlock $subject, $itemsBoxText, DataObject $addressEntity)
+    {
         return $itemsBoxText . $this->helper->getInline('multishipping_address', $addressEntity);
     }
 }
diff --git a/app/code/Magento/GiftMessage/Model/Plugin/QuoteItem.php b/app/code/Magento/GiftMessage/Model/Plugin/QuoteItem.php
index 0faee488baea061b83e1180a715dd213a5ceec07..6367fd7ced4052e06cd7b787c98de861ed8b166a 100644
--- a/app/code/Magento/GiftMessage/Model/Plugin/QuoteItem.php
+++ b/app/code/Magento/GiftMessage/Model/Plugin/QuoteItem.php
@@ -5,40 +5,42 @@
  */
 namespace Magento\GiftMessage\Model\Plugin;
 
-use Closure;
-use Magento\Sales\Model\Order\Item;
+use Magento\Sales\Api\Data\OrderItemInterface;
+use Magento\GiftMessage\Helper\Message as MessageHelper;
+use Magento\Quote\Model\Quote\Item\ToOrderItem;
+use Magento\Quote\Model\Quote\Item\AbstractItem;
 
 class QuoteItem
 {
     /**
-     * @var \Magento\GiftMessage\Helper\Message
+     * @var MessageHelper
      */
     protected $_helper;
 
     /**
-     * @param \Magento\GiftMessage\Helper\Message $helper
+     * @param MessageHelper $helper
      */
-    public function __construct(\Magento\GiftMessage\Helper\Message $helper)
+    public function __construct(MessageHelper $helper)
     {
         $this->_helper = $helper;
     }
 
     /**
-     * @param \Magento\Quote\Model\Quote\Item\ToOrderItem $subject
-     * @param callable $proceed
-     * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item
+     * Apply gift message per every item in order if available
+     *
+     * @param ToOrderItem $subject
+     * @param OrderItemInterface $orderItem
+     * @param AbstractItem $item
      * @param array $additional
-     * @return Item
+     * @return OrderItemInterface
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundConvert(
-        \Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
-        Closure $proceed,
-        \Magento\Quote\Model\Quote\Item\AbstractItem $item,
+    public function afterConvert(
+        ToOrderItem $subject,
+        OrderItemInterface $orderItem,
+        AbstractItem $item,
         $additional = []
     ) {
-        /** @var $orderItem Item */
-        $orderItem = $proceed($item, $additional);
         $isAvailable = $this->_helper->isMessagesAllowed('item', $item, $item->getStoreId());
 
         $orderItem->setGiftMessageId($item->getGiftMessageId());
diff --git a/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/QuoteItemTest.php b/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/QuoteItemTest.php
index ad099bebc8847d7556414d6118687c1da108c8d5..cfb8f1463d6eb0d8aba41e60a271ebc4ccfa33f7 100644
--- a/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/QuoteItemTest.php
+++ b/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/QuoteItemTest.php
@@ -68,7 +68,7 @@ class QuoteItemTest extends \PHPUnit_Framework_TestCase
         $this->model = new \Magento\GiftMessage\Model\Plugin\QuoteItem($this->helperMock);
     }
 
-    public function testAroundItemToOrderItem()
+    public function testAfterItemToOrderItem()
     {
         $storeId = 1;
         $giftMessageId = 1;
@@ -99,7 +99,7 @@ class QuoteItemTest extends \PHPUnit_Framework_TestCase
 
         $this->assertSame(
             $this->orderItemMock,
-            $this->model->aroundConvert($this->subjectMock, $this->closureMock, $this->quoteItemMock, [])
+            $this->model->afterConvert($this->subjectMock, $this->orderItemMock, $this->quoteItemMock, [])
         );
     }
 }
diff --git a/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Link/RelationPersister.php b/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Link/RelationPersister.php
index f83b34ec3b30d694207511fc096a87a1b90ff90c..f431bf3af952b2109b60e44436944f9040e16049 100644
--- a/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Link/RelationPersister.php
+++ b/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Link/RelationPersister.php
@@ -9,6 +9,7 @@ namespace Magento\GroupedProduct\Model\ResourceModel\Product\Link;
 use Magento\Catalog\Model\ProductLink\LinkFactory;
 use Magento\Catalog\Model\ResourceModel\Product\Link;
 use Magento\Catalog\Model\ResourceModel\Product\Relation;
+use Magento\GroupedProduct\Model\ResourceModel\Product\Link as GroupedLink;
 
 class RelationPersister
 {
@@ -38,17 +39,16 @@ class RelationPersister
      * Save grouped products to product relation table
      *
      * @param Link $subject
-     * @param \Closure $proceed
+     * @param Link $result
      * @param int $parentId
      * @param array $data
      * @param int $typeId
      * @return Link
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundSaveProductLinks(Link $subject, \Closure $proceed, $parentId, $data, $typeId)
+    public function afterSaveProductLinks(Link $subject, Link $result, $parentId, $data, $typeId)
     {
-        $result = $proceed($parentId, $data, $typeId);
-        if ($typeId == \Magento\GroupedProduct\Model\ResourceModel\Product\Link::LINK_TYPE_GROUPED) {
+        if ($typeId == GroupedLink::LINK_TYPE_GROUPED) {
             foreach ($data as $linkData) {
                 $this->relationProcessor->addRelation(
                     $parentId,
@@ -73,7 +73,7 @@ class RelationPersister
         $link = $this->linkFactory->create();
         $subject->load($link, $linkId, $subject->getIdFieldName());
         $result = $proceed($linkId);
-        if ($link->getLinkTypeId() == \Magento\GroupedProduct\Model\ResourceModel\Product\Link::LINK_TYPE_GROUPED) {
+        if ($link->getLinkTypeId() == GroupedLink::LINK_TYPE_GROUPED) {
             $this->relationProcessor->removeRelations(
                 $link->getProductId(),
                 $link->getLinkedProductId()
diff --git a/app/code/Magento/GroupedProduct/Model/Sales/AdminOrder/Product/Quote/Plugin/Initializer.php b/app/code/Magento/GroupedProduct/Model/Sales/AdminOrder/Product/Quote/Plugin/Initializer.php
index f1204efa714a502cfec8563e408df353e4f89b53..2ab99856df306f9af5b2d951bb75785e64107c95 100644
--- a/app/code/Magento/GroupedProduct/Model/Sales/AdminOrder/Product/Quote/Plugin/Initializer.php
+++ b/app/code/Magento/GroupedProduct/Model/Sales/AdminOrder/Product/Quote/Plugin/Initializer.php
@@ -18,7 +18,7 @@ class Initializer
 {
     /**
      * @param \Magento\Sales\Model\AdminOrder\Product\Quote\Initializer $subject
-     * @param callable $proceed
+     * @param \Magento\Quote\Model\Quote\Item|string $item
      * @param \Magento\Quote\Model\Quote $quote
      * @param \Magento\Catalog\Model\Product $product
      * @param \Magento\Framework\DataObject $config
@@ -26,15 +26,13 @@ class Initializer
      * @return \Magento\Quote\Model\Quote\Item|string
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundInit(
+    public function afterInit(
         \Magento\Sales\Model\AdminOrder\Product\Quote\Initializer $subject,
-        \Closure $proceed,
+        $item,
         \Magento\Quote\Model\Quote $quote,
         \Magento\Catalog\Model\Product $product,
         \Magento\Framework\DataObject $config
     ) {
-        $item = $proceed($quote, $product, $config);
-
         if (is_string($item) && $product->getTypeId() != Grouped::TYPE_CODE) {
             $item = $quote->addProduct(
                 $product,
diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/ResourceModel/Product/Link/RelationPersisterTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/ResourceModel/Product/Link/RelationPersisterTest.php
index d11539945b0e84f7437b43975de4abc6db00dcc3..49bcd76cc2b83d0f838438e333924eaca62b912e 100644
--- a/app/code/Magento/GroupedProduct/Test/Unit/Model/ResourceModel/Product/Link/RelationPersisterTest.php
+++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/ResourceModel/Product/Link/RelationPersisterTest.php
@@ -3,13 +3,14 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\GroupedProduct\Test\Unit\Model\ResourceModel\Product\Link;
 
 use Magento\GroupedProduct\Model\ResourceModel\Product\Link\RelationPersister;
 use Magento\Catalog\Model\ProductLink\LinkFactory;
 use Magento\Catalog\Model\Product\Link;
 use Magento\Catalog\Model\ResourceModel\Product\Relation;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Catalog\Model\ResourceModel\Product\Link as LinkResourceModel;
 
 class RelationPersisterTest extends \PHPUnit_Framework_TestCase
 {
@@ -22,12 +23,29 @@ class RelationPersisterTest extends \PHPUnit_Framework_TestCase
     /** @var  Relation */
     private $relationProcessor;
 
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var LinkFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $linkFactory;
+
+    /**
+     * @var LinkResourceModel|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subject;
+
     /**
      * @inheritDoc
      */
     protected function setUp()
     {
-        $linkFactory = $this->getMockBuilder(LinkFactory::class)
+        $this->objectManager = new ObjectManager($this);
+
+        $this->linkFactory = $this->getMockBuilder(LinkFactory::class)
             ->setMethods(['create'])
             ->disableOriginalConstructor()
             ->getMock();
@@ -41,23 +59,27 @@ class RelationPersisterTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
-        $linkFactory->expects($this->any())->method('create')->willReturn($this->link);
+        $this->linkFactory->expects($this->any())->method('create')->willReturn($this->link);
 
-        $this->object = new RelationPersister(
-            $this->relationProcessor,
-            $linkFactory
+        $this->subject = $this->getMockBuilder(LinkResourceModel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->object = $this->objectManager->getObject(
+            RelationPersister::class,
+            [
+                'relationProcessor' => $this->relationProcessor,
+                'linkFactory' => $this->linkFactory
+            ]
         );
     }
 
-    public function testAroundSaveProductLinks()
+    public function testAfterSaveProductLinks()
     {
-        $subject = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Link::class)
-            ->disableOriginalConstructor()
-            ->getMock();
         $this->relationProcessor->expects($this->once())->method('addRelation')->with(2, 10);
-        $this->assertEquals($subject, $this->object->aroundSaveProductLinks(
-            $subject,
-            function() use ($subject) { return $subject; },
+        $this->assertEquals($this->subject, $this->object->afterSaveProductLinks(
+            $this->subject,
+            $this->subject,
             2,
             [['product_id' => 10]],
             3
@@ -87,10 +109,11 @@ class RelationPersisterTest extends \PHPUnit_Framework_TestCase
             $subject,
             $this->object->aroundDeleteProductLink(
                 $subject,
-                function() use ($subject) { return $subject; },
+                function () use ($subject) {
+                    return $subject;
+                },
                 155
             )
         );
-
     }
 }
diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Sales/AdminOrder/Product/Quote/Plugin/InitializerTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Sales/AdminOrder/Product/Quote/Plugin/InitializerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..43d7aa93dd0df9d6030640114241dfe64273209a
--- /dev/null
+++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Sales/AdminOrder/Product/Quote/Plugin/InitializerTest.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\GroupedProduct\Test\Unit\Model\Sales\AdminOrder\Product\Quote\Plugin;
+
+use Magento\GroupedProduct\Model\Sales\AdminOrder\Product\Quote\Plugin\Initializer as QuoteInitializerPlugin;
+use Magento\Sales\Model\AdminOrder\Product\Quote\Initializer as QuoteInitializer;
+use Magento\Quote\Model\Quote;
+use Magento\Catalog\Model\Product;
+use Magento\Quote\Model\Quote\Item as QuoteItem;
+use Magento\Framework\DataObject;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+
+class InitializerTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var QuoteInitializerPlugin|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $plugin;
+
+    /**
+     * @var QuoteInitializer|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $initializer;
+
+    /**
+     * @var Quote|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $quote;
+
+    /**
+     * @var QuoteItem|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $quoteItem;
+
+    /**
+     * @var Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $product;
+
+    /**
+     * @var DataObject|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $config;
+
+    protected function setUp()
+    {
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+
+        $this->initializer = $this->getMockBuilder(QuoteInitializer::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->quote = $this->getMockBuilder(Quote::class)
+            ->setMethods(['addProduct'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->product = $this->getMockBuilder(Product::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getTypeId'])
+            ->getMock();
+        $this->quoteItem = $this->getMockBuilder(QuoteItem::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->config = $this->getMockBuilder(DataObject::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->plugin = $this->objectManagerHelper->getObject(
+            QuoteInitializerPlugin::class
+        );
+    }
+
+    public function testAfterInit()
+    {
+        $this->assertSame(
+            $this->quoteItem,
+            $this->plugin->afterInit($this->initializer, $this->quoteItem, $this->quote, $this->product, $this->config)
+        );
+    }
+}
diff --git a/app/code/Magento/MediaStorage/Model/Asset/Plugin/CleanMergedJsCss.php b/app/code/Magento/MediaStorage/Model/Asset/Plugin/CleanMergedJsCss.php
index 0100c9887f1537081af83a477da9c78c107615a9..4cd6d67a01073b76b396c29c3ad933022c4ee94f 100644
--- a/app/code/Magento/MediaStorage/Model/Asset/Plugin/CleanMergedJsCss.php
+++ b/app/code/Magento/MediaStorage/Model/Asset/Plugin/CleanMergedJsCss.php
@@ -35,15 +35,13 @@ class CleanMergedJsCss
      * Clean files in database on cleaning merged assets
      *
      * @param \Magento\Framework\View\Asset\MergeService $subject
-     * @param callable $proceed
+     * @param void $result
      *
      * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundCleanMergedJsCss(\Magento\Framework\View\Asset\MergeService $subject, \Closure $proceed)
+    public function afterCleanMergedJsCss(\Magento\Framework\View\Asset\MergeService $subject, $result)
     {
-        $proceed();
-
         /** @var \Magento\Framework\Filesystem\Directory\ReadInterface $pubStaticDirectory */
         $pubStaticDirectory = $this->filesystem->getDirectoryRead(DirectoryList::STATIC_VIEW);
         $mergedDir = $pubStaticDirectory->getAbsolutePath() . '/'
diff --git a/app/code/Magento/MediaStorage/Test/Unit/Model/Asset/Plugin/CleanMergedJsCssTest.php b/app/code/Magento/MediaStorage/Test/Unit/Model/Asset/Plugin/CleanMergedJsCssTest.php
index aa6578ebc7effec18df4e88a0f99784aed580ad3..9ff391185952255ccc216f376bf7c65e595bd21a 100644
--- a/app/code/Magento/MediaStorage/Test/Unit/Model/Asset/Plugin/CleanMergedJsCssTest.php
+++ b/app/code/Magento/MediaStorage/Test/Unit/Model/Asset/Plugin/CleanMergedJsCssTest.php
@@ -3,9 +3,6 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
-// @codingStandardsIgnoreFile
-
 namespace Magento\MediaStorage\Test\Unit\Model\Asset\Plugin;
 
 use Magento\Framework\App\Filesystem\DirectoryList;
@@ -22,11 +19,6 @@ class CleanMergedJsCssTest extends \Magento\Framework\TestFramework\Unit\BaseTes
      */
     private $filesystemMock;
 
-    /**
-     * @var bool
-     */
-    private $hasBeenCalled = false;
-
     /**
      * @var \Magento\MediaStorage\Model\Asset\Plugin\CleanMergedJsCss
      */
@@ -46,11 +38,8 @@ class CleanMergedJsCssTest extends \Magento\Framework\TestFramework\Unit\BaseTes
         );
     }
 
-    public function testAroundCleanMergedJsCss()
+    public function testAfterCleanMergedJsCss()
     {
-        $callable = function () {
-            $this->hasBeenCalled = true;
-        };
         $readDir = 'read directory';
         $mergedDir = $readDir .  '/' . \Magento\Framework\View\Asset\Merged::getRelativeDir();
 
@@ -65,11 +54,9 @@ class CleanMergedJsCssTest extends \Magento\Framework\TestFramework\Unit\BaseTes
             ->with(DirectoryList::STATIC_VIEW)
             ->willReturn($readDirectoryMock);
 
-        $this->model->aroundCleanMergedJsCss(
+        $this->model->afterCleanMergedJsCss(
             $this->basicMock(\Magento\Framework\View\Asset\MergeService::class),
-            $callable
+            null
         );
-
-        $this->assertTrue($this->hasBeenCalled);
     }
 }
diff --git a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php
index d785b3a82aff76cf200a5c662ad1070acc83d944..3cae720825a04efae6277123f8ba56d62325a08f 100644
--- a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php
+++ b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php
@@ -31,46 +31,27 @@ class CustomerPlugin
     /**
      * Plugin after create customer that updates any newsletter subscription that may have existed.
      *
+     * If we have extension attribute (is_subscribed) we need to subscribe that customer
+     *
      * @param CustomerRepository $subject
+     * @param CustomerInterface $result
      * @param CustomerInterface $customer
      * @return CustomerInterface
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function afterSave(CustomerRepository $subject, CustomerInterface $customer)
+    public function afterSave(CustomerRepository $subject, CustomerInterface $result, CustomerInterface $customer)
     {
-        $this->subscriberFactory->create()->updateSubscription($customer->getId());
-        return $customer;
-    }
-
-    /**
-     * Plugin around customer repository save. If we have extension attribute (is_subscribed) we need to subscribe that customer
-     *
-     * @param CustomerRepository $subject
-     * @param \Closure $proceed
-     * @param CustomerInterface $customer
-     * @param null $passwordHash
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
-     */
-    public function aroundSave(
-        CustomerRepository $subject,
-        \Closure $proceed,
-        CustomerInterface $customer,
-        $passwordHash = null
-    ) {
-        /** @var CustomerInterface $savedCustomer */
-        $savedCustomer = $proceed($customer, $passwordHash);
-
-        if ($savedCustomer->getId() && $customer->getExtensionAttributes()) {
+        $this->subscriberFactory->create()->updateSubscription($result->getId());
+        if ($result->getId() && $customer->getExtensionAttributes()) {
             if ($customer->getExtensionAttributes()->getIsSubscribed() === true) {
-                $this->subscriberFactory->create()->subscribeCustomerById($savedCustomer->getId());
+                $this->subscriberFactory->create()->subscribeCustomerById($result->getId());
             } elseif ($customer->getExtensionAttributes()->getIsSubscribed() === false) {
-                $this->subscriberFactory->create()->unsubscribeCustomerById($savedCustomer->getId());
+                $this->subscriberFactory->create()->unsubscribeCustomerById($result->getId());
             }
         }
-
-        return $savedCustomer;
+        return $result;
     }
-    
+
     /**
      * Plugin around delete customer that updates any newsletter subscription that may have existed.
      *
@@ -96,21 +77,16 @@ class CustomerPlugin
     }
 
     /**
-     * Plugin around delete customer that updates any newsletter subscription that may have existed.
+     * Plugin after delete customer that updates any newsletter subscription that may have existed.
      *
      * @param CustomerRepository $subject
-     * @param callable $deleteCustomer Function we are wrapping around
-     * @param CustomerInterface $customer Input to the function
+     * @param bool $result
+     * @param CustomerInterface $customer
      * @return bool
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDelete(
-        CustomerRepository $subject,
-        callable $deleteCustomer,
-        $customer
-    ) {
-        $result = $deleteCustomer($customer);
-        /** @var \Magento\Newsletter\Model\Subscriber $subscriber */
+    public function afterDelete(CustomerRepository $subject, $result, CustomerInterface $customer)
+    {
         $subscriber = $this->subscriberFactory->create();
         $subscriber->loadByEmail($customer->getEmail());
         if ($subscriber->getId()) {
diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php
index b4bc13043bf97409bfc6ceb7d8db47a5440fadca..458d6ea22b009323da9aee75dfe7a1a416833d19 100644
--- a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php
+++ b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php
@@ -60,26 +60,11 @@ class CustomerPluginTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    public function testAfterSave()
+    public function testAfterSaveWithoutIsSubscribed()
     {
-        $customerId = 1;
-        $subject = $this->getMock(\Magento\Customer\Api\CustomerRepositoryInterface::class);
-        $customer = $this->getMock(\Magento\Customer\Api\Data\CustomerInterface::class);
-        $customer->expects($this->once())->method('getId')->willReturn($customerId);
-        $this->subscriber->expects($this->once())->method('updateSubscription')->with($customerId)->willReturnSelf();
-
-        $this->assertEquals($customer, $this->plugin->afterSave($subject, $customer));
-    }
-
-    public function testAroundSaveWithoutIsSubscribed()
-    {
-        $passwordHash = null;
         $customerId = 1;
         /** @var CustomerInterface | \PHPUnit_Framework_MockObject_MockObject $customer */
         $customer = $this->getMock(\Magento\Customer\Api\Data\CustomerInterface::class);
-        $proceed  = function (CustomerInterface $customer, $passwordHash = null) use ($customer) {
-            return $customer;
-        };
         /** @var CustomerRepository | \PHPUnit_Framework_MockObject_MockObject $subject */
         $subject = $this->getMock(\Magento\Customer\Api\CustomerRepositoryInterface::class);
 
@@ -87,26 +72,27 @@ class CustomerPluginTest extends \PHPUnit_Framework_TestCase
             ->method("getId")
             ->willReturn($customerId);
 
-        $this->assertEquals($customer, $this->plugin->aroundSave($subject, $proceed, $customer, $passwordHash));
+        $this->assertEquals($customer, $this->plugin->afterSave($subject, $customer, $customer));
     }
 
     /**
      * @return array
      */
-    public function provideExtensionAttributeDataForAroundSave()
+    public function afterSaveExtensionAttributeDataProvider()
     {
         return [
-            [true, true] ,
+            [true, true],
             [false, false]
         ];
     }
 
     /**
-     * @dataProvider provideExtensionAttributeDataForAroundSave
+     * @param boolean $isSubscribed
+     * @param boolean $subscribeIsCreated
+     * @dataProvider afterSaveExtensionAttributeDataProvider
      */
-    public function testAroundSaveWithIsSubscribed($isSubscribed, $subscribeIsCreated)
+    public function testAfterSaveWithIsSubscribed($isSubscribed, $subscribeIsCreated)
     {
-        $passwordHash = null;
         $customerId = 1;
         /** @var CustomerInterface | \PHPUnit_Framework_MockObject_MockObject $customer */
         $customer = $this->getMock(\Magento\Customer\Api\Data\CustomerInterface::class);
@@ -134,9 +120,6 @@ class CustomerPluginTest extends \PHPUnit_Framework_TestCase
                 ->with($customerId);
         }
 
-        $proceed  = function (CustomerInterface $customer, $passwordHash = null) use ($customer) {
-            return $customer;
-        };
         /** @var CustomerRepository | \PHPUnit_Framework_MockObject_MockObject $subject */
         $subject = $this->getMock(\Magento\Customer\Api\CustomerRepositoryInterface::class);
 
@@ -144,14 +127,11 @@ class CustomerPluginTest extends \PHPUnit_Framework_TestCase
             ->method("getId")
             ->willReturn($customerId);
 
-        $this->assertEquals($customer, $this->plugin->aroundSave($subject, $proceed, $customer, $passwordHash));
+        $this->assertEquals($customer, $this->plugin->afterSave($subject, $customer, $customer));
     }
 
-    public function testAroundDelete()
+    public function testAfterDelete()
     {
-        $deleteCustomer = function () {
-            return true;
-        };
         $subject = $this->getMock(\Magento\Customer\Api\CustomerRepositoryInterface::class);
         $customer = $this->getMock(\Magento\Customer\Api\Data\CustomerInterface::class);
         $customer->expects($this->once())->method('getEmail')->willReturn('test@test.com');
@@ -159,7 +139,7 @@ class CustomerPluginTest extends \PHPUnit_Framework_TestCase
         $this->subscriber->expects($this->once())->method('getId')->willReturn(1);
         $this->subscriber->expects($this->once())->method('delete')->willReturnSelf();
 
-        $this->assertEquals(true, $this->plugin->aroundDelete($subject, $deleteCustomer, $customer));
+        $this->assertEquals(true, $this->plugin->afterDelete($subject, true, $customer));
     }
 
     public function testAroundDeleteById()
diff --git a/app/code/Magento/PageCache/Model/App/FrontController/VarnishPlugin.php b/app/code/Magento/PageCache/Model/App/FrontController/VarnishPlugin.php
index 1d716991f9d3f9dca62b76f12c0b1060d36ea31f..08b68447c9d4d8dd4f3612692e5488dd204d1e15 100644
--- a/app/code/Magento/PageCache/Model/App/FrontController/VarnishPlugin.php
+++ b/app/code/Magento/PageCache/Model/App/FrontController/VarnishPlugin.php
@@ -5,61 +5,67 @@
  */
 namespace Magento\PageCache\Model\App\FrontController;
 
+use Magento\PageCache\Model\Config;
+use Magento\Framework\App\PageCache\Version;
+use Magento\Framework\App\State as AppState;
+use Magento\Framework\App\FrontControllerInterface;
+use Magento\Framework\App\ResponseInterface;
+use Magento\Framework\App\Response\Http as ResponseHttp;
+use Magento\Framework\Controller\ResultInterface;
+
 /**
  * Varnish for processing builtin cache
  */
 class VarnishPlugin
 {
     /**
-     * @var \Magento\Framework\App\Config\ScopeConfigInterface
+     * @var Config
      */
-    protected $config;
+    private $config;
 
     /**
-     * @var \Magento\Framework\App\PageCache\Version
+     * @var Version
      */
-    protected $version;
+    private $version;
 
     /**
-     * @var \Magento\Framework\App\State
+     * @var AppState
      */
-    protected $state;
+    private $state;
 
     /**
-     * @param \Magento\PageCache\Model\Config $config
-     * @param \Magento\Framework\App\PageCache\Version $version
-     * @param \Magento\Framework\App\State $state
+     * @param Config $config
+     * @param Version $version
+     * @param AppState $state
      */
-    public function __construct(
-        \Magento\PageCache\Model\Config $config,
-        \Magento\Framework\App\PageCache\Version $version,
-        \Magento\Framework\App\State $state
-    ) {
+    public function __construct(Config $config, Version $version, AppState $state)
+    {
         $this->config = $config;
         $this->version = $version;
         $this->state = $state;
     }
 
     /**
-     * @param \Magento\Framework\App\FrontControllerInterface $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\App\RequestInterface $request
-     * @return false|\Magento\Framework\App\Response\Http|\Magento\Framework\Controller\ResultInterface
+     * Perform response postprocessing
+     *
+     * @param FrontControllerInterface $subject
+     * @param ResponseInterface|ResultInterface $result
+     * @return ResponseHttp|ResultInterface
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDispatch(
-        \Magento\Framework\App\FrontControllerInterface $subject,
-        \Closure $proceed,
-        \Magento\Framework\App\RequestInterface $request
-    ) {
-        $response = $proceed($request);
-        if ($this->config->getType() == \Magento\PageCache\Model\Config::VARNISH && $this->config->isEnabled()
-            && $response instanceof \Magento\Framework\App\Response\Http) {
+    public function afterDispatch(FrontControllerInterface $subject, $result)
+    {
+        if ($this->config->getType() == Config::VARNISH && $this->config->isEnabled()
+            && $result instanceof ResponseHttp
+        ) {
             $this->version->process();
-            if ($this->state->getMode() == \Magento\Framework\App\State::MODE_DEVELOPER) {
-                $response->setHeader('X-Magento-Debug', 1);
+
+            if ($this->state->getMode() == AppState::MODE_DEVELOPER) {
+                $result->setHeader('X-Magento-Debug', 1);
             }
         }
-        return $response;
+
+        return $result;
     }
 }
diff --git a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php
index 0fcad45130d3ebb0dfe94ae8eeafbd885fb5e38b..d2ef015fb69272586576a335ce7df3c9d81acf59 100644
--- a/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php
+++ b/app/code/Magento/PageCache/Model/Controller/Result/BuiltinPlugin.php
@@ -5,47 +5,50 @@
  */
 namespace Magento\PageCache\Model\Controller\Result;
 
+use Magento\PageCache\Model\Config;
+use Magento\Framework\App\PageCache\Kernel;
+use Magento\Framework\App\State as AppState;
+use Magento\Framework\Registry;
+use Magento\Framework\Controller\ResultInterface;
 use Magento\Framework\App\Response\Http as ResponseHttp;
+use Zend\Http\Header\HeaderInterface as HttpHeaderInterface;
+use Magento\PageCache\Model\Cache\Type as CacheType;
 
 /**
  * Plugin for processing builtin cache
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class BuiltinPlugin
 {
     /**
-     * @var \Magento\Framework\App\Config\ScopeConfigInterface
+     * @var Config
      */
-    protected $config;
+    private $config;
 
     /**
-     * @var \Magento\Framework\App\PageCache\Kernel
+     * @var Kernel
      */
-    protected $kernel;
+    private $kernel;
 
     /**
-     * @var \Magento\Framework\App\State
+     * @var AppState
      */
-    protected $state;
+    private $state;
 
     /**
-     * @var \Magento\Framework\Registry
+     * @var Registry
      */
-    protected $registry;
+    private $registry;
 
     /**
-     * Constructor
-     *
-     * @param \Magento\PageCache\Model\Config $config
-     * @param \Magento\Framework\App\PageCache\Kernel $kernel
-     * @param \Magento\Framework\App\State $state
-     * @param \Magento\Framework\Registry $registry
+     * @param Config $config
+     * @param Kernel $kernel
+     * @param AppState $state
+     * @param Registry $registry
      */
-    public function __construct(
-        \Magento\PageCache\Model\Config $config,
-        \Magento\Framework\App\PageCache\Kernel $kernel,
-        \Magento\Framework\App\State $state,
-        \Magento\Framework\Registry $registry
-    ) {
+    public function __construct(Config $config, Kernel $kernel, AppState $state, Registry $registry)
+    {
         $this->config = $config;
         $this->kernel = $kernel;
         $this->state = $state;
@@ -53,43 +56,45 @@ class BuiltinPlugin
     }
 
     /**
-     * @param \Magento\Framework\Controller\ResultInterface $subject
-     * @param callable $proceed
+     * Perform result postprocessing
+     *
+     * @param ResultInterface $subject
+     * @param ResultInterface $result
      * @param ResponseHttp $response
-     * @return \Magento\Framework\Controller\ResultInterface
+     * @return ResultInterface
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundRenderResult(
-        \Magento\Framework\Controller\ResultInterface $subject,
-        \Closure $proceed,
-        ResponseHttp $response
-    ) {
-        $result = $proceed($response);
+    public function afterRenderResult(ResultInterface $subject, ResultInterface $result, ResponseHttp $response)
+    {
         $usePlugin = $this->registry->registry('use_page_cache_plugin');
-        if (!$usePlugin || !$this->config->isEnabled()
-            || $this->config->getType() != \Magento\PageCache\Model\Config::BUILT_IN
-        ) {
+
+        if (!$usePlugin || !$this->config->isEnabled() || $this->config->getType() != Config::BUILT_IN) {
             return $result;
         }
 
-        if ($this->state->getMode() == \Magento\Framework\App\State::MODE_DEVELOPER) {
+        if ($this->state->getMode() == AppState::MODE_DEVELOPER) {
             $cacheControlHeader = $response->getHeader('Cache-Control');
-            if ($cacheControlHeader instanceof \Zend\Http\Header\HeaderInterface) {
+
+            if ($cacheControlHeader instanceof HttpHeaderInterface) {
                 $response->setHeader('X-Magento-Cache-Control', $cacheControlHeader->getFieldValue());
             }
+
             $response->setHeader('X-Magento-Cache-Debug', 'MISS', true);
         }
 
         $tagsHeader = $response->getHeader('X-Magento-Tags');
         $tags = [];
+
         if ($tagsHeader) {
             $tags = explode(',', $tagsHeader->getFieldValue());
             $response->clearHeader('X-Magento-Tags');
         }
-        $tags = array_unique(array_merge($tags, [\Magento\PageCache\Model\Cache\Type::CACHE_TAG]));
-        $response->setHeader('X-Magento-Tags', implode(',', $tags));
 
+        $tags = array_unique(array_merge($tags, [CacheType::CACHE_TAG]));
+        $response->setHeader('X-Magento-Tags', implode(',', $tags));
         $this->kernel->process($response);
+
         return $result;
     }
 }
diff --git a/app/code/Magento/PageCache/Model/Controller/Result/VarnishPlugin.php b/app/code/Magento/PageCache/Model/Controller/Result/VarnishPlugin.php
index 19868a2261f1fd7d486fab066b1e4cc02704a6c5..368a6db80c7e01d7fdaafca710beb3470d870e33 100644
--- a/app/code/Magento/PageCache/Model/Controller/Result/VarnishPlugin.php
+++ b/app/code/Magento/PageCache/Model/Controller/Result/VarnishPlugin.php
@@ -5,7 +5,12 @@
  */
 namespace Magento\PageCache\Model\Controller\Result;
 
+use Magento\PageCache\Model\Config;
+use Magento\Framework\App\PageCache\Version;
+use Magento\Framework\App\State as AppState;
+use Magento\Framework\Registry;
 use Magento\Framework\App\Response\Http as ResponseHttp;
+use Magento\Framework\Controller\ResultInterface;
 
 /**
  * Plugin for processing varnish cache
@@ -13,74 +18,61 @@ use Magento\Framework\App\Response\Http as ResponseHttp;
 class VarnishPlugin
 {
     /**
-     * @var \Magento\Framework\App\Config\ScopeConfigInterface
+     * @var Config
      */
-    protected $config;
+    private $config;
 
     /**
-     * @var \Magento\Framework\App\PageCache\Version
+     * @var Version
      */
-    protected $version;
+    private $version;
 
     /**
-     * @var \Magento\Framework\App\PageCache\Kernel
+     * @var AppState
      */
-    protected $kernel;
+    private $state;
 
     /**
-     * @var \Magento\Framework\App\State
+     * @var Registry
      */
-    protected $state;
+    private $registry;
 
     /**
-     * @var \Magento\Framework\Registry
+     * @param Config $config
+     * @param Version $version
+     * @param AppState $state
+     * @param Registry $registry
      */
-    protected $registry;
-
-    /**
-     * Constructor
-     *
-     * @param \Magento\PageCache\Model\Config $config
-     * @param \Magento\Framework\App\PageCache\Version $version
-     * @param \Magento\Framework\App\PageCache\Kernel $kernel
-     * @param \Magento\Framework\App\State $state
-     * @param \Magento\Framework\Registry $registry
-     */
-    public function __construct(
-        \Magento\PageCache\Model\Config $config,
-        \Magento\Framework\App\PageCache\Version $version,
-        \Magento\Framework\App\PageCache\Kernel $kernel,
-        \Magento\Framework\App\State $state,
-        \Magento\Framework\Registry $registry
-    ) {
+    public function __construct(Config $config, Version $version, AppState $state, Registry $registry)
+    {
         $this->config = $config;
         $this->version = $version;
-        $this->kernel = $kernel;
         $this->state = $state;
         $this->registry = $registry;
     }
 
     /**
-     * @param \Magento\Framework\Controller\ResultInterface $subject
-     * @param callable $proceed
+     * Perform result postprocessing
+     *
+     * @param ResultInterface $subject
+     * @param ResultInterface $result
      * @param ResponseHttp $response
-     * @return \Magento\Framework\Controller\ResultInterface
+     * @return ResultInterface
+     *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundRenderResult(
-        \Magento\Framework\Controller\ResultInterface $subject,
-        \Closure $proceed,
-        ResponseHttp $response
-    ) {
-        $proceed($response);
+    public function afterRenderResult(ResultInterface $subject, ResultInterface $result, ResponseHttp $response)
+    {
         $usePlugin = $this->registry->registry('use_page_cache_plugin');
-        if ($this->config->getType() == \Magento\PageCache\Model\Config::VARNISH && $this->config->isEnabled()
-            && $usePlugin) {
+
+        if ($this->config->getType() == Config::VARNISH && $this->config->isEnabled() && $usePlugin) {
             $this->version->process();
-            if ($this->state->getMode() == \Magento\Framework\App\State::MODE_DEVELOPER) {
+
+            if ($this->state->getMode() == AppState::MODE_DEVELOPER) {
                 $response->setHeader('X-Magento-Debug', 1);
             }
         }
 
-        return $subject;
+        return $result;
     }
 }
diff --git a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/VarnishPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/VarnishPluginTest.php
index 32328d7d027930b768683f9773366f6be9b92f84..a9b0e10307ae37d556f9c4cc96224f01fcd2bb9d 100644
--- a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/VarnishPluginTest.php
+++ b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/VarnishPluginTest.php
@@ -3,148 +3,169 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\PageCache\Test\Unit\Model\App\FrontController;
 
 use Magento\PageCache\Model\App\FrontController\VarnishPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\PageCache\Model\Config;
+use Magento\Framework\App\PageCache\Version;
+use Magento\Framework\App\State as AppState;
+use Magento\Framework\App\FrontControllerInterface;
+use Magento\Framework\App\Response\Http as ResponseHttp;
+use Magento\Framework\Controller\ResultInterface;
 
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class VarnishPluginTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * @var VarnishPlugin
      */
-    protected $plugin;
+    private $plugin;
 
     /**
-     * @var \Magento\PageCache\Model\Config|\PHPUnit_Framework_MockObject_MockObject
+     * @var ObjectManagerHelper
      */
-    protected $configMock;
+    private $objectManagerHelper;
 
     /**
-     * @var \Magento\Framework\App\PageCache\Version|\PHPUnit_Framework_MockObject_MockObject
+     * @var Config|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $versionMock;
+    private $configMock;
 
     /**
-     * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject
+     * @var Version|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $stateMock;
+    private $versionMock;
 
     /**
-     * @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject
+     * @var AppState|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $responseMock;
+    private $stateMock;
 
     /**
-     * @var \Magento\Framework\App\FrontControllerInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var FrontControllerInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $frontControllerMock;
+    private $frontControllerMock;
 
     /**
-     * @var \Closure
+     * @var ResponseHttp|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $closure;
+    private $responseMock;
 
     /**
-     * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $requestMock;
+    private $resultMock;
 
-    /**
-     * SetUp
-     */
     protected function setUp()
     {
-        $this->configMock = $this->getMock(\Magento\PageCache\Model\Config::class, [], [], '', false);
-        $this->versionMock = $this->getMock(\Magento\Framework\App\PageCache\Version::class, [], [], '', false);
-        $this->stateMock = $this->getMock(\Magento\Framework\App\State::class, [], [], '', false);
-        $this->frontControllerMock = $this->getMock(
-            \Magento\Framework\App\FrontControllerInterface::class,
-            [],
-            [],
-            '',
-            false
-        );
-        $this->requestMock = $this->getMock(\Magento\Framework\App\RequestInterface::class, [], [], '', false);
-        $this->responseMock = $this->getMock(\Magento\Framework\App\Response\Http::class, [], [], '', false);
-        $response = $this->responseMock;
-        $this->closure = function () use ($response) {
-            return $response;
-        };
-        $this->plugin = new \Magento\PageCache\Model\App\FrontController\VarnishPlugin(
-            $this->configMock,
-            $this->versionMock,
-            $this->stateMock
+        $this->configMock = $this->getMockBuilder(Config::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->versionMock = $this->getMockBuilder(Version::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->stateMock = $this->getMockBuilder(AppState::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->frontControllerMock = $this->getMockBuilder(FrontControllerInterface::class)
+            ->getMockForAbstractClass();
+        $this->responseMock = $this->getMockBuilder(ResponseHttp::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->resultMock = $this->getMockBuilder(ResultInterface::class)
+            ->getMockForAbstractClass();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            VarnishPlugin::class,
+            [
+                'config' => $this->configMock,
+                'version' => $this->versionMock,
+                'state' => $this->stateMock
+            ]
         );
     }
 
     /**
-     * @dataProvider dataProvider
+     * @param string $state
+     * @param int $countHeader
+     *
+     * @dataProvider afterDispatchDataProvider
      */
-    public function testAroundDispatchReturnsCache($state, $countHeader, $countProcess, $countGetMode, $response)
+    public function testAfterDispatchReturnsCache($state, $countHeader)
     {
-        $this->configMock
-            ->expects($this->once())
+        $this->configMock->expects(static::once())
             ->method('isEnabled')
-            ->will($this->returnValue(true));
-        $this->configMock
-            ->expects($this->once())
+            ->willReturn(true);
+        $this->configMock->expects(static::once())
             ->method('getType')
-            ->will($this->returnValue(\Magento\PageCache\Model\Config::VARNISH));
-        $this->versionMock
-            ->expects($countProcess)
+            ->willReturn(Config::VARNISH);
+        $this->versionMock->expects(static::once())
             ->method('process');
-        $this->stateMock->expects($countGetMode)
+        $this->stateMock->expects(static::once())
             ->method('getMode')
-            ->will($this->returnValue($state));
-        $response->expects($countHeader)
+            ->willReturn($state);
+        $this->responseMock->expects(static::exactly($countHeader))
             ->method('setHeader')
             ->with('X-Magento-Debug');
 
-        $this->closure = function () use ($response) {
-            return $response;
-        };
+        $this->assertSame(
+            $this->responseMock,
+            $this->plugin->afterDispatch($this->frontControllerMock, $this->responseMock)
+        );
+    }
 
-        $this->plugin->aroundDispatch($this->frontControllerMock, $this->closure, $this->requestMock);
+    public function testAfterDispatchNotResponse()
+    {
+        $this->configMock->expects(static::once())
+            ->method('isEnabled')
+            ->willReturn(true);
+        $this->configMock->expects(static::once())
+            ->method('getType')
+            ->willReturn(Config::VARNISH);
+        $this->versionMock->expects(static::never())
+            ->method('process');
+        $this->stateMock->expects(static::never())
+            ->method('getMode');
+        $this->resultMock->expects(static::never())
+            ->method('setHeader');
+
+        $this->assertSame(
+            $this->resultMock,
+            $this->plugin->afterDispatch($this->frontControllerMock, $this->resultMock)
+        );
     }
 
-    /**
-     * @dataProvider dataProvider
-     */
-    public function testAroundDispatchDisabled($state)
+    public function testAfterDispatchDisabled()
     {
-        $this->configMock
-            ->expects($this->any())
+        $this->configMock->expects(static::any())
             ->method('getType')
-            ->will($this->returnValue(null));
-        $this->versionMock
-            ->expects($this->never())
+            ->willReturn(null);
+        $this->versionMock->expects(static::never())
             ->method('process');
-        $this->stateMock->expects($this->any())
+        $this->stateMock->expects(static::any())
             ->method('getMode')
-            ->will($this->returnValue($state));
-        $this->responseMock->expects($this->never())
+            ->willReturn(AppState::MODE_DEVELOPER);
+        $this->responseMock->expects(static::never())
             ->method('setHeader');
-        $this->plugin->aroundDispatch($this->frontControllerMock, $this->closure, $this->requestMock);
+
+        $this->assertSame(
+            $this->responseMock,
+            $this->plugin->afterDispatch($this->frontControllerMock, $this->responseMock)
+        );
     }
 
-    public function dataProvider()
+    /**
+     * @return array
+     */
+    public function afterDispatchDataProvider()
     {
         return [
-            'developer_mode' => [
-                \Magento\Framework\App\State::MODE_DEVELOPER,
-                $this->once(),
-                $this->once(),
-                $this->once(),
-                $this->getMock(\Magento\Framework\App\Response\Http::class, [], [], '', false),
-            ],
-            'production' => [
-                \Magento\Framework\App\State::MODE_PRODUCTION,
-                $this->never(),
-                $this->never(),
-                $this->never(),
-                $this->getMock(\Magento\Framework\Controller\ResultInterface::class, [], [], '', false),
-            ],
+            'developer_mode' => [AppState::MODE_DEVELOPER, 1],
+            'production' => [AppState::MODE_PRODUCTION, 0]
         ];
     }
 }
diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php
index 15934f3104b2305c00a8a0f2b23c3ab8717daba7..f0ba7b22dad2d5126b6b1481f8776f5bacc7843f 100644
--- a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php
+++ b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/BuiltinPluginTest.php
@@ -3,141 +3,195 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
-// @codingStandardsIgnoreFile
-
 namespace Magento\PageCache\Test\Unit\Model\Controller\Result;
 
+use Magento\PageCache\Model\Controller\Result\BuiltinPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\PageCache\Model\Config;
+use Magento\Framework\App\PageCache\Kernel;
+use Magento\Framework\App\State as AppState;
+use Magento\Framework\Registry;
+use Magento\Framework\Controller\ResultInterface;
+use Magento\Framework\App\Response\Http as ResponseHttp;
+use Zend\Http\Header\HeaderInterface as HttpHeaderInterface;
+use Magento\PageCache\Model\Cache\Type as CacheType;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class BuiltinPluginTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\PageCache\Model\Controller\Result\BuiltinPlugin
+     * @var BuiltinPlugin
+     */
+    private $plugin;
+
+    /**
+     * @var ObjectManagerHelper
      */
-    protected $plugin;
+    private $objectManagerHelper;
 
     /**
-     * @var \Magento\Framework\Controller\ResultInterface
+     * @var Config|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $subject;
+    private $configMock;
 
     /**
-     * @var \Closure
+     * @var Kernel|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $closure;
+    private $kernelMock;
 
     /**
-     * @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject
+     * @var AppState|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $response;
+    private $stateMock;
 
     /**
-     * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject
+     * @var Registry|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $registry;
+    private $registryMock;
 
     /**
-     * @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject
+     * @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $state;
+    private $resultMock;
 
     /**
-     * @var \Zend\Http\Header\HeaderInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var ResponseHttp|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $header;
+    private $responseMock;
 
     /**
-     * @var \Magento\Framework\App\PageCache\Kernel|\PHPUnit_Framework_MockObject_MockObject
+     * @var HttpHeaderInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $kernel;
+    private $httpHeaderMock;
 
     protected function setUp()
     {
-        $result = $this->getMock(\Magento\Framework\Controller\ResultInterface::class, [], [], '', false);
-        $this->closure = function() use ($result) {
-            return $result;
-        };
-
-        $this->header = $this->getMock(\Zend\Http\Header\HeaderInterface::class, [], [], '', false);
-        $this->subject = $this->getMock(\Magento\Framework\Controller\ResultInterface::class, [], [], '', false);
-        $this->response = $this->getMock(
-            \Magento\Framework\App\Response\Http::class,
-            ['getHeader', 'clearHeader', 'setHeader'],
-            [],
-            '',
-            false
-        );
-        $this->response->expects($this->any())->method('getHeader')->willReturnMap(
+        $this->configMock = $this->getMockBuilder(Config::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->kernelMock = $this->getMockBuilder(Kernel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->stateMock = $this->getMockBuilder(AppState::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->registryMock = $this->getMockBuilder(Registry::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->resultMock = $this->getMockBuilder(ResultInterface::class)
+            ->getMockForAbstractClass();
+        $this->responseMock = $this->getMockBuilder(ResponseHttp::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->httpHeaderMock = $this->getMockBuilder(HttpHeaderInterface::class)
+            ->getMockForAbstractClass();
+
+        $this->responseMock->expects(static::any())
+            ->method('getHeader')
+            ->willReturnMap(
+                [
+                    ['X-Magento-Tags', $this->httpHeaderMock],
+                    ['Cache-Control', $this->httpHeaderMock]
+                ]
+            );
+        $this->configMock->expects(static::any())
+            ->method('isEnabled')
+            ->willReturn(true);
+        $this->configMock->expects(static::any())
+            ->method('getType')
+            ->willReturn(Config::BUILT_IN);
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            BuiltinPlugin::class,
             [
-                ['X-Magento-Tags', $this->header],
-                ['Cache-Control', $this->header]
-            ]
-        );
-
-        $this->registry = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false);
-
-        $config = $this->getMock(\Magento\PageCache\Model\Config::class, ['isEnabled', 'getType'], [], '', false);
-        $config->expects($this->any())->method('isEnabled')->willReturn(true);
-        $config->expects($this->any())->method('getType')->willReturn(\Magento\PageCache\Model\Config::BUILT_IN);
-
-        $this->kernel = $this->getMock(\Magento\Framework\App\PageCache\Kernel::class, [], [], '', false);
-
-        $this->state = $this->getMock(\Magento\Framework\App\State::class, [], [], '', false);
-        $this->plugin = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
-            \Magento\PageCache\Model\Controller\Result\BuiltinPlugin::class,
-            [
-                'registry' => $this->registry,
-                'config' => $config,
-                'kernel' => $this->kernel,
-                'state' => $this->state
+                'registry' => $this->registryMock,
+                'config' => $this->configMock,
+                'kernel' => $this->kernelMock,
+                'state' => $this->stateMock
             ]
         );
     }
 
-    public function testAroundResultWithoutPlugin()
+    public function testAfterResultWithoutPlugin()
     {
-        $this->registry->expects($this->once())->method('registry')->with('use_page_cache_plugin')->willReturn(false);
-        $this->kernel->expects($this->never())->method('process')->with($this->response);
+        $this->registryMock->expects(static::once())
+            ->method('registry')
+            ->with('use_page_cache_plugin')
+            ->willReturn(false);
+        $this->kernelMock->expects(static::never())
+            ->method('process')
+            ->with($this->responseMock);
+
         $this->assertSame(
-            call_user_func($this->closure),
-            $this->plugin->aroundRenderResult($this->subject, $this->closure, $this->response)
+            $this->resultMock,
+            $this->plugin->afterRenderResult($this->resultMock, $this->resultMock, $this->responseMock)
         );
     }
 
-    public function testAroundResultWithPlugin()
+    public function testAfterResultWithPlugin()
     {
-        $this->registry->expects($this->once())->method('registry')->with('use_page_cache_plugin')->willReturn(true);
-        $this->state->expects($this->once())->method('getMode')->willReturn(null);
-        $this->header->expects($this->any())->method('getFieldValue')->willReturn('tag,tag');
-        $this->response->expects($this->once())->method('clearHeader')->with('X-Magento-Tags');
-        $this->response->expects($this->once())->method('setHeader')->with(
-            'X-Magento-Tags',
-            'tag,' . \Magento\PageCache\Model\Cache\Type::CACHE_TAG
+        $this->registryMock->expects(static::once())
+            ->method('registry')
+            ->with('use_page_cache_plugin')
+            ->willReturn(true);
+        $this->stateMock->expects(static::once())
+            ->method('getMode')
+            ->willReturn(null);
+        $this->httpHeaderMock->expects(static::any())
+            ->method('getFieldValue')
+            ->willReturn('tag,tag');
+        $this->responseMock->expects(static::once())
+            ->method('clearHeader')
+            ->with('X-Magento-Tags');
+        $this->responseMock->expects(static::once())
+            ->method('setHeader')
+            ->with('X-Magento-Tags', 'tag,' . CacheType::CACHE_TAG);
+        $this->kernelMock->expects(static::once())
+            ->method('process')
+            ->with($this->responseMock);
+
+        $this->assertSame(
+            $this->resultMock,
+            $this->plugin->afterRenderResult($this->resultMock, $this->resultMock, $this->responseMock)
         );
-        $this->kernel->expects($this->once())->method('process')->with($this->response);
-        $result = call_user_func($this->closure);
-        $this->assertSame($result, $this->plugin->aroundRenderResult($this->subject, $this->closure, $this->response));
     }
 
-    public function testAroundResultWithPluginDeveloperMode()
+    public function testAfterResultWithPluginDeveloperMode()
     {
-        $this->registry->expects($this->once())->method('registry')->with('use_page_cache_plugin')->willReturn(true);
-        $this->state->expects($this->once())->method('getMode')
-            ->willReturn(\Magento\Framework\App\State::MODE_DEVELOPER);
-
-        $this->header->expects($this->any())->method('getFieldValue')->willReturnOnConsecutiveCalls('test', 'tag,tag2');
+        $this->registryMock->expects(static::once())
+            ->method('registry')
+            ->with('use_page_cache_plugin')
+            ->willReturn(true);
+        $this->stateMock->expects(static::once())
+            ->method('getMode')
+            ->willReturn(AppState::MODE_DEVELOPER);
+        $this->httpHeaderMock->expects(static::any())
+            ->method('getFieldValue')
+            ->willReturnOnConsecutiveCalls('test', 'tag,tag2');
+        $this->responseMock->expects(static::any())
+            ->method('setHeader')
+            ->withConsecutive(
+                ['X-Magento-Cache-Control', 'test'],
+                ['X-Magento-Cache-Debug', 'MISS', true],
+                ['X-Magento-Tags', 'tag,tag2,' . CacheType::CACHE_TAG]
+            );
+        $this->responseMock->expects(static::once())
+            ->method('clearHeader')
+            ->with('X-Magento-Tags');
+        $this->registryMock->expects(static::once())
+            ->method('registry')
+            ->with('use_page_cache_plugin')
+            ->willReturn(true);
+        $this->kernelMock->expects(static::once())
+            ->method('process')
+            ->with($this->responseMock);
 
-        $this->response->expects($this->any())->method('setHeader')->withConsecutive(
-            ['X-Magento-Cache-Control', 'test'],
-            ['X-Magento-Cache-Debug', 'MISS', true],
-            ['X-Magento-Tags', 'tag,tag2,' . \Magento\PageCache\Model\Cache\Type::CACHE_TAG]
+        $this->assertSame(
+            $this->resultMock,
+            $this->plugin->afterRenderResult($this->resultMock, $this->resultMock, $this->responseMock)
         );
-
-        $this->response->expects($this->once())->method('clearHeader')->with('X-Magento-Tags');
-        $this->registry->expects($this->once())->method('registry')->with('use_page_cache_plugin')
-            ->will($this->returnValue(true));
-        $this->kernel->expects($this->once())->method('process')->with($this->response);
-
-        $result = call_user_func($this->closure);
-        $this->assertSame($result, $this->plugin->aroundRenderResult($this->subject, $this->closure, $this->response));
     }
 }
diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/VarnishPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/VarnishPluginTest.php
index e58aae1668ff8dbaeda016465d126b63c1fc4924..a54dc5bacb5a6dc31dc317622c74a9f7b18ba2d0 100644
--- a/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/VarnishPluginTest.php
+++ b/app/code/Magento/PageCache/Test/Unit/Model/Controller/Result/VarnishPluginTest.php
@@ -5,72 +5,135 @@
  */
 namespace Magento\PageCache\Test\Unit\Model\Controller\Result;
 
+use Magento\PageCache\Model\Controller\Result\VarnishPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\PageCache\Model\Config;
+use Magento\Framework\App\PageCache\Version;
+use Magento\Framework\App\State as AppState;
+use Magento\Framework\Registry;
+use Magento\Framework\Controller\ResultInterface;
+use Magento\Framework\App\Response\Http as ResponseHttp;
+
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class VarnishPluginTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @param bool $usePlugin
-     * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $setCacheDebugHeaderCount
-     * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $getModeCount
-     * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $processCount
-     * @dataProvider dataProvider
+     * @var VarnishPlugin
      */
-    public function testAroundResult($usePlugin, $setCacheDebugHeaderCount, $getModeCount, $processCount)
-    {
-        /** @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject $response */
-        $response = $this->getMock(\Magento\Framework\App\Response\Http::class, [], [], '', false);
-        $response->expects($setCacheDebugHeaderCount)->method('setHeader')
-            ->with('X-Magento-Debug', 1);
+    private $plugin;
+
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var Config|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $configMock;
 
-        /** @var \Magento\Framework\Controller\ResultInterface $result */
-        $result = $this->getMock(\Magento\Framework\Controller\ResultInterface::class, [], [], '', false);
-        $closure = function () use ($result) {
-            return $result;
-        };
+    /**
+     * @var Version|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $versionMock;
 
-        /** @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject $registry */
-        $registry = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false);
-        $registry->expects($this->once())->method('registry')->with('use_page_cache_plugin')
-            ->will($this->returnValue($usePlugin));
+    /**
+     * @var AppState|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $appStateMock;
 
-        /** @var \Magento\PageCache\Model\Config|\PHPUnit_Framework_MockObject_MockObject $config */
-        $config = $this->getMock(\Magento\PageCache\Model\Config::class, [], [], '', false);
-        $config->expects($this->once())->method('isEnabled')->will($this->returnValue(true));
-        $config->expects($this->once())->method('getType')
-            ->will($this->returnValue(\Magento\PageCache\Model\Config::VARNISH));
+    /**
+     * @var Registry|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $registryMock;
 
-        /** @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject $state */
-        $state = $this->getMock(\Magento\Framework\App\State::class, [], [], '', false);
-        $state->expects($getModeCount)->method('getMode')
-            ->will($this->returnValue(\Magento\Framework\App\State::MODE_DEVELOPER));
+    /**
+     * @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $resultMock;
 
-        /** @var \Magento\Framework\Controller\ResultInterface|\PHPUnit_Framework_MockObject_MockObject $subject */
-        $subject = $this->getMock(\Magento\Framework\Controller\ResultInterface::class, [], [], '', false);
+    /**
+     * @var ResponseHttp|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $responseMock;
 
-        /** @var \Magento\Framework\App\PageCache\Version|\PHPUnit_Framework_MockObject_MockObject $version */
-        $version = $this->getMock(\Magento\Framework\App\PageCache\Version::class, [], [], '', false);
-        $version->expects($processCount)->method('process');
+    protected function setUp()
+    {
+        $this->configMock = $this->getMockBuilder(Config::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->versionMock = $this->getMockBuilder(Version::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->appStateMock = $this->getMockBuilder(AppState::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->registryMock = $this->getMockBuilder(Registry::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->resultMock = $this->getMockBuilder(ResultInterface::class)
+            ->getMockForAbstractClass();
+        $this->responseMock = $this->getMockBuilder(ResponseHttp::class)
+            ->disableOriginalConstructor()
+            ->getMock();
 
-        /** @var \Magento\PageCache\Model\Controller\Result\VarnishPlugin $plugin */
-        $plugin = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
-            \Magento\PageCache\Model\Controller\Result\VarnishPlugin::class,
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            VarnishPlugin::class,
             [
-                'registry' => $registry,
-                'config' => $config,
-                'state' => $state,
-                'version' => $version
+                'registry' => $this->registryMock,
+                'config' => $this->configMock,
+                'state' => $this->appStateMock,
+                'version' => $this->versionMock
             ]
         );
-        $this->assertSame($subject, $plugin->aroundRenderResult($subject, $closure, $response));
+    }
+
+    /**
+     * @param bool $usePlugin
+     * @param int $setCacheDebugHeaderCount
+     * @param int $getModeCount
+     * @param int $processCount
+     *
+     * @dataProvider afterRenderResultDataProvider
+     */
+    public function testAfterRenderResult($usePlugin, $setCacheDebugHeaderCount, $getModeCount, $processCount)
+    {
+        $this->responseMock->expects(static::exactly($setCacheDebugHeaderCount))
+            ->method('setHeader')
+            ->with('X-Magento-Debug', 1);
+        $this->registryMock->expects(static::once())
+            ->method('registry')
+            ->with('use_page_cache_plugin')
+            ->willReturn($usePlugin);
+        $this->configMock->expects(static::once())
+            ->method('isEnabled')
+            ->willReturn(true);
+        $this->configMock->expects(static::once())
+            ->method('getType')
+            ->willReturn(Config::VARNISH);
+        $this->appStateMock->expects(static::exactly($getModeCount))
+            ->method('getMode')
+            ->willReturn(AppState::MODE_DEVELOPER);
+        $this->versionMock->expects(static::exactly($processCount))
+            ->method('process');
+
+        $this->assertSame(
+            $this->resultMock,
+            $this->plugin->afterRenderResult($this->resultMock, $this->resultMock, $this->responseMock)
+        );
     }
 
     /**
      * @return array
      */
-    public function dataProvider()
+    public function afterRenderResultDataProvider()
     {
         return [
-            [true, $this->once(), $this->once(), $this->once()],
-            [false, $this->never(), $this->never(), $this->never()]
+            [true, 1, 1, 1],
+            [false, 0, 0, 0]
         ];
     }
 }
diff --git a/app/code/Magento/Paypal/Block/Adminhtml/Store/SwitcherPlugin.php b/app/code/Magento/Paypal/Block/Adminhtml/Store/SwitcherPlugin.php
index 3af4493e61e1f869a9c5288c076f609add10b4fe..23b7fa31fb578056dba1e4e9ea440dd0ec53bc63 100644
--- a/app/code/Magento/Paypal/Block/Adminhtml/Store/SwitcherPlugin.php
+++ b/app/code/Magento/Paypal/Block/Adminhtml/Store/SwitcherPlugin.php
@@ -5,26 +5,28 @@
  */
 namespace Magento\Paypal\Block\Adminhtml\Store;
 
+use Magento\Backend\Block\Store\Switcher as StoreSwitcherBlock;
+use Magento\Paypal\Model\Config\StructurePlugin as ConfigStructurePlugin;
+
+/**
+ * Plugin for \Magento\Backend\Block\Store\Switcher
+ */
 class SwitcherPlugin
 {
     /**
      * Remove country request param from url
      *
-     * @param \Magento\Backend\Block\Store\Switcher $subject
-     * @param \Closure $proceed
+     * @param StoreSwitcherBlock $subject
      * @param string $route
      * @param array $params
-     * @return string
+     * @return array
      */
-    public function aroundGetUrl(
-        \Magento\Backend\Block\Store\Switcher $subject,
-        \Closure $proceed,
-        $route = '',
-        $params = []
-    ) {
-        if ($subject->getRequest()->getParam(\Magento\Paypal\Model\Config\StructurePlugin::REQUEST_PARAM_COUNTRY)) {
-            $params[\Magento\Paypal\Model\Config\StructurePlugin::REQUEST_PARAM_COUNTRY] = null;
+    public function beforeGetUrl(StoreSwitcherBlock $subject, $route = '', $params = [])
+    {
+        if ($subject->getRequest()->getParam(ConfigStructurePlugin::REQUEST_PARAM_COUNTRY)) {
+            $params[ConfigStructurePlugin::REQUEST_PARAM_COUNTRY] = null;
         }
-        return $proceed($route, $params);
+
+        return [$route, $params];
     }
 }
diff --git a/app/code/Magento/Paypal/Model/Config/Structure/Element/FieldPlugin.php b/app/code/Magento/Paypal/Model/Config/Structure/Element/FieldPlugin.php
index 2d49ddfcb3d01955fefd089052f4b444d9c91134..a6288b8fc685c45aa823e807433c1df896c5dc9f 100644
--- a/app/code/Magento/Paypal/Model/Config/Structure/Element/FieldPlugin.php
+++ b/app/code/Magento/Paypal/Model/Config/Structure/Element/FieldPlugin.php
@@ -5,39 +5,45 @@
  */
 namespace Magento\Paypal\Model\Config\Structure\Element;
 
+use Magento\Framework\App\RequestInterface;
+use Magento\Config\Model\Config\Structure\Element\Field as FieldConfigStructure;
+use Magento\Paypal\Model\Config\StructurePlugin as ConfigStructurePlugin;
+
+/**
+ * Plugin for \Magento\Config\Model\Config\Structure\Element\Field
+ */
 class FieldPlugin
 {
     /**
-     * @var \Magento\Framework\App\RequestInterface
+     * @var RequestInterface
      */
-    protected $_request;
+    private $request;
 
     /**
-     * @param \Magento\Framework\App\RequestInterface $request
+     * @param RequestInterface $request
      */
-    public function __construct(\Magento\Framework\App\RequestInterface $request)
+    public function __construct(RequestInterface $request)
     {
-        $this->_request = $request;
+        $this->request = $request;
     }
 
     /**
      * Get original configPath (not changed by PayPal configuration inheritance)
      *
-     * @param \Magento\Config\Model\Config\Structure\Element\Field $subject
-     * @param \Closure $proceed
+     * @param FieldConfigStructure $subject
+     * @param string|null $result
      * @return string|null
      */
-    public function aroundGetConfigPath(
-        \Magento\Config\Model\Config\Structure\Element\Field $subject,
-        \Closure $proceed
-    ) {
-        $configPath = $proceed();
-        if (!isset($configPath) && $this->_request->getParam('section') == 'payment') {
-            $configPath = preg_replace('@^(' . implode(
-                '|',
-                \Magento\Paypal\Model\Config\StructurePlugin::getPaypalConfigCountries(true)
-            ) . ')/@', 'payment/', $subject->getPath());
+    public function afterGetConfigPath(FieldConfigStructure $subject, $result)
+    {
+        if (!$result && $this->request->getParam('section') == 'payment') {
+            $result = preg_replace(
+                '@^(' . implode('|', ConfigStructurePlugin::getPaypalConfigCountries(true)) . ')/@',
+                'payment/',
+                $subject->getPath()
+            );
         }
-        return $configPath;
+
+        return $result;
     }
 }
diff --git a/app/code/Magento/Paypal/Model/Config/StructurePlugin.php b/app/code/Magento/Paypal/Model/Config/StructurePlugin.php
index 52f46a8411c0257d1f894ce0693d6b77d8dd35c0..f45a7948906253ec53a4dd710b37825363f39877 100644
--- a/app/code/Magento/Paypal/Model/Config/StructurePlugin.php
+++ b/app/code/Magento/Paypal/Model/Config/StructurePlugin.php
@@ -11,6 +11,9 @@ use Magento\Config\Model\Config\Structure\Element\Section;
 use Magento\Config\Model\Config\Structure\ElementInterface;
 use Magento\Paypal\Helper\Backend as BackendHelper;
 
+/**
+ * Plugin for \Magento\Config\Model\Config\Structure
+ */
 class StructurePlugin
 {
     /**
@@ -21,17 +24,17 @@ class StructurePlugin
     /**
      * @var BackendHelper
      */
-    protected $_helper;
+    private $backendHelper;
 
     /**
      * @var ScopeDefiner
      */
-    protected $_scopeDefiner;
+    private $scopeDefiner;
 
     /**
      * @var string[]
      */
-    private static $_paypalConfigCountries = [
+    private static $paypalConfigCountries = [
         'payment_us',
         'payment_ca',
         'payment_au',
@@ -49,12 +52,10 @@ class StructurePlugin
      * @param ScopeDefiner $scopeDefiner
      * @param BackendHelper $helper
      */
-    public function __construct(
-        ScopeDefiner $scopeDefiner,
-        BackendHelper $helper
-    ) {
-        $this->_scopeDefiner = $scopeDefiner;
-        $this->_helper = $helper;
+    public function __construct(ScopeDefiner $scopeDefiner, BackendHelper $helper)
+    {
+        $this->scopeDefiner = $scopeDefiner;
+        $this->backendHelper = $helper;
     }
 
     /**
@@ -65,10 +66,12 @@ class StructurePlugin
      */
     public static function getPaypalConfigCountries($addOther = false)
     {
-        $countries = self::$_paypalConfigCountries;
+        $countries = self::$paypalConfigCountries;
+
         if ($addOther) {
             $countries[] = 'payment_other';
         }
+
         return $countries;
     }
 
@@ -78,39 +81,45 @@ class StructurePlugin
      * @param Structure $subject
      * @param \Closure $proceed
      * @param array $pathParts
-     * @return ElementInterface
+     * @return ElementInterface|null
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundGetElementByPathParts(
-        Structure $subject,
-        \Closure $proceed,
-        array $pathParts
-    ) {
+    public function aroundGetElementByPathParts(Structure $subject, \Closure $proceed, array $pathParts)
+    {
         $isSectionChanged = $pathParts[0] == 'payment';
+
         if ($isSectionChanged) {
-            $requestedCountrySection = 'payment_' . strtolower($this->_helper->getConfigurationCountryCode());
+            $requestedCountrySection = 'payment_' . strtolower($this->backendHelper->getConfigurationCountryCode());
+
             if (in_array($requestedCountrySection, self::getPaypalConfigCountries())) {
                 $pathParts[0] = $requestedCountrySection;
             } else {
                 $pathParts[0] = 'payment_other';
             }
         }
-        /** @var ElementInterface $result */
+
         $result = $proceed($pathParts);
-        if ($isSectionChanged && isset($result)) {
+
+        if ($isSectionChanged && $result) {
             if ($result instanceof Section) {
                 $this->restructurePayments($result);
-                $result->setData(array_merge(
-                    $result->getData(),
-                    ['showInDefault' => true, 'showInWebsite' => true, 'showInStore' => true]
-                ), $this->_scopeDefiner->getScope());
+                $result->setData(
+                    array_merge(
+                        $result->getData(),
+                        ['showInDefault' => true, 'showInWebsite' => true, 'showInStore' => true]
+                    ),
+                    $this->scopeDefiner->getScope()
+                );
             }
         }
+
         return $result;
     }
 
     /**
-     * Changes payment config structure.
+     * Change payment config structure
+     *
      * Groups which have `displayIn` element, transfer to appropriate group.
      * Groups without `displayIn` transfer to other payment methods group.
      *
@@ -139,7 +148,7 @@ class StructurePlugin
         }
 
         $configuration['children'] = $sectionMap;
-        $result->setData($configuration, $this->_scopeDefiner->getScope());
+        $result->setData($configuration, $this->scopeDefiner->getScope());
     }
 
     /**
diff --git a/app/code/Magento/Paypal/Model/Method/Checks/SpecificationPlugin.php b/app/code/Magento/Paypal/Model/Method/Checks/SpecificationPlugin.php
index a171cd5628f0fb90dcb57ec521d5c47533f9ccc7..f2b650b95e9e8cdfaa86f2bf7e398f9fad74abb2 100644
--- a/app/code/Magento/Paypal/Model/Method/Checks/SpecificationPlugin.php
+++ b/app/code/Magento/Paypal/Model/Method/Checks/SpecificationPlugin.php
@@ -11,49 +11,54 @@ use Magento\Paypal\Model\Config;
 use Magento\Paypal\Model\Billing\AgreementFactory;
 use Magento\Quote\Model\Quote;
 
+/**
+ * Plugin for \Magento\Payment\Model\Checks\Composite
+ */
 class SpecificationPlugin
 {
     /**
      * @var AgreementFactory
      */
-    protected $_agreementFactory;
+    private $agreementFactory;
 
     /**
      * @param AgreementFactory $agreementFactory
      */
     public function __construct(AgreementFactory $agreementFactory)
     {
-        $this->_agreementFactory = $agreementFactory;
+        $this->agreementFactory = $agreementFactory;
     }
 
     /**
      * Override check for Billing Agreements
      *
      * @param SpecificationInterface $specification
-     * @param \Closure $proceed
+     * @param bool $result
      * @param MethodInterface $paymentMethod
      * @param Quote $quote
      * @return bool
+     *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundIsApplicable(
+    public function afterIsApplicable(
         SpecificationInterface $specification,
-        \Closure $proceed,
+        $result,
         MethodInterface $paymentMethod,
         Quote $quote
     ) {
-        $originallyIsApplicable = $proceed($paymentMethod, $quote);
-        if (!$originallyIsApplicable) {
+        if (!$result) {
             return false;
         }
 
         if ($paymentMethod->getCode() == Config::METHOD_BILLING_AGREEMENT) {
             if ($quote->getCustomerId()) {
-                $availableBA = $this->_agreementFactory->create()->getAvailableCustomerBillingAgreements(
+                $availableBA = $this->agreementFactory->create()->getAvailableCustomerBillingAgreements(
                     $quote->getCustomerId()
                 );
+
                 return count($availableBA) > 0;
             }
+
             return false;
         }
 
diff --git a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/Store/SwitcherPluginTest.php b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/Store/SwitcherPluginTest.php
index 320e89d567b9cac8155c45885671d20ed569ef99..a97fb0ad2a6fcc5723635b980571559e82742e70 100644
--- a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/Store/SwitcherPluginTest.php
+++ b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/Store/SwitcherPluginTest.php
@@ -5,42 +5,72 @@
  */
 namespace Magento\Paypal\Test\Unit\Block\Adminhtml\Store;
 
+use Magento\Paypal\Block\Adminhtml\Store\SwitcherPlugin as StoreSwitcherBlockPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Backend\Block\Store\Switcher as StoreSwitcherBlock;
+use Magento\Framework\App\RequestInterface;
+use Magento\Paypal\Model\Config\StructurePlugin as ConfigStructurePlugin;
+
 class SwitcherPluginTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var SwitcherPlugin
+     * @var StoreSwitcherBlockPlugin
+     */
+    private $plugin;
+
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var StoreSwitcherBlock|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $_model;
+    private $subjectMock;
+
+    /**
+     * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $requestMock;
 
     protected function setUp()
     {
-        $this->_model = new \Magento\Paypal\Block\Adminhtml\Store\SwitcherPlugin();
+        $this->subjectMock = $this->getMockBuilder(StoreSwitcherBlock::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->requestMock = $this->getMockBuilder(RequestInterface::class)
+            ->getMockForAbstractClass();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(StoreSwitcherBlockPlugin::class);
     }
 
     /**
-     * @param null|string $countryParam
+     * @param string|null $countryParam
      * @param array $getUrlParams
-     * @dataProvider aroundGetUrlDataProvider
+     *
+     * @dataProvider beforeGetUrlDataProvider
      */
-    public function testAroundGetUrl($countryParam, $getUrlParams)
+    public function testBeforeGetUrl($countryParam, $getUrlParams)
     {
-        $subjectRequest = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class);
-        $subjectRequest->expects($this->once())
+        $this->requestMock->expects(static::once())
             ->method('getParam')
-            ->with(\Magento\Paypal\Model\Config\StructurePlugin::REQUEST_PARAM_COUNTRY)
-            ->will($this->returnValue($countryParam));
-        $subject = $this->getMock(\Magento\Backend\Block\Store\Switcher::class, ['getRequest'], [], '', false);
-        $subject->expects($this->any())->method('getRequest')->will($this->returnValue($subjectRequest));
-        $getUrl = function ($route, $params) {
-            return [$route, $params];
-        };
-        $this->assertEquals(['', $getUrlParams], $this->_model->aroundGetUrl($subject, $getUrl, '', []));
+            ->with(ConfigStructurePlugin::REQUEST_PARAM_COUNTRY)
+            ->willReturn($countryParam);
+        $this->subjectMock->expects(static::any())
+            ->method('getRequest')
+            ->willReturn($this->requestMock);
+
+        $this->assertEquals(['', $getUrlParams], $this->plugin->beforeGetUrl($this->subjectMock, '', []));
     }
 
-    public function aroundGetUrlDataProvider()
+    /**
+     * @return array
+     */
+    public function beforeGetUrlDataProvider()
     {
         return [
-            ['any value', [\Magento\Paypal\Model\Config\StructurePlugin::REQUEST_PARAM_COUNTRY => null]],
+            ['any value', [ConfigStructurePlugin::REQUEST_PARAM_COUNTRY => null]],
             [null, []]
         ];
     }
diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php
index e630e3c57207c113e502da623dd77f116de6bc53..48314c28ceb983b5ca660aa6e3e34345cc0d1cd3 100644
--- a/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php
+++ b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php
@@ -3,80 +3,96 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\Paypal\Test\Unit\Model\Config\Structure\Element;
 
+use Magento\Paypal\Model\Config\Structure\Element\FieldPlugin as FieldConfigStructurePlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Framework\App\RequestInterface;
+use Magento\Config\Model\Config\Structure\Element\Field as FieldConfigStructureMock;
+
 class FieldPluginTest extends \PHPUnit_Framework_TestCase
 {
-    /** @var FieldPlugin */
-    protected $model;
+    /**
+     * @var FieldConfigStructurePlugin
+     */
+    private $plugin;
 
-    /** @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject */
-    protected $request;
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $requestMock;
 
-    /** @var  \Magento\Config\Model\Config\Structure\Element\Field|\PHPUnit_Framework_MockObject_MockObject */
-    protected $subject;
+    /**
+     * @var FieldConfigStructureMock|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
 
     protected function setUp()
     {
-        $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class);
-        $this->subject = $this->getMock(\Magento\Config\Model\Config\Structure\Element\Field::class, [], [], '', false);
+        $this->requestMock = $this->getMockBuilder(RequestInterface::class)
+            ->getMockForAbstractClass();
+        $this->subjectMock = $this->getMockBuilder(FieldConfigStructureMock::class)
+            ->disableOriginalConstructor()
+            ->getMock();
 
-        $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-        $this->model = $helper->getObject(
-            \Magento\Paypal\Model\Config\Structure\Element\FieldPlugin::class,
-            ['request' => $this->request]
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            FieldConfigStructurePlugin::class,
+            ['request' => $this->requestMock]
         );
     }
 
     public function testAroundGetConfigPathHasResult()
     {
         $someResult = 'some result';
-        $callback = function () use ($someResult) {
-            return $someResult;
-        };
-        $this->assertEquals($someResult, $this->model->aroundGetConfigPath($this->subject, $callback));
+
+        $this->assertEquals($someResult, $this->plugin->afterGetConfigPath($this->subjectMock, $someResult));
     }
 
     public function testAroundGetConfigPathNonPaymentSection()
     {
-        $callback = function () {
-            return null;
-        };
-        $this->request->expects($this->once())
+        $this->requestMock->expects(static::once())
             ->method('getParam')
             ->with('section')
-            ->will($this->returnValue('non-payment'));
-        $this->assertNull($this->model->aroundGetConfigPath($this->subject, $callback));
+            ->willReturn('non-payment');
+
+        $this->assertNull($this->plugin->afterGetConfigPath($this->subjectMock, null));
     }
 
     /**
      * @param string $subjectPath
      * @param string $expectedConfigPath
-     * @dataProvider aroundGetConfigPathDataProvider
+     *
+     * @dataProvider afterGetConfigPathDataProvider
      */
     public function testAroundGetConfigPath($subjectPath, $expectedConfigPath)
     {
-        $callback = function () {
-            return null;
-        };
-        $this->request->expects($this->once())
+        $this->requestMock->expects(static::once())
             ->method('getParam')
             ->with('section')
-            ->will($this->returnValue('payment'));
-        $this->subject->expects($this->once())
+            ->willReturn('payment');
+        $this->subjectMock->expects(static::once())
             ->method('getPath')
-            ->will($this->returnValue($subjectPath));
-        $this->assertEquals($expectedConfigPath, $this->model->aroundGetConfigPath($this->subject, $callback));
+            ->willReturn($subjectPath);
+
+        $this->assertEquals($expectedConfigPath, $this->plugin->afterGetConfigPath($this->subjectMock, null));
     }
 
-    public function aroundGetConfigPathDataProvider()
+    /**
+     * @return array
+     */
+    public function afterGetConfigPathDataProvider()
     {
         return [
             ['payment_us/group/field', 'payment/group/field'],
             ['payment_other/group/field', 'payment/group/field'],
             ['payment_us', 'payment_us'],
-            ['payment_wrong_country/group/field', 'payment_wrong_country/group/field'],
+            ['payment_wrong_country/group/field', 'payment_wrong_country/group/field']
         ];
     }
 }
diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Config/StructurePluginTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Config/StructurePluginTest.php
index 4479f0a1233515039e499f73cc4595c5369007f7..63abcd660425abd7173fbd2528e047498c7cce62 100644
--- a/app/code/Magento/Paypal/Test/Unit/Model/Config/StructurePluginTest.php
+++ b/app/code/Magento/Paypal/Test/Unit/Model/Config/StructurePluginTest.php
@@ -3,45 +3,80 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\Paypal\Test\Unit\Model\Config;
 
-use Magento\Paypal\Model\Config\StructurePlugin;
+use Magento\Paypal\Model\Config\StructurePlugin as ConfigStructurePlugin;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Config\Model\Config\ScopeDefiner as ConfigScopeDefiner;
+use Magento\Paypal\Helper\Backend as BackendHelper;
+use Magento\Config\Model\Config\Structure as ConfigStructure;
+use Magento\Config\Model\Config\Structure\ElementInterface as ElementConfigStructure;
 
 class StructurePluginTest extends \PHPUnit_Framework_TestCase
 {
-    /** @var \Magento\Paypal\Model\Config\StructurePlugin */
-    protected $_model;
+    /**
+     * @var ConfigStructurePlugin
+     */
+    private $plugin;
+
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var ConfigScopeDefiner|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $configScopeDefinerMock;
+
+    /**
+     * @var BackendHelper|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $backendHelperMock;
 
-    /** @var \Magento\Config\Model\Config\ScopeDefiner|\PHPUnit_Framework_MockObject_MockObject */
-    protected $_scopeDefiner;
+    /**
+     * @var ConfigStructure|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $configStructureMock;
 
-    /** @var \Magento\Paypal\Helper\Backend|\PHPUnit_Framework_MockObject_MockObject */
-    protected $_helper;
+    /**
+     * @var ElementConfigStructure|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $elementConfigStructureMock;
 
     protected function setUp()
     {
-        $this->_scopeDefiner = $this->getMock(\Magento\Config\Model\Config\ScopeDefiner::class, [], [], '', false);
-        $this->_helper = $this->getMock(\Magento\Paypal\Helper\Backend::class, [], [], '', false);
-
-        $objectManagerHelper = new ObjectManagerHelper($this);
-        $this->_model = $objectManagerHelper->getObject(
-            \Magento\Paypal\Model\Config\StructurePlugin::class,
-            ['scopeDefiner' => $this->_scopeDefiner, 'helper' => $this->_helper]
+        $this->configScopeDefinerMock = $this->getMockBuilder(ConfigScopeDefiner::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->backendHelperMock = $this->getMockBuilder(BackendHelper::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->configStructureMock = $this->getMockBuilder(ConfigStructure::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->elementConfigStructureMock = $this->getMockBuilder(ElementConfigStructure::class)
+            ->getMockForAbstractClass();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            ConfigStructurePlugin::class,
+            ['scopeDefiner' => $this->configScopeDefinerMock, 'helper' => $this->backendHelperMock]
         );
     }
 
     public function testGetPaypalConfigCountriesWithOther()
     {
-        $countries = StructurePlugin::getPaypalConfigCountries(true);
+        $countries = ConfigStructurePlugin::getPaypalConfigCountries(true);
+
         $this->assertContains('payment_us', $countries);
         $this->assertContains('payment_other', $countries);
     }
 
     public function testGetPaypalConfigCountries()
     {
-        $countries = StructurePlugin::getPaypalConfigCountries(false);
+        $countries = ConfigStructurePlugin::getPaypalConfigCountries(false);
+
         $this->assertContains('payment_us', $countries);
         $this->assertNotContains('payment_other', $countries);
     }
@@ -49,20 +84,25 @@ class StructurePluginTest extends \PHPUnit_Framework_TestCase
     /**
      * @param array $pathParts
      * @param bool $returnResult
+     *
      * @dataProvider aroundGetElementByPathPartsNonPaymentDataProvider
      */
     public function testAroundGetElementByPathPartsNonPayment($pathParts, $returnResult)
     {
-        $result = $returnResult
-            ? $this->getMockForAbstractClass(\Magento\Config\Model\Config\Structure\ElementInterface::class)
-            : null;
-        $this->_aroundGetElementByPathPartsAssertResult(
+        $result = $returnResult ? $this->elementConfigStructureMock : null;
+        $proceed = function () use ($result) {
+            return $result;
+        };
+
+        $this->assertSame(
             $result,
-            $this->_getElementByPathPartsCallback($pathParts, $result),
-            $pathParts
+            $this->plugin->aroundGetElementByPathParts($this->configStructureMock, $proceed, $pathParts)
         );
     }
 
+    /**
+     * @return array
+     */
     public function aroundGetElementByPathPartsNonPaymentDataProvider()
     {
         return [
@@ -76,146 +116,62 @@ class StructurePluginTest extends \PHPUnit_Framework_TestCase
     /**
      * @param array $pathParts
      * @param string $countryCode
-     * @param array $expectedPathParts
+     *
      * @dataProvider aroundGetElementByPathPartsDataProvider
      */
-    public function testAroundGetElementByPathPartsNoResult($pathParts, $countryCode, $expectedPathParts)
+    public function testAroundGetElementByPathPartsNoResult($pathParts, $countryCode)
     {
-        $this->_getElementByPathPartsPrepareHelper($countryCode);
-        $this->_aroundGetElementByPathPartsAssertResult(
+        $proceed = function () {
+            return null;
+        };
+
+        $this->backendHelperMock->expects(static::once())
+            ->method('getConfigurationCountryCode')
+            ->willReturn($countryCode);
+
+        $this->assertEquals(
             null,
-            $this->_getElementByPathPartsCallback($expectedPathParts, null),
-            $pathParts
+            $this->plugin->aroundGetElementByPathParts($this->configStructureMock, $proceed, $pathParts)
         );
     }
 
     /**
      * @param array $pathParts
      * @param string $countryCode
-     * @param array $expectedPathParts
+     *
      * @dataProvider aroundGetElementByPathPartsDataProvider
      */
-    public function testAroundGetElementByPathParts($pathParts, $countryCode, $expectedPathParts)
+    public function testAroundGetElementByPathParts($pathParts, $countryCode)
     {
-        $this->_getElementByPathPartsPrepareHelper($countryCode);
-        $result = $this->getMockForAbstractClass(\Magento\Config\Model\Config\Structure\ElementInterface::class);
-        $this->_aroundGetElementByPathPartsAssertResult(
-            $result,
-            $this->_getElementByPathPartsCallback($expectedPathParts, $result),
-            $pathParts
+        $result = $this->elementConfigStructureMock;
+        $proceed = function () use ($result) {
+            return $result;
+        };
+
+        $this->backendHelperMock->expects(static::once())
+            ->method('getConfigurationCountryCode')
+            ->willReturn($countryCode);
+
+        $this->assertSame(
+            $this->elementConfigStructureMock,
+            $this->plugin->aroundGetElementByPathParts($this->configStructureMock, $proceed, $pathParts)
         );
     }
 
+    /**
+     * @return array
+     */
     public function aroundGetElementByPathPartsDataProvider()
     {
         return [
             [
                 ['payment', 'group1', 'group2', 'field'],
                 'any',
-                ['payment_other', 'group1', 'group2', 'field'],
             ],
             [
                 ['payment', 'group1', 'group2', 'field'],
                 'DE',
-                ['payment_de', 'group1', 'group2', 'field']
-            ],
+            ]
         ];
     }
-
-    /**
-     * @param array $pathParts
-     * @param string $countryCode
-     * @param array $expectedPathParts
-     * @dataProvider aroundGetSectionByPathPartsDataProvider
-     */
-    public function testAroundGetSectionByPathParts($pathParts, $countryCode, $expectedPathParts)
-    {
-        $this->_getElementByPathPartsPrepareHelper($countryCode);
-        $result = $this->getMock(\Magento\Config\Model\Config\Structure\Element\Section::class, [], [], '', false);
-        $self = $this;
-        $getElementByPathParts = function ($pathParts) use ($self, $expectedPathParts, $result) {
-            $self->assertEquals($expectedPathParts, $pathParts);
-            $scope = 'any scope';
-            $sectionMap = [
-                'account' => [],
-                'recommended_solutions' => [],
-                'other_paypal_payment_solutions' => [],
-                'other_payment_methods' => []
-            ];
-            $self->_scopeDefiner->expects($self->any())
-                ->method('getScope')
-                ->will($self->returnValue($scope));
-            $result->expects($self->at(0))
-                ->method('getData')
-                ->will($self->returnValue(['children' => []]));
-            $result->expects($self->at(2))
-                ->method('getData')
-                ->will($self->returnValue(['children' => $sectionMap]));
-            $result->expects($self->at(1))
-                ->method('setData')
-                ->with(['children' => $sectionMap], $scope)
-                ->will($self->returnSelf());
-            $result->expects($self->at(3))
-                ->method('setData')
-                ->with(['children' => $sectionMap,
-                    'showInDefault' => true,
-                    'showInWebsite' => true,
-                    'showInStore' => true], $scope)
-                ->will($self->returnSelf());
-            return $result;
-        };
-        $this->_aroundGetElementByPathPartsAssertResult($result, $getElementByPathParts, $pathParts);
-    }
-
-    public function aroundGetSectionByPathPartsDataProvider()
-    {
-        return [
-            [['payment'], 'GB', ['payment_gb']],
-            [['payment'], 'any', ['payment_other']],
-        ];
-    }
-
-    /**
-     * Assert result of aroundGetElementByPathParts method
-     *
-     * @param \PHPUnit_Framework_MockObject_MockObject|null $result
-     * @param \Closure $getElementByPathParts
-     * @param array $pathParts
-     */
-    private function _aroundGetElementByPathPartsAssertResult($result, $getElementByPathParts, $pathParts)
-    {
-        $this->assertEquals($result, $this->_model->aroundGetElementByPathParts(
-            $this->getMock(\Magento\Config\Model\Config\Structure::class, [], [], '', false),
-            $getElementByPathParts,
-            $pathParts
-        ));
-    }
-
-    /**
-     * Get callback for aroundGetElementByPathParts method
-     *
-     * @param array $expectedPathParts
-     * @param \PHPUnit_Framework_MockObject_MockObject|null $result
-     * @return \Closure
-     */
-    private function _getElementByPathPartsCallback($expectedPathParts, $result)
-    {
-        $self = $this;
-        return function ($pathParts) use ($self, $expectedPathParts, $result) {
-            $self->assertEquals($expectedPathParts, $pathParts);
-            return $result;
-        };
-    }
-
-    /**
-     * Prepare helper for test
-     *
-     * @param string $countryCode
-     */
-    private function _getElementByPathPartsPrepareHelper($countryCode)
-    {
-        $this->_helper->expects($this->once())
-            ->method('getConfigurationCountryCode')
-            ->will($this->returnValue($countryCode));
-    }
 }
diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Method/Checks/SpecificationPluginTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Method/Checks/SpecificationPluginTest.php
index eb8eb1091b083d70d0ee7b1c69d26da87525d113..0994088d84a259f3596065e1ffdc9bb423110b52 100644
--- a/app/code/Magento/Paypal/Test/Unit/Model/Method/Checks/SpecificationPluginTest.php
+++ b/app/code/Magento/Paypal/Test/Unit/Model/Method/Checks/SpecificationPluginTest.php
@@ -3,155 +3,179 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\Paypal\Test\Unit\Model\Method\Checks;
 
+use Magento\Paypal\Model\Method\Checks\SpecificationPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Paypal\Model\Billing\AgreementFactory as BillingAgreementFactory;
+use Magento\Payment\Model\Checks\SpecificationInterface;
 use Magento\Payment\Model\MethodInterface;
-use Magento\Paypal\Model\Billing\AgreementFactory;
 use Magento\Quote\Model\Quote;
-use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Paypal\Model\ResourceModel\Billing\Agreement\Collection as BillingAgreementCollection;
+use Magento\Paypal\Model\Billing\Agreement as BillingAgreement;
 
 class SpecificationPluginTest extends \PHPUnit_Framework_TestCase
 {
-    /** @var SpecificationPlugin */
-    protected $model;
+    /**
+     * @var SpecificationPlugin
+     */
+    private $plugin;
+
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
 
-    /** @var AgreementFactory|\PHPUnit_Framework_MockObject_MockObject */
-    protected $agreementFactory;
+    /**
+     * @var BillingAgreementFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $billingAgreementFactoryMock;
+
+    /**
+     * @var SpecificationInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $specificationMock;
+
+    /**
+     * @var MethodInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $paymentMethodMock;
+
+    /**
+     * @var Quote|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $quoteMock;
+
+    /**
+     * @var BillingAgreementCollection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $billingAgreementCollectionMock;
+
+    /**
+     * @var BillingAgreement|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $billingAgreementMock;
 
     protected function setUp()
     {
-        $this->agreementFactory = $this->getMockBuilder(\Magento\Paypal\Model\Billing\AgreementFactory::class)
+        $this->billingAgreementFactoryMock = $this->getMockBuilder(BillingAgreementFactory::class)
             ->disableOriginalConstructor()
             ->setMethods(['create'])
             ->getMock();
+        $this->specificationMock = $this->getMockBuilder(SpecificationInterface::class)
+            ->getMockForAbstractClass();
+        $this->paymentMethodMock = $this->getMockBuilder(MethodInterface::class)
+            ->getMockForAbstractClass();
+        $this->quoteMock = $this->getMockBuilder(Quote::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getCustomerId'])
+            ->getMock();
+        $this->billingAgreementCollectionMock = $this->getMockBuilder(BillingAgreementCollection::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->billingAgreementMock = $this->getMockBuilder(BillingAgreement::class)
+            ->disableOriginalConstructor()
+            ->getMock();
 
-        $objectManagerHelper = new ObjectManagerHelper($this);
-        $this->model = $objectManagerHelper->getObject(
-            \Magento\Paypal\Model\Method\Checks\SpecificationPlugin::class,
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            SpecificationPlugin::class,
             [
-                'agreementFactory' => $this->agreementFactory
+                'agreementFactory' => $this->billingAgreementFactoryMock
             ]
         );
     }
 
-    public function testAroundIsApplicableNotOriginallyApplicable()
+    public function testAfterIsApplicableNotOriginallyApplicable()
     {
-        $paymentMethod = $this->getPaymentMethod('any');
-        $quote = $this->getQuote('any');
-        $proceed = $this->getProceedClosure(false, $paymentMethod, $quote);
-        $this->assertFalse($this->callAroundIsApplicable($proceed, $paymentMethod, $quote));
+        $this->setExpectations('any', 'any');
+
+        $this->assertFalse(
+            $this->plugin->afterIsApplicable(
+                $this->specificationMock,
+                false,
+                $this->paymentMethodMock,
+                $this->quoteMock
+            )
+        );
     }
 
-    public function testAroundIsApplicableNotAgreement()
+    public function testAfterIsApplicableNotAgreement()
     {
-        $paymentMethod = $this->getPaymentMethod('not_agreement');
-        $quote = $this->getQuote('any');
-        $proceed = $this->getProceedClosure(true, $paymentMethod, $quote);
-        $this->assertTrue($this->callAroundIsApplicable($proceed, $paymentMethod, $quote));
+        $this->setExpectations('not_agreement', 'any');
+
+        $this->assertTrue(
+            $this->plugin->afterIsApplicable(
+                $this->specificationMock,
+                true,
+                $this->paymentMethodMock,
+                $this->quoteMock
+            )
+        );
     }
 
-    public function testAroundIsApplicableNoCustomerId()
+    public function testAfterIsApplicableNoCustomerId()
     {
-        $paymentMethod = $this->getPaymentMethod('paypal_billing_agreement');
-        $quote = $this->getQuote(null);
-        $proceed = $this->getProceedClosure(true, $paymentMethod, $quote);
-        $this->assertFalse($this->callAroundIsApplicable($proceed, $paymentMethod, $quote));
+        $this->setExpectations('paypal_billing_agreement', null);
+
+        $this->assertFalse(
+            $this->plugin->afterIsApplicable(
+                $this->specificationMock,
+                true,
+                $this->paymentMethodMock,
+                $this->quoteMock
+            )
+        );
     }
 
     /**
      * @param int $count
-     * @dataProvider aroundIsApplicableDataProvider
+     *
+     * @dataProvider afterIsApplicableDataProvider
      */
-    public function testAroundIsApplicable($count)
+    public function testAfterIsApplicable($count)
     {
-        $paymentMethod = $this->getPaymentMethod('paypal_billing_agreement');
-        $quote = $this->getQuote(1);
-        $proceed = $this->getProceedClosure(true, $paymentMethod, $quote);
-        $agreementCollection = $this->getMock(
-            \Magento\Paypal\Model\ResourceModel\Billing\Agreement\Collection::class,
-            [],
-            [],
-            '',
-            false
-        );
-        $agreementCollection->expects($this->once())
-            ->method('count')
-            ->will($this->returnValue($count));
-        $agreement = $this->getMock(\Magento\Paypal\Model\Billing\Agreement::class, [], [], '', false);
-        $agreement->expects($this->once())
+        $this->setExpectations('paypal_billing_agreement', 1);
+
+        $this->billingAgreementFactoryMock->expects(static::once())
+            ->method('create')
+            ->willReturn($this->billingAgreementMock);
+        $this->billingAgreementMock->expects(static::once())
             ->method('getAvailableCustomerBillingAgreements')
             ->with(1)
-            ->will($this->returnValue($agreementCollection));
-        $this->agreementFactory->expects($this->once())
-            ->method('create')
-            ->will($this->returnValue($agreement));
-        $this->assertEquals($count > 0, $this->callAroundIsApplicable($proceed, $paymentMethod, $quote));
-    }
-
-    public function aroundIsApplicableDataProvider()
-    {
-        return [[0], [1], [2]];
-    }
+            ->willReturn($this->billingAgreementCollectionMock);
+        $this->billingAgreementCollectionMock->expects(static::once())
+            ->method('count')
+            ->willReturn($count);
 
-    /**
-     * @param bool $result
-     * @param MethodInterface $paymentMethod
-     * @param Quote $quote
-     * @return \Closure
-     */
-    private function getProceedClosure($result, MethodInterface $paymentMethod, Quote $quote)
-    {
-        $self = $this;
-        return function ($parameter1, $parameter2) use ($result, $paymentMethod, $quote, $self) {
-            $self->assertSame($paymentMethod, $parameter1);
-            $self->assertSame($quote, $parameter2);
-            return $result;
-        };
+        $this->assertEquals(
+            $count > 0,
+            $this->plugin->afterIsApplicable($this->specificationMock, true, $this->paymentMethodMock, $this->quoteMock)
+        );
     }
 
     /**
-     * Get payment method parameter
-     *
-     * @param string $code
-     * @return MethodInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @return array
      */
-    private function getPaymentMethod($code)
+    public function afterIsApplicableDataProvider()
     {
-        $paymentMethod = $this->getMockForAbstractClass(\Magento\Payment\Model\MethodInterface::class);
-        $paymentMethod->expects($this->any())
-            ->method('getCode')
-            ->will($this->returnValue($code));
-        return $paymentMethod;
+        return [[0], [1], [2]];
     }
 
     /**
-     * Get quote parameter
+     * Set expectations
      *
+     * @param mixed $paymentMethodCode
      * @param mixed $customerId
-     * @return Quote|\PHPUnit_Framework_MockObject_MockObject
+     * @return void
      */
-    private function getQuote($customerId)
+    private function setExpectations($paymentMethodCode, $customerId)
     {
-        $quote = $this->getMock(\Magento\Quote\Model\Quote::class, ['__wakeup'], [], '', false);
-        $quote->setCustomerId($customerId);
-        return $quote;
-    }
-
-    /**
-     * Call aroundIsApplicable method
-     *
-     * @param \Closure $proceed
-     * @param MethodInterface $paymentMethod
-     * @param Quote $quote
-     * @return bool
-     */
-    private function callAroundIsApplicable(
-        \Closure $proceed,
-        MethodInterface $paymentMethod,
-        Quote $quote
-    ) {
-        $specification = $this->getMockForAbstractClass(\Magento\Payment\Model\Checks\SpecificationInterface::class);
-        return $this->model->aroundIsApplicable($specification, $proceed, $paymentMethod, $quote);
+        $this->paymentMethodMock->expects(static::any())
+            ->method('getCode')
+            ->willReturn($paymentMethodCode);
+        $this->quoteMock->expects(static::any())
+            ->method('getCustomerId')
+            ->willReturn($customerId);
     }
 }
diff --git a/app/code/Magento/Sales/Api/Data/CommentInterface.php b/app/code/Magento/Sales/Api/Data/CommentInterface.php
index d7021dc9f9546b76f427a282a38a31d64f033775..fcab786319340535c1c6309c884a678a91341d90 100644
--- a/app/code/Magento/Sales/Api/Data/CommentInterface.php
+++ b/app/code/Magento/Sales/Api/Data/CommentInterface.php
@@ -12,6 +12,16 @@ namespace Magento\Sales\Api\Data;
  */
 interface CommentInterface
 {
+    /*
+     * Is-visible-on-storefront flag.
+     */
+    const IS_VISIBLE_ON_FRONT = 'is_visible_on_front';
+
+    /*
+     * Comment.
+     */
+    const COMMENT = 'comment';
+
     /**
      * Gets the comment for the invoice.
      *
diff --git a/app/code/Magento/Sales/Api/Data/EntityInterface.php b/app/code/Magento/Sales/Api/Data/EntityInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..d09b25920f899aa652f87dad269047136892a061
--- /dev/null
+++ b/app/code/Magento/Sales/Api/Data/EntityInterface.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Api\Data;
+
+/**
+ * Interface EntityInterface
+ * @api
+ */
+interface EntityInterface
+{
+    /*
+     * Entity ID.
+     */
+    const ENTITY_ID = 'entity_id';
+
+    /*
+     * Created-at timestamp.
+     */
+    const CREATED_AT = 'created_at';
+
+    /**
+     * Gets the created-at timestamp for the invoice.
+     *
+     * @return string|null Created-at timestamp.
+     */
+    public function getCreatedAt();
+
+    /**
+     * Sets the created-at timestamp for the invoice.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
+    /**
+     * Gets the ID for the invoice.
+     *
+     * @return int|null Invoice ID.
+     */
+    public function getEntityId();
+
+    /**
+     * Sets entity ID.
+     *
+     * @param int $entityId
+     * @return $this
+     */
+    public function setEntityId($entityId);
+}
diff --git a/app/code/Magento/Sales/Api/Data/InvoiceCommentCreationInterface.php b/app/code/Magento/Sales/Api/Data/InvoiceCommentCreationInterface.php
index 85c114da3821eb7cb0d98538c37513ddd10fed84..5e4d85e93cb98969580df87b71d7bcc9f6c916f6 100644
--- a/app/code/Magento/Sales/Api/Data/InvoiceCommentCreationInterface.php
+++ b/app/code/Magento/Sales/Api/Data/InvoiceCommentCreationInterface.php
@@ -6,11 +6,29 @@
 
 namespace Magento\Sales\Api\Data;
 
+use Magento\Framework\Api\ExtensibleDataInterface;
+
 /**
  * Interface InvoiceCommentCreationInterface
  *
  * @api
  */
-interface InvoiceCommentCreationInterface extends \Magento\Sales\Api\Data\CommentInterface
+interface InvoiceCommentCreationInterface extends ExtensibleDataInterface, CommentInterface
 {
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\InvoiceCommentCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\InvoiceCommentCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\InvoiceCommentCreationExtensionInterface $extensionAttributes
+    );
 }
diff --git a/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php b/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php
index 1447fab476ad9cad5a4f498450952324264b9eec..f661a1ab6a4ca1755f003304c9ff55ee287bba98 100644
--- a/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php
+++ b/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Sales\Api\Data;
 
+use Magento\Framework\Api\ExtensibleDataInterface;
+
 /**
  * Invoice comment interface.
  *
@@ -12,16 +14,11 @@ namespace Magento\Sales\Api\Data;
  * invoice history.
  * @api
  */
-interface InvoiceCommentInterface extends \Magento\Framework\Api\ExtensibleDataInterface,
-\Magento\Sales\Api\Data\InvoiceCommentCreationInterface
+interface InvoiceCommentInterface extends ExtensibleDataInterface, CommentInterface, EntityInterface
 {
     /**#@+
      * Constants for keys of data array. Identical to the name of the getter in snake case.
      */
-    /*
-     * Entity ID.
-     */
-    const ENTITY_ID = 'entity_id';
     /*
      * Parent ID.
      */
@@ -30,48 +27,6 @@ interface InvoiceCommentInterface extends \Magento\Framework\Api\ExtensibleDataI
      * Is-customer-notified flag.
      */
     const IS_CUSTOMER_NOTIFIED = 'is_customer_notified';
-    /*
-     * Is-visible-on-storefront flag.
-     */
-    const IS_VISIBLE_ON_FRONT = 'is_visible_on_front';
-    /*
-     * Comment.
-     */
-    const COMMENT = 'comment';
-    /*
-     * Created-at timestamp.
-     */
-    const CREATED_AT = 'created_at';
-
-    /**
-     * Gets the created-at timestamp for the invoice.
-     *
-     * @return string|null Created-at timestamp.
-     */
-    public function getCreatedAt();
-
-    /**
-     * Sets the created-at timestamp for the invoice.
-     *
-     * @param string $createdAt timestamp
-     * @return $this
-     */
-    public function setCreatedAt($createdAt);
-
-    /**
-     * Gets the ID for the invoice.
-     *
-     * @return int|null Invoice ID.
-     */
-    public function getEntityId();
-
-    /**
-     * Sets entity ID.
-     *
-     * @param int $entityId
-     * @return $this
-     */
-    public function setEntityId($entityId);
 
     /**
      * Gets the is-customer-notified flag value for the invoice.
diff --git a/app/code/Magento/Sales/Api/Data/InvoiceItemCreationInterface.php b/app/code/Magento/Sales/Api/Data/InvoiceItemCreationInterface.php
index 4b5c6fa2c6136f4d5896a418636d87632af5d603..2f2ddfbf758a0de4341b60b840263704bf6968ab 100644
--- a/app/code/Magento/Sales/Api/Data/InvoiceItemCreationInterface.php
+++ b/app/code/Magento/Sales/Api/Data/InvoiceItemCreationInterface.php
@@ -6,6 +6,8 @@
 
 namespace Magento\Sales\Api\Data;
 
+use Magento\Framework\Api\ExtensibleDataInterface;
+
 /**
  * Input argument for invoice creation
  *
@@ -13,6 +15,22 @@ namespace Magento\Sales\Api\Data;
  *
  * @api
  */
-interface InvoiceItemCreationInterface extends LineItemInterface
+interface InvoiceItemCreationInterface extends LineItemInterface, ExtensibleDataInterface
 {
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\InvoiceItemCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\InvoiceItemCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\InvoiceItemCreationExtensionInterface $extensionAttributes
+    );
 }
diff --git a/app/code/Magento/Sales/Api/Data/InvoiceItemInterface.php b/app/code/Magento/Sales/Api/Data/InvoiceItemInterface.php
index ecdcd07bc0ca5b90f4970dcffbfcbcdd3757774d..37a933f97a29ab26a192735e808c031ede534900 100644
--- a/app/code/Magento/Sales/Api/Data/InvoiceItemInterface.php
+++ b/app/code/Magento/Sales/Api/Data/InvoiceItemInterface.php
@@ -5,14 +5,15 @@
  */
 namespace Magento\Sales\Api\Data;
 
+use Magento\Framework\Api\ExtensibleDataInterface;
+
 /**
  * Invoice item interface.
  *
  * An invoice is a record of the receipt of payment for an order. An invoice item is a purchased item in an invoice.
  * @api
  */
-interface InvoiceItemInterface extends \Magento\Sales\Api\Data\InvoiceItemCreationInterface,
-\Magento\Framework\Api\ExtensibleDataInterface
+interface InvoiceItemInterface extends ExtensibleDataInterface, LineItemInterface
 {
     /**#@+
      * Constants for keys of data array. Identical to the name of the getter in snake case.
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentCommentCreationInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentCommentCreationInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..40beaa32d02f25c57af5fe93cbfd3e3f6d97e93f
--- /dev/null
+++ b/app/code/Magento/Sales/Api/Data/ShipmentCommentCreationInterface.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Api\Data;
+
+use Magento\Framework\Api\ExtensibleDataInterface;
+
+/**
+ * Interface ShipmentCommentCreationInterface
+ * @api
+ */
+interface ShipmentCommentCreationInterface extends ExtensibleDataInterface, CommentInterface
+{
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\ShipmentCommentCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\ShipmentCommentCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentCommentCreationExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php
index a67b0577a7761ee72bc1fad3c9fda679f31aa27e..0c00bf80d78f1d4f0311f7331a3639eec3e30bba 100644
--- a/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php
+++ b/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Sales\Api\Data;
 
+use Magento\Framework\Api\ExtensibleDataInterface;
+
 /**
  * Shipment comment interface.
  *
@@ -12,15 +14,11 @@ namespace Magento\Sales\Api\Data;
  * document lists the products and their quantities in the delivery package. A shipment document can contain comments.
  * @api
  */
-interface ShipmentCommentInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+interface ShipmentCommentInterface extends ExtensibleDataInterface, CommentInterface, EntityInterface
 {
     /**#@+
      * Constants for keys of data array. Identical to the name of the getter in snake case.
      */
-    /*
-     * Entity ID.
-     */
-    const ENTITY_ID = 'entity_id';
     /*
      * Parent ID.
      */
@@ -29,55 +27,6 @@ interface ShipmentCommentInterface extends \Magento\Framework\Api\ExtensibleData
      * Is-customer-notified flag.
      */
     const IS_CUSTOMER_NOTIFIED = 'is_customer_notified';
-    /*
-     * Is-visible-on-storefront flag.
-     */
-    const IS_VISIBLE_ON_FRONT = 'is_visible_on_front';
-    /*
-     * Comment.
-     */
-    const COMMENT = 'comment';
-    /*
-     * Created-at timestamp.
-     */
-    const CREATED_AT = 'created_at';
-
-    /**
-     * Gets the comment for the shipment.
-     *
-     * @return string Comment.
-     */
-    public function getComment();
-
-    /**
-     * Gets the created-at timestamp for the shipment comment.
-     *
-     * @return string|null Created-at timestamp.
-     */
-    public function getCreatedAt();
-
-    /**
-     * Sets the created-at timestamp for the shipment comment.
-     *
-     * @param string $createdAt timestamp
-     * @return $this
-     */
-    public function setCreatedAt($createdAt);
-
-    /**
-     * Gets the ID for the shipment comment.
-     *
-     * @return int|null Shipment comment ID.
-     */
-    public function getEntityId();
-
-    /**
-     * Sets entity ID.
-     *
-     * @param int $entityId
-     * @return $this
-     */
-    public function setEntityId($entityId);
 
     /**
      * Gets the is-customer-notified flag value for the shipment comment.
@@ -86,13 +35,6 @@ interface ShipmentCommentInterface extends \Magento\Framework\Api\ExtensibleData
      */
     public function getIsCustomerNotified();
 
-    /**
-     * Gets the is-visible-on-storefront flag value for the shipment comment.
-     *
-     * @return int Is-visible-on-storefront flag value.
-     */
-    public function getIsVisibleOnFront();
-
     /**
      * Gets the parent ID for the shipment comment.
      *
@@ -116,22 +58,6 @@ interface ShipmentCommentInterface extends \Magento\Framework\Api\ExtensibleData
      */
     public function setIsCustomerNotified($isCustomerNotified);
 
-    /**
-     * Sets the is-visible-on-storefront flag value for the shipment comment.
-     *
-     * @param int $isVisibleOnFront
-     * @return $this
-     */
-    public function setIsVisibleOnFront($isVisibleOnFront);
-
-    /**
-     * Sets the comment for the shipment.
-     *
-     * @param string $comment
-     * @return $this
-     */
-    public function setComment($comment);
-
     /**
      * Retrieve existing extension attributes object or create a new one.
      *
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentCreationArgumentsInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentCreationArgumentsInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..f03d05be3c7426bf28d16a7976957b087c432b0d
--- /dev/null
+++ b/app/code/Magento/Sales/Api/Data/ShipmentCreationArgumentsInterface.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Api\Data;
+
+/**
+ * Interface for creation arguments for Shipment.
+ *
+ * @api
+ */
+interface ShipmentCreationArgumentsInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+{
+    /**
+     * Gets existing extension attributes.
+     *
+     * @return \Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Sets extension attributes.
+     *
+     * @param \Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface $extensionAttributes
+     *
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentItemCreationInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentItemCreationInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..57c908fb88c5e9aa5e1dd7dbc6e8c39449d2217d
--- /dev/null
+++ b/app/code/Magento/Sales/Api/Data/ShipmentItemCreationInterface.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Sales\Api\Data;
+
+/**
+ * Input argument for shipment item creation
+ *
+ * Interface ShipmentItemCreationInterface
+ *
+ * @api
+ */
+interface ShipmentItemCreationInterface extends LineItemInterface,
+\Magento\Framework\Api\ExtensibleDataInterface
+{
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\ShipmentItemCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\ShipmentItemCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentItemCreationExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentItemInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentItemInterface.php
index 03f8fba0b431cbb39be5642aede49993bb8d202c..6d891371d04a14be86c4740b70a4838ccbc5a8cb 100644
--- a/app/code/Magento/Sales/Api/Data/ShipmentItemInterface.php
+++ b/app/code/Magento/Sales/Api/Data/ShipmentItemInterface.php
@@ -12,7 +12,8 @@ namespace Magento\Sales\Api\Data;
  * document lists the products and their quantities in the delivery package. A product is an item in a shipment.
  * @api
  */
-interface ShipmentItemInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+interface ShipmentItemInterface extends \Magento\Sales\Api\Data\LineItemInterface,
+\Magento\Framework\Api\ExtensibleDataInterface
 {
     /**#@+
      * Constants for keys of data array. Identical to the name of the getter in snake case
@@ -102,13 +103,6 @@ interface ShipmentItemInterface extends \Magento\Framework\Api\ExtensibleDataInt
      */
     public function getName();
 
-    /**
-     * Gets the order item ID for the shipment item.
-     *
-     * @return int Order item ID.
-     */
-    public function getOrderItemId();
-
     /**
      * Gets the parent ID for the shipment item.
      *
@@ -130,13 +124,6 @@ interface ShipmentItemInterface extends \Magento\Framework\Api\ExtensibleDataInt
      */
     public function getProductId();
 
-    /**
-     * Gets the quantity for the shipment item.
-     *
-     * @return float Quantity.
-     */
-    public function getQty();
-
     /**
      * Gets the row total for the shipment item.
      *
@@ -190,14 +177,6 @@ interface ShipmentItemInterface extends \Magento\Framework\Api\ExtensibleDataInt
      */
     public function setWeight($weight);
 
-    /**
-     * Sets the quantity for the shipment item.
-     *
-     * @param float $qty
-     * @return $this
-     */
-    public function setQty($qty);
-
     /**
      * Sets the product ID for the shipment item.
      *
@@ -206,14 +185,6 @@ interface ShipmentItemInterface extends \Magento\Framework\Api\ExtensibleDataInt
      */
     public function setProductId($id);
 
-    /**
-     * Sets the order item ID for the shipment item.
-     *
-     * @param int $id
-     * @return $this
-     */
-    public function setOrderItemId($id);
-
     /**
      * Sets the additional data for the shipment item.
      *
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentPackageCreationInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentPackageCreationInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..c83c54b3019bec5dfe507c97fe82bab02dc8f96b
--- /dev/null
+++ b/app/code/Magento/Sales/Api/Data/ShipmentPackageCreationInterface.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Api\Data;
+
+/**
+ * Shipment package interface.
+ *
+ * A shipment is a delivery package that contains products. A shipment document accompanies the shipment. This
+ * document lists the products and their quantities in the delivery package.
+ * @api
+ */
+interface ShipmentPackageCreationInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+{
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\ShipmentPackageCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\ShipmentPackageCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentPackageCreationExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentTrackCreationInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentTrackCreationInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..6a45242fcd1568275db8727b64e83a22fdbdd73f
--- /dev/null
+++ b/app/code/Magento/Sales/Api/Data/ShipmentTrackCreationInterface.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Api\Data;
+
+use Magento\Framework\Api\ExtensibleDataInterface;
+
+/**
+ * Shipment Track Creation interface.
+ *
+ * @api
+ */
+interface ShipmentTrackCreationInterface extends TrackInterface, ExtensibleDataInterface
+{
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\ShipmentTrackCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\ShipmentTrackCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentTrackCreationExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php
index f28a0b0e7a1970a05dfeef5739238966b1503182..beafa1370f107e1ba725568b893b4d390d9469b5 100644
--- a/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php
+++ b/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Sales\Api\Data;
 
+use Magento\Framework\Api\ExtensibleDataInterface;
+
 /**
  * Shipment track interface.
  *
@@ -13,7 +15,7 @@ namespace Magento\Sales\Api\Data;
  * shipments.
  * @api
  */
-interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+interface ShipmentTrackInterface extends TrackInterface, ExtensibleDataInterface
 {
     /**#@+
      * Constants for keys of data array. Identical to the name of the getter in snake case.
@@ -64,11 +66,19 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn
     const UPDATED_AT = 'updated_at';
 
     /**
-     * Gets the carrier code for the shipment package.
+     * Sets the order_id for the shipment package.
      *
-     * @return string Carrier code.
+     * @param int $id
+     * @return $this
      */
-    public function getCarrierCode();
+    public function setOrderId($id);
+
+    /**
+     * Gets the order_id for the shipment package.
+     *
+     * @return int
+     */
+    public function getOrderId();
 
     /**
      * Gets the created-at timestamp for the shipment package.
@@ -85,13 +95,6 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn
      */
     public function setCreatedAt($createdAt);
 
-    /**
-     * Gets the description for the shipment package.
-     *
-     * @return string Description.
-     */
-    public function getDescription();
-
     /**
      * Gets the ID for the shipment package.
      *
@@ -107,13 +110,6 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn
      */
     public function setEntityId($entityId);
 
-    /**
-     * Gets the order_id for the shipment package.
-     *
-     * @return int
-     */
-    public function getOrderId();
-
     /**
      * Gets the parent ID for the shipment package.
      *
@@ -121,27 +117,6 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn
      */
     public function getParentId();
 
-    /**
-     * Gets the quantity for the shipment package.
-     *
-     * @return float Quantity.
-     */
-    public function getQty();
-
-    /**
-     * Gets the title for the shipment package.
-     *
-     * @return string Title.
-     */
-    public function getTitle();
-
-    /**
-     * Gets the track number for the shipment package.
-     *
-     * @return string Track number.
-     */
-    public function getTrackNumber();
-
     /**
      * Gets the updated-at timestamp for the shipment package.
      *
@@ -149,13 +124,6 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn
      */
     public function getUpdatedAt();
 
-    /**
-     * Gets the weight for the shipment package.
-     *
-     * @return float Weight.
-     */
-    public function getWeight();
-
     /**
      * Sets the updated-at timestamp for the shipment package.
      *
@@ -181,28 +149,26 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn
     public function setWeight($weight);
 
     /**
-     * Sets the quantity for the shipment package.
+     * Gets the weight for the shipment package.
      *
-     * @param float $qty
-     * @return $this
+     * @return float Weight.
      */
-    public function setQty($qty);
+    public function getWeight();
 
     /**
-     * Sets the order_id for the shipment package.
+     * Sets the quantity for the shipment package.
      *
-     * @param int $id
+     * @param float $qty
      * @return $this
      */
-    public function setOrderId($id);
+    public function setQty($qty);
 
     /**
-     * Sets the track number for the shipment package.
+     * Gets the quantity for the shipment package.
      *
-     * @param string $trackNumber
-     * @return $this
+     * @return float Quantity.
      */
-    public function setTrackNumber($trackNumber);
+    public function getQty();
 
     /**
      * Sets the description for the shipment package.
@@ -213,20 +179,11 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn
     public function setDescription($description);
 
     /**
-     * Sets the title for the shipment package.
-     *
-     * @param string $title
-     * @return $this
-     */
-    public function setTitle($title);
-
-    /**
-     * Sets the carrier code for the shipment package.
+     * Gets the description for the shipment package.
      *
-     * @param string $code
-     * @return $this
+     * @return string Description.
      */
-    public function setCarrierCode($code);
+    public function getDescription();
 
     /**
      * Retrieve existing extension attributes object or create a new one.
diff --git a/app/code/Magento/Sales/Api/Data/TrackInterface.php b/app/code/Magento/Sales/Api/Data/TrackInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..17c08ff9b983031efa1268fc0a4c0fc359f60bd5
--- /dev/null
+++ b/app/code/Magento/Sales/Api/Data/TrackInterface.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Api\Data;
+
+/**
+ * Shipment Track Creation interface.
+ *
+ * @api
+ */
+interface TrackInterface
+{
+    /**
+     * Sets the track number for the shipment package.
+     *
+     * @param string $trackNumber
+     * @return $this
+     */
+    public function setTrackNumber($trackNumber);
+
+    /**
+     * Gets the track number for the shipment package.
+     *
+     * @return string Track number.
+     */
+    public function getTrackNumber();
+
+    /**
+     * Sets the title for the shipment package.
+     *
+     * @param string $title
+     * @return $this
+     */
+    public function setTitle($title);
+
+    /**
+     * Gets the title for the shipment package.
+     *
+     * @return string Title.
+     */
+    public function getTitle();
+
+    /**
+     * Sets the carrier code for the shipment package.
+     *
+     * @param string $code
+     * @return $this
+     */
+    public function setCarrierCode($code);
+
+    /**
+     * Gets the carrier code for the shipment package.
+     *
+     * @return string Carrier code.
+     */
+    public function getCarrierCode();
+}
diff --git a/app/code/Magento/Sales/Api/Exception/CouldNotShipExceptionInterface.php b/app/code/Magento/Sales/Api/Exception/CouldNotShipExceptionInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..b52e627791249eb6b347d495e32639e20536370e
--- /dev/null
+++ b/app/code/Magento/Sales/Api/Exception/CouldNotShipExceptionInterface.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Api\Exception;
+
+/**
+ * @api
+ */
+interface CouldNotShipExceptionInterface
+{
+}
diff --git a/app/code/Magento/Sales/Api/OrderInvoiceInterface.php b/app/code/Magento/Sales/Api/InvoiceOrderInterface.php
similarity index 93%
rename from app/code/Magento/Sales/Api/OrderInvoiceInterface.php
rename to app/code/Magento/Sales/Api/InvoiceOrderInterface.php
index 80fa6159afc3e449115a6b373560628a5aa7157f..0f0ce4596991664671cc6ea4d1f231ad05066ed3 100644
--- a/app/code/Magento/Sales/Api/OrderInvoiceInterface.php
+++ b/app/code/Magento/Sales/Api/InvoiceOrderInterface.php
@@ -7,11 +7,11 @@
 namespace Magento\Sales\Api;
 
 /**
- * Class OrderInvoiceInterface
+ * Class InvoiceOrderInterface
  *
  * @api
  */
-interface OrderInvoiceInterface
+interface InvoiceOrderInterface
 {
     /**
      * @param int $orderId
diff --git a/app/code/Magento/Sales/Api/ShipOrderInterface.php b/app/code/Magento/Sales/Api/ShipOrderInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..d28fedf4e507fc9c73f64ef60d42032e5d0c2114
--- /dev/null
+++ b/app/code/Magento/Sales/Api/ShipOrderInterface.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Api;
+
+/**
+ * Class ShipOrderInterface
+ *
+ * @api
+ */
+interface ShipOrderInterface
+{
+    /**
+     * Creates new Shipment for given Order.
+     *
+     * @param int $orderId
+     * @param \Magento\Sales\Api\Data\ShipmentItemCreationInterface[] $items
+     * @param bool $notify
+     * @param bool $appendComment
+     * @param \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|null $comment
+     * @param \Magento\Sales\Api\Data\ShipmentTrackCreationInterface[] $tracks
+     * @param \Magento\Sales\Api\Data\ShipmentPackageCreationInterface[] $packages
+     * @param \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface|null $arguments
+     * @return int Id of created Shipment.
+     */
+    public function execute(
+        $orderId,
+        array $items = [],
+        $notify = false,
+        $appendComment = false,
+        \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null,
+        array $tracks = [],
+        array $packages = [],
+        \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface $arguments = null
+    );
+}
diff --git a/app/code/Magento/Sales/Exception/CouldNotShipException.php b/app/code/Magento/Sales/Exception/CouldNotShipException.php
new file mode 100644
index 0000000000000000000000000000000000000000..4881bbd79a3a3eec84c74f66ffaf8a4c5553bd16
--- /dev/null
+++ b/app/code/Magento/Sales/Exception/CouldNotShipException.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Exception;
+
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Sales\Api\Exception\CouldNotShipExceptionInterface;
+
+/**
+ * Class CouldNotShipException
+ */
+class CouldNotShipException extends LocalizedException implements CouldNotShipExceptionInterface
+{
+}
diff --git a/app/code/Magento/Sales/Model/OrderInvoice.php b/app/code/Magento/Sales/Model/InvoiceOrder.php
similarity index 86%
rename from app/code/Magento/Sales/Model/OrderInvoice.php
rename to app/code/Magento/Sales/Model/InvoiceOrder.php
index b499b7028bce1336f29688b4ebb04aaa9db32048..e51b46082d943d8ff609bacb80fae19c861afba4 100644
--- a/app/code/Magento/Sales/Model/OrderInvoice.php
+++ b/app/code/Magento/Sales/Model/InvoiceOrder.php
@@ -9,22 +9,25 @@ namespace Magento\Sales\Model;
 use Magento\Framework\App\ResourceConnection;
 use Magento\Sales\Api\Data\InvoiceCommentCreationInterface;
 use Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface;
-use Magento\Sales\Api\OrderInvoiceInterface;
+use Magento\Sales\Api\InvoiceOrderInterface;
 use Magento\Sales\Api\OrderRepositoryInterface;
 use Magento\Sales\Model\Order\Config as OrderConfig;
+use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface;
 use Magento\Sales\Model\Order\Invoice\NotifierInterface;
 use Magento\Sales\Model\Order\InvoiceDocumentFactory;
+use Magento\Sales\Model\Order\InvoiceQuantityValidator;
 use Magento\Sales\Model\Order\InvoiceRepository;
-use Magento\Sales\Model\Order\InvoiceValidatorInterface;
 use Magento\Sales\Model\Order\OrderStateResolverInterface;
+use Magento\Sales\Model\Order\OrderValidatorInterface;
 use Magento\Sales\Model\Order\PaymentAdapterInterface;
+use Magento\Sales\Model\Order\Validation\CanInvoice;
 use Psr\Log\LoggerInterface;
 
 /**
- * Class InvoiceService
+ * Class InvoiceOrder
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
-class OrderInvoice implements OrderInvoiceInterface
+class InvoiceOrder implements InvoiceOrderInterface
 {
     /**
      * @var ResourceConnection
@@ -76,12 +79,18 @@ class OrderInvoice implements OrderInvoiceInterface
      */
     private $logger;
 
+    /**
+     * @var OrderValidatorInterface
+     */
+    private $orderValidator;
+
     /**
      * OrderInvoice constructor.
      * @param ResourceConnection $resourceConnection
      * @param OrderRepositoryInterface $orderRepository
      * @param InvoiceDocumentFactory $invoiceDocumentFactory
      * @param InvoiceValidatorInterface $invoiceValidator
+     * @param OrderValidatorInterface $orderValidator
      * @param PaymentAdapterInterface $paymentAdapter
      * @param OrderStateResolverInterface $orderStateResolver
      * @param OrderConfig $config
@@ -95,6 +104,7 @@ class OrderInvoice implements OrderInvoiceInterface
         OrderRepositoryInterface $orderRepository,
         InvoiceDocumentFactory $invoiceDocumentFactory,
         InvoiceValidatorInterface $invoiceValidator,
+        OrderValidatorInterface $orderValidator,
         PaymentAdapterInterface $paymentAdapter,
         OrderStateResolverInterface $orderStateResolver,
         OrderConfig $config,
@@ -106,6 +116,7 @@ class OrderInvoice implements OrderInvoiceInterface
         $this->orderRepository = $orderRepository;
         $this->invoiceDocumentFactory = $invoiceDocumentFactory;
         $this->invoiceValidator = $invoiceValidator;
+        $this->orderValidator = $orderValidator;
         $this->paymentAdapter = $paymentAdapter;
         $this->orderStateResolver = $orderStateResolver;
         $this->config = $config;
@@ -147,7 +158,16 @@ class OrderInvoice implements OrderInvoiceInterface
             ($appendComment && $notify),
             $arguments
         );
-        $errorMessages = $this->invoiceValidator->validate($invoice, $order);
+        $errorMessages = array_merge(
+            $this->invoiceValidator->validate(
+                $invoice,
+                [InvoiceQuantityValidator::class]
+            ),
+            $this->orderValidator->validate(
+                $order,
+                [CanInvoice::class]
+            )
+        );
         if (!empty($errorMessages)) {
             throw new \Magento\Sales\Exception\DocumentValidationException(
                 __("Invoice Document Validation Error(s):\n" . implode("\n", $errorMessages))
diff --git a/app/code/Magento/Sales/Model/Order/Invoice/CommentCreation.php b/app/code/Magento/Sales/Model/Order/Invoice/CommentCreation.php
new file mode 100644
index 0000000000000000000000000000000000000000..fa53f72ebcafcccc24a5e0cbf35b9cf208ec49ad
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Invoice/CommentCreation.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Sales\Model\Order\Invoice;
+
+use Magento\Sales\Api\Data\InvoiceCommentCreationInterface;
+
+/**
+ * Class InvoiceCommentCreation
+ */
+class CommentCreation implements InvoiceCommentCreationInterface
+{
+
+    /**
+     * @var string
+     */
+    private $comment;
+
+    /**
+     * @var int
+     */
+    private $isVisibleOnFront;
+
+    /**
+     * @var \Magento\Sales\Api\Data\InvoiceCommentCreationExtensionInterface
+     */
+    private $extensionAttributes;
+
+    /**
+     * Gets the comment for the invoice.
+     *
+     * @return string Comment.
+     */
+    public function getComment()
+    {
+        return $this->comment;
+    }
+
+    /**
+     * Sets the comment for the invoice.
+     *
+     * @param string $comment
+     * @return $this
+     */
+    public function setComment($comment)
+    {
+        $this->comment = $comment;
+        return $this;
+    }
+
+    /**
+     * Gets the is-visible-on-storefront flag value for the invoice.
+     *
+     * @return int Is-visible-on-storefront flag value.
+     */
+    public function getIsVisibleOnFront()
+    {
+        return $this->isVisibleOnFront;
+    }
+
+    /**
+     * Sets the is-visible-on-storefront flag value for the invoice.
+     *
+     * @param int $isVisibleOnFront
+     * @return $this
+     */
+    public function setIsVisibleOnFront($isVisibleOnFront)
+    {
+        $this->isVisibleOnFront = $isVisibleOnFront;
+        return $this;
+    }
+
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\InvoiceCommentCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->extensionAttributes;
+    }
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\InvoiceCommentCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\InvoiceCommentCreationExtensionInterface $extensionAttributes
+    ) {
+        $this->extensionAttributes = $extensionAttributes;
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidator.php b/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..cbb68edaa8a5508968ce4453973b5631a35768e5
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidator.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Invoice;
+
+use Magento\Sales\Api\Data\InvoiceInterface;
+
+/**
+ * Class InvoiceValidatorRunner
+ */
+class InvoiceValidator implements InvoiceValidatorInterface
+{
+    /**
+     * @var \Magento\Sales\Model\Validator
+     */
+    private $validator;
+
+    /**
+     * InvoiceValidatorRunner constructor.
+     * @param \Magento\Sales\Model\Validator $validator
+     */
+    public function __construct(\Magento\Sales\Model\Validator $validator)
+    {
+        $this->validator = $validator;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function validate(InvoiceInterface $entity, array $validators)
+    {
+        return $this->validator->validate($entity, $validators);
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidatorInterface.php b/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidatorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..568019a40fce5bc69ccab49ca8b3f4fee048505e
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidatorInterface.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Invoice;
+
+use Magento\Sales\Api\Data\InvoiceInterface;
+use Magento\Sales\Exception\DocumentValidationException;
+use Magento\Sales\Model\ValidatorInterface;
+
+/**
+ * Interface InvoiceValidatorInterface
+ */
+interface InvoiceValidatorInterface
+{
+    /**
+     * @param InvoiceInterface $entity
+     * @param ValidatorInterface[] $validators
+     * @return string[]
+     * @throws DocumentValidationException
+     */
+    public function validate(InvoiceInterface $entity, array $validators);
+}
diff --git a/app/code/Magento/Sales/Model/Order/Invoice/ItemCreation.php b/app/code/Magento/Sales/Model/Order/Invoice/ItemCreation.php
index abc19c3aaa73d99550f9157634d315b1053baf1d..26d8d7ae6ca99fd7191fdfaae957d4a067cd1455 100644
--- a/app/code/Magento/Sales/Model/Order/Invoice/ItemCreation.php
+++ b/app/code/Magento/Sales/Model/Order/Invoice/ItemCreation.php
@@ -23,6 +23,11 @@ class ItemCreation implements InvoiceItemCreationInterface
      */
     private $qty;
 
+    /**
+     * @var \Magento\Sales\Api\Data\InvoiceItemCreationExtensionInterface
+     */
+    private $extensionAttributes;
+
     /**
      * {@inheritdoc}
      */
@@ -54,4 +59,27 @@ class ItemCreation implements InvoiceItemCreationInterface
     {
         $this->qty = $qty;
     }
+
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\InvoiceItemCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->extensionAttributes;
+    }
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\InvoiceItemCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\InvoiceItemCreationExtensionInterface $extensionAttributes
+    ) {
+        $this->extensionAttributes = $extensionAttributes;
+        return $this;
+    }
 }
diff --git a/app/code/Magento/Sales/Model/Order/InvoiceValidator.php b/app/code/Magento/Sales/Model/Order/InvoiceQuantityValidator.php
similarity index 73%
rename from app/code/Magento/Sales/Model/Order/InvoiceValidator.php
rename to app/code/Magento/Sales/Model/Order/InvoiceQuantityValidator.php
index 35222599fc69ec67f636a6db69949d8d33006655..9ae81dacb0a17408337bec32961da40056ab4a8a 100644
--- a/app/code/Magento/Sales/Model/Order/InvoiceValidator.php
+++ b/app/code/Magento/Sales/Model/Order/InvoiceQuantityValidator.php
@@ -9,42 +9,38 @@ namespace Magento\Sales\Model\Order;
 use Magento\Sales\Api\Data\InvoiceInterface;
 use Magento\Sales\Api\Data\InvoiceItemInterface;
 use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Sales\Model\ValidatorInterface;
 
 /**
  * Interface InvoiceValidatorInterface
  */
-class InvoiceValidator implements InvoiceValidatorInterface
+class InvoiceQuantityValidator implements ValidatorInterface
 {
     /**
-     * @var OrderValidatorInterface
+     * @var OrderRepositoryInterface
      */
-    private $orderValidator;
+    private $orderRepository;
 
     /**
      * InvoiceValidator constructor.
-     * @param OrderValidatorInterface $orderValidator
+     * @param OrderRepositoryInterface $orderRepository
      */
-    public function __construct(OrderValidatorInterface $orderValidator)
+    public function __construct(OrderRepositoryInterface $orderRepository)
     {
-        $this->orderValidator = $orderValidator;
+        $this->orderRepository = $orderRepository;
     }
 
     /**
-     * @param InvoiceInterface $invoice
-     * @param OrderInterface $order
-     * @return array
+     * @inheritdoc
      */
-    public function validate(InvoiceInterface $invoice, OrderInterface $order)
+    public function validate($invoice)
     {
-        $messages = $this->checkQtyAvailability($invoice, $order);
-
-        if (!$this->orderValidator->canInvoice($order)) {
-            $messages[] = __(
-                'An invoice cannot be created when an order has a status of %1.',
-                $order->getStatus()
-            );
+        if ($invoice->getOrderId() === null) {
+            return [__('Order Id is required for invoice document')];
         }
-        return $messages;
+        $order = $this->orderRepository->get($invoice->getOrderId());
+        return $this->checkQtyAvailability($invoice, $order);
     }
 
     /**
diff --git a/app/code/Magento/Sales/Model/Order/InvoiceValidatorInterface.php b/app/code/Magento/Sales/Model/Order/InvoiceValidatorInterface.php
deleted file mode 100644
index 64b2f98dfe37eb15463d77509a14a9b5a579e043..0000000000000000000000000000000000000000
--- a/app/code/Magento/Sales/Model/Order/InvoiceValidatorInterface.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-/**
- * Copyright © 2016 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\Sales\Model\Order;
-
-use Magento\Sales\Api\Data\InvoiceInterface;
-use Magento\Sales\Api\Data\OrderInterface;
-
-/**
- * Interface InvoiceValidatorInterface
- *
- * @api
- */
-interface InvoiceValidatorInterface
-{
-    /**
-     * @param InvoiceInterface $invoice
-     * @param OrderInterface $order
-     * @return array
-     */
-    public function validate(InvoiceInterface $invoice, OrderInterface $order);
-}
diff --git a/app/code/Magento/Sales/Model/Order/OrderValidator.php b/app/code/Magento/Sales/Model/Order/OrderValidator.php
index c476671136875658e16185770b223c538e0d4e2f..8208af96c93d34cbae918883047b116713faad98 100644
--- a/app/code/Magento/Sales/Model/Order/OrderValidator.php
+++ b/app/code/Magento/Sales/Model/Order/OrderValidator.php
@@ -3,40 +3,35 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\Sales\Model\Order;
 
 use Magento\Sales\Api\Data\OrderInterface;
-use Magento\Sales\Model\Order;
+use Magento\Sales\Exception\DocumentValidationException;
 
 /**
- * Order Validator
- *
+ * Class OrderValidator
  */
 class OrderValidator implements OrderValidatorInterface
 {
     /**
-     * Retrieve order invoice availability
-     *
-     * @param OrderInterface $order
-     * @return bool
+     * @var \Magento\Sales\Model\Validator
+     */
+    private $validator;
+
+    /**
+     * OrderValidator constructor.
+     * @param \Magento\Sales\Model\Validator $validator
+     */
+    public function __construct(\Magento\Sales\Model\Validator $validator)
+    {
+        $this->validator = $validator;
+    }
+
+    /**
+     * @inheritdoc
      */
-    public function canInvoice(OrderInterface $order)
+    public function validate(OrderInterface $entity, array $validators)
     {
-        if ($order->getState() === Order::STATE_PAYMENT_REVIEW ||
-            $order->getState() === Order::STATE_HOLDED ||
-            $order->getState() === Order::STATE_CANCELED ||
-            $order->getState() === Order::STATE_COMPLETE ||
-            $order->getState() === Order::STATE_CLOSED
-        ) {
-            return false;
-        };
-        /** @var \Magento\Sales\Model\Order\Item $item */
-        foreach ($order->getItems() as $item) {
-            if ($item->getQtyToInvoice() > 0 && !$item->getLockedDoInvoice()) {
-                return true;
-            }
-        }
-        return false;
+        return $this->validator->validate($entity, $validators);
     }
 }
diff --git a/app/code/Magento/Sales/Model/Order/OrderValidatorInterface.php b/app/code/Magento/Sales/Model/Order/OrderValidatorInterface.php
index d0dcc38af642a7633af1b3ec4de124bc140c9497..c5a9a6c1d32963fcc223099efe1fcb81bb530464 100644
--- a/app/code/Magento/Sales/Model/Order/OrderValidatorInterface.php
+++ b/app/code/Magento/Sales/Model/Order/OrderValidatorInterface.php
@@ -3,21 +3,22 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\Sales\Model\Order;
 
 use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Exception\DocumentValidationException;
+use Magento\Sales\Model\ValidatorInterface;
 
 /**
  * Interface OrderValidatorInterface
- *
- * @api
  */
 interface OrderValidatorInterface
 {
     /**
-     * @param OrderInterface $order
-     * @return bool
+     * @param OrderInterface $entity
+     * @param ValidatorInterface[] $validators
+     * @return string[]
+     * @throws DocumentValidationException
      */
-    public function canInvoice(OrderInterface $order);
+    public function validate(OrderInterface $entity, array $validators);
 }
diff --git a/app/code/Magento/Sales/Model/Order/Shipment.php b/app/code/Magento/Sales/Model/Order/Shipment.php
index 6647bae750fffebc8a28ca09102b596336badd7f..2277f92d6e0e8be638c919f02dfc13d1054fb819 100644
--- a/app/code/Magento/Sales/Model/Order/Shipment.php
+++ b/app/code/Magento/Sales/Model/Order/Shipment.php
@@ -405,7 +405,7 @@ class Shipment extends AbstractModel implements EntityInterface, ShipmentInterfa
      * Adds comment to shipment with additional possibility to send it to customer via email
      * and show it in customer account
      *
-     * @param \Magento\Sales\Model\Order\Shipment\Comment $comment
+     * @param \Magento\Sales\Model\Order\Shipment\Comment|string $comment
      * @param bool $notify
      * @param bool $visibleOnFront
      * @return $this
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/CommentCreation.php b/app/code/Magento/Sales/Model/Order/Shipment/CommentCreation.php
new file mode 100644
index 0000000000000000000000000000000000000000..19d06fb0eff3275a41ccd2d0c9cc42e77c0436c7
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/CommentCreation.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+use Magento\Sales\Api\Data\ShipmentCommentCreationInterface;
+
+/**
+ * Class CommentCreation
+ */
+class CommentCreation implements ShipmentCommentCreationInterface
+{
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentCommentCreationExtensionInterface
+     */
+    private $extensionAttributes;
+
+    /**
+     * @var string
+     */
+    private $comment;
+
+    /**
+     * @var int
+     */
+    private $isVisibleOnFront;
+
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Sales\Api\Data\ShipmentCommentCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->extensionAttributes;
+    }
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Sales\Api\Data\ShipmentCommentCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentCommentCreationExtensionInterface $extensionAttributes
+    ) {
+        $this->extensionAttributes = $extensionAttributes;
+        return $this;
+    }
+
+    /**
+     * Gets the comment for the invoice.
+     *
+     * @return string Comment.
+     */
+    public function getComment()
+    {
+        return $this->comment;
+    }
+
+    /**
+     * Sets the comment for the invoice.
+     *
+     * @param string $comment
+     * @return $this
+     */
+    public function setComment($comment)
+    {
+        $this->comment = $comment;
+        return $this;
+    }
+
+    /**
+     * Gets the is-visible-on-storefront flag value for the invoice.
+     *
+     * @return int Is-visible-on-storefront flag value.
+     */
+    public function getIsVisibleOnFront()
+    {
+        return $this->isVisibleOnFront;
+    }
+
+    /**
+     * Sets the is-visible-on-storefront flag value for the invoice.
+     *
+     * @param int $isVisibleOnFront
+     * @return $this
+     */
+    public function setIsVisibleOnFront($isVisibleOnFront)
+    {
+        $this->isVisibleOnFront = $isVisibleOnFront;
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/CreationArguments.php b/app/code/Magento/Sales/Model/Order/Shipment/CreationArguments.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a43a73553e79ab7887995c3a691263ff86c7cd6
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/CreationArguments.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+/**
+ * Creation arguments for Shipment.
+ *
+ * @api
+ */
+class CreationArguments implements \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface
+{
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface
+     */
+    private $extensionAttributes;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->extensionAttributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentCreationArgumentsExtensionInterface $extensionAttributes
+    ) {
+        $this->extensionAttributes = $extensionAttributes;
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Item.php b/app/code/Magento/Sales/Model/Order/Shipment/Item.php
index c7fdca853b17e366e9a64201623b5f68697c8381..8627f76031b06657d2d727b5261a89cc261ea6e1 100644
--- a/app/code/Magento/Sales/Model/Order/Shipment/Item.php
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Item.php
@@ -151,22 +151,7 @@ class Item extends AbstractModel implements ShipmentItemInterface
      */
     public function setQty($qty)
     {
-        if ($this->getOrderItem()->getIsQtyDecimal()) {
-            $qty = (double)$qty;
-        } else {
-            $qty = (int)$qty;
-        }
-        $qty = $qty > 0 ? $qty : 0;
-        /**
-         * Check qty availability
-         */
-        if ($qty <= $this->getOrderItem()->getQtyToShip() || $this->getOrderItem()->isDummy(true)) {
-            $this->setData('qty', $qty);
-        } else {
-            throw new \Magento\Framework\Exception\LocalizedException(
-                __('We found an invalid quantity to ship for item "%1".', $this->getName())
-            );
-        }
+        $this->setData('qty', $qty);
         return $this;
     }
 
@@ -174,6 +159,7 @@ class Item extends AbstractModel implements ShipmentItemInterface
      * Applying qty to order item
      *
      * @return $this
+     * @throws \Magento\Framework\Exception\LocalizedException
      */
     public function register()
     {
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/ItemCreation.php b/app/code/Magento/Sales/Model/Order/Shipment/ItemCreation.php
new file mode 100644
index 0000000000000000000000000000000000000000..e3cb2f23d7cf31e81a737fa06c64b89d1d11d796
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/ItemCreation.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+use Magento\Sales\Api\Data\ShipmentItemCreationInterface;
+
+/**
+ * Class ItemCreation
+ */
+class ItemCreation implements ShipmentItemCreationInterface
+{
+    /**
+     * @var int
+     */
+    private $orderItemId;
+
+    /**
+     * @var float
+     */
+    private $qty;
+
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentItemCreationExtensionInterface
+     */
+    private $extensionAttributes;
+
+    //@codeCoverageIgnoreStart
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOrderItemId()
+    {
+        return $this->orderItemId;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOrderItemId($orderItemId)
+    {
+        $this->orderItemId = $orderItemId;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getQty()
+    {
+        return $this->qty;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setQty($qty)
+    {
+        $this->qty = $qty;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return \Magento\Sales\Api\Data\ShipmentItemCreationExtensionInterface|null
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->extensionAttributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param \Magento\Sales\Api\Data\ShipmentItemCreationExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentItemCreationExtensionInterface $extensionAttributes
+    ) {
+        $this->extensionAttributes = $extensionAttributes;
+        return $this;
+    }
+    //@codeCoverageIgnoreEnd
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Notifier.php b/app/code/Magento/Sales/Model/Order/Shipment/Notifier.php
new file mode 100644
index 0000000000000000000000000000000000000000..21dd5ad4a58f62ad2b77143e373f9e0f19c3bc66
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Notifier.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+/**
+ * Shipment notifier.
+ *
+ * @api
+ */
+class Notifier implements \Magento\Sales\Model\Order\Shipment\NotifierInterface
+{
+    /**
+     * @var \Magento\Sales\Model\Order\Shipment\SenderInterface[]
+     */
+    private $senders;
+
+    /**
+     * @param \Magento\Sales\Model\Order\Shipment\SenderInterface[] $senders
+     */
+    public function __construct(array $senders = [])
+    {
+        $this->senders = $senders;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function notify(
+        \Magento\Sales\Api\Data\OrderInterface $order,
+        \Magento\Sales\Api\Data\ShipmentInterface $shipment,
+        \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null,
+        $forceSyncMode = false
+    ) {
+        foreach ($this->senders as $sender) {
+            $sender->send($order, $shipment, $comment, $forceSyncMode);
+        }
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/NotifierInterface.php b/app/code/Magento/Sales/Model/Order/Shipment/NotifierInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..f34eb6178d09468de16f48fba3a717de3b22091a
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/NotifierInterface.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+/**
+ * Interface for Shipment notifier.
+ *
+ * @api
+ */
+interface NotifierInterface
+{
+    /**
+     * Notifies customer.
+     *
+     * @param \Magento\Sales\Api\Data\OrderInterface $order
+     * @param \Magento\Sales\Api\Data\ShipmentInterface $shipment
+     * @param \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|null $comment
+     * @param bool $forceSyncMode
+     *
+     * @return void
+     */
+    public function notify(
+        \Magento\Sales\Api\Data\OrderInterface $order,
+        \Magento\Sales\Api\Data\ShipmentInterface $shipment,
+        \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null,
+        $forceSyncMode = false
+    );
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/OrderRegistrar.php b/app/code/Magento/Sales/Model/Order/Shipment/OrderRegistrar.php
new file mode 100644
index 0000000000000000000000000000000000000000..040ab12949be1748638cd7e83ff8c971b7e34161
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/OrderRegistrar.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\Data\ShipmentInterface;
+
+class OrderRegistrar implements \Magento\Sales\Model\Order\Shipment\OrderRegistrarInterface
+{
+    /**
+     * @param OrderInterface $order
+     * @param ShipmentInterface $shipment
+     * @return OrderInterface
+     */
+    public function register(OrderInterface $order, ShipmentInterface $shipment)
+    {
+        /** @var  \Magento\Sales\Api\Data\ShipmentItemInterface|\Magento\Sales\Model\Order\Shipment\Item $item */
+        foreach ($shipment->getItems() as $item) {
+            if ($item->getQty() > 0) {
+                $item->register();
+            }
+        }
+        return $order;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/OrderRegistrarInterface.php b/app/code/Magento/Sales/Model/Order/Shipment/OrderRegistrarInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..7d54acece35997efcb2db7c62a9fb92c42cd4788
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/OrderRegistrarInterface.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\Data\ShipmentInterface;
+
+/**
+ * Interface OrderRegistrarInterface
+ *
+ * Calculate order shipped data based on created shipment
+ *
+ * @api
+ */
+interface OrderRegistrarInterface
+{
+    /**
+     * @param OrderInterface $order
+     * @param ShipmentInterface $shipment
+     * @return OrderInterface
+     */
+    public function register(OrderInterface $order, ShipmentInterface $shipment);
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Package.php b/app/code/Magento/Sales/Model/Order/Shipment/Package.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f8f54336a2c5b989ef7728866d8b18039c492f4
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Package.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+/**
+ * Class Package
+ * @api
+ */
+class Package implements \Magento\Sales\Api\Data\ShipmentPackageInterface
+{
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentPackageExtensionInterface
+     */
+    private $extensionAttributes;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->extensionAttributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentPackageExtensionInterface $extensionAttributes
+    ) {
+        $this->extensionAttributes = $extensionAttributes;
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/PackageCreation.php b/app/code/Magento/Sales/Model/Order/Shipment/PackageCreation.php
new file mode 100644
index 0000000000000000000000000000000000000000..50ad944b8251c62a6df8af329839d58a49d8827d
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/PackageCreation.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+/**
+ * Class PackageCreation
+ * @api
+ */
+class PackageCreation implements \Magento\Sales\Api\Data\ShipmentPackageCreationInterface
+{
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentPackageCreationExtensionInterface
+     */
+    private $extensionAttributes;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->extensionAttributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentPackageCreationExtensionInterface $extensionAttributes
+    ) {
+        $this->extensionAttributes = $extensionAttributes;
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php
new file mode 100644
index 0000000000000000000000000000000000000000..228a45ff16aaef74de124cbd24041d1e27227112
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment\Sender;
+
+use Magento\Sales\Model\Order\Email\Sender;
+use Magento\Sales\Model\Order\Shipment\SenderInterface;
+
+/**
+ * Email notification sender for Shipment.
+ */
+class EmailSender extends Sender implements SenderInterface
+{
+    /**
+     * @var \Magento\Payment\Helper\Data
+     */
+    private $paymentHelper;
+
+    /**
+     * @var \Magento\Sales\Model\ResourceModel\Order\Shipment
+     */
+    private $shipmentResource;
+
+    /**
+     * @var \Magento\Framework\App\Config\ScopeConfigInterface
+     */
+    private $globalConfig;
+
+    /**
+     * @var \Magento\Framework\Event\ManagerInterface
+     */
+    private $eventManager;
+
+    /**
+     * @param \Magento\Sales\Model\Order\Email\Container\Template $templateContainer
+     * @param \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity $identityContainer
+     * @param \Magento\Sales\Model\Order\Email\SenderBuilderFactory $senderBuilderFactory
+     * @param \Psr\Log\LoggerInterface $logger
+     * @param \Magento\Sales\Model\Order\Address\Renderer $addressRenderer
+     * @param \Magento\Payment\Helper\Data $paymentHelper
+     * @param \Magento\Sales\Model\ResourceModel\Order\Shipment $shipmentResource
+     * @param \Magento\Framework\App\Config\ScopeConfigInterface $globalConfig
+     * @param \Magento\Framework\Event\ManagerInterface $eventManager
+     */
+    public function __construct(
+        \Magento\Sales\Model\Order\Email\Container\Template $templateContainer,
+        \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity $identityContainer,
+        \Magento\Sales\Model\Order\Email\SenderBuilderFactory $senderBuilderFactory,
+        \Psr\Log\LoggerInterface $logger,
+        \Magento\Sales\Model\Order\Address\Renderer $addressRenderer,
+        \Magento\Payment\Helper\Data $paymentHelper,
+        \Magento\Sales\Model\ResourceModel\Order\Shipment $shipmentResource,
+        \Magento\Framework\App\Config\ScopeConfigInterface $globalConfig,
+        \Magento\Framework\Event\ManagerInterface $eventManager
+    ) {
+        parent::__construct(
+            $templateContainer,
+            $identityContainer,
+            $senderBuilderFactory,
+            $logger,
+            $addressRenderer
+        );
+
+        $this->paymentHelper = $paymentHelper;
+        $this->shipmentResource = $shipmentResource;
+        $this->globalConfig = $globalConfig;
+        $this->eventManager = $eventManager;
+    }
+
+    /**
+     * Sends order shipment email to the customer.
+     *
+     * Email will be sent immediately in two cases:
+     *
+     * - if asynchronous email sending is disabled in global settings
+     * - if $forceSyncMode parameter is set to TRUE
+     *
+     * Otherwise, email will be sent later during running of
+     * corresponding cron job.
+     *
+     * @param \Magento\Sales\Api\Data\OrderInterface $order
+     * @param \Magento\Sales\Api\Data\ShipmentInterface $shipment
+     * @param \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|null $comment
+     * @param bool $forceSyncMode
+     *
+     * @return bool
+     */
+    public function send(
+        \Magento\Sales\Api\Data\OrderInterface $order,
+        \Magento\Sales\Api\Data\ShipmentInterface $shipment,
+        \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null,
+        $forceSyncMode = false
+    ) {
+        $shipment->setSendEmail(true);
+
+        if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) {
+            $transport = [
+                'order' => $order,
+                'shipment' => $shipment,
+                'comment' => $comment ? $comment->getComment() : '',
+                'billing' => $order->getBillingAddress(),
+                'payment_html' => $this->getPaymentHtml($order),
+                'store' => $order->getStore(),
+                'formattedShippingAddress' => $this->getFormattedShippingAddress($order),
+                'formattedBillingAddress' => $this->getFormattedBillingAddress($order)
+            ];
+
+            $this->eventManager->dispatch(
+                'email_shipment_set_template_vars_before',
+                ['sender' => $this, 'transport' => $transport]
+            );
+
+            $this->templateContainer->setTemplateVars($transport);
+
+            if ($this->checkAndSend($order)) {
+                $shipment->setEmailSent(true);
+
+                $this->shipmentResource->saveAttribute($shipment, ['send_email', 'email_sent']);
+
+                return true;
+            }
+        } else {
+            $shipment->setEmailSent(null);
+
+            $this->shipmentResource->saveAttribute($shipment, 'email_sent');
+        }
+
+        $this->shipmentResource->saveAttribute($shipment, 'send_email');
+
+        return false;
+    }
+
+    /**
+     * Returns payment info block as HTML.
+     *
+     * @param \Magento\Sales\Api\Data\OrderInterface $order
+     *
+     * @return string
+     */
+    private function getPaymentHtml(\Magento\Sales\Api\Data\OrderInterface $order)
+    {
+        return $this->paymentHelper->getInfoBlockHtml(
+            $order->getPayment(),
+            $this->identityContainer->getStore()->getStoreId()
+        );
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/SenderInterface.php b/app/code/Magento/Sales/Model/Order/Shipment/SenderInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..a030038b7b139a65ebc38fd5f9fb2c1a76f8a188
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/SenderInterface.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+/**
+ * Interface for notification sender for Shipment.
+ */
+interface SenderInterface
+{
+    /**
+     * Sends notification to a customer.
+     *
+     * @param \Magento\Sales\Api\Data\OrderInterface $order
+     * @param \Magento\Sales\Api\Data\ShipmentInterface $shipment
+     * @param \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|null $comment
+     * @param bool $forceSyncMode
+     *
+     * @return bool
+     */
+    public function send(
+        \Magento\Sales\Api\Data\OrderInterface $order,
+        \Magento\Sales\Api\Data\ShipmentInterface $shipment,
+        \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null,
+        $forceSyncMode = false
+    );
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidator.php b/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..816551b8b322c126d82795349a4ba081598a955c
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidator.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+use Magento\Sales\Api\Data\ShipmentInterface;
+
+/**
+ * Class ShipmentValidator
+ */
+class ShipmentValidator implements ShipmentValidatorInterface
+{
+    /**
+     * @var \Magento\Sales\Model\Validator
+     */
+    private $validator;
+
+    /**
+     * ShipmentValidator constructor.
+     * @param \Magento\Sales\Model\Validator $validator
+     */
+    public function __construct(\Magento\Sales\Model\Validator $validator)
+    {
+        $this->validator = $validator;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function validate(ShipmentInterface $entity, array $validators)
+    {
+        return $this->validator->validate($entity, $validators);
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidatorInterface.php b/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidatorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..198a4019bf6b898e0c85fd2e2ff7c7ff77c2a9f6
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidatorInterface.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+use Magento\Sales\Api\Data\ShipmentInterface;
+use Magento\Sales\Exception\DocumentValidationException;
+use Magento\Sales\Model\ValidatorInterface;
+
+/**
+ * Interface ShipmentValidatorInterface
+ */
+interface ShipmentValidatorInterface
+{
+    /**
+     * @param ShipmentInterface $shipment
+     * @param ValidatorInterface[] $validators
+     * @return string[]
+     * @throws DocumentValidationException
+     */
+    public function validate(ShipmentInterface $shipment, array $validators);
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/TrackCreation.php b/app/code/Magento/Sales/Model/Order/Shipment/TrackCreation.php
new file mode 100644
index 0000000000000000000000000000000000000000..6e8ce097f411d91794ea07ce904851bce725f169
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/TrackCreation.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment;
+
+use Magento\Sales\Api\Data\ShipmentTrackCreationInterface;
+
+/**
+ * Class TrackCreation
+ */
+class TrackCreation implements ShipmentTrackCreationInterface
+{
+    /**
+     * @var string
+     */
+    private $trackNumber;
+
+    /**
+     * @var string
+     */
+    private $title;
+
+    /**
+     * @var string
+     */
+    private $carrierCode;
+
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentTrackCreationExtensionInterface
+     */
+    private $extensionAttributes;
+
+    //@codeCoverageIgnoreStart
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getTrackNumber()
+    {
+        return $this->trackNumber;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setTrackNumber($trackNumber)
+    {
+        $this->trackNumber = $trackNumber;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setTitle($title)
+    {
+        $this->title = $title;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCarrierCode()
+    {
+        return $this->carrierCode;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setCarrierCode($carrierCode)
+    {
+        $this->carrierCode = $carrierCode;
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->extensionAttributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(
+        \Magento\Sales\Api\Data\ShipmentTrackCreationExtensionInterface $extensionAttributes
+    ) {
+        $this->extensionAttributes = $extensionAttributes;
+        return $this;
+    }
+
+    //@codeCoverageIgnoreEnd
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Validation/QuantityValidator.php b/app/code/Magento/Sales/Model/Order/Shipment/Validation/QuantityValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..20e3712d889ed249ebca9fbbab0945d37a8cb8cf
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Validation/QuantityValidator.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment\Validation;
+
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\Data\OrderItemInterface;
+use Magento\Sales\Api\Data\ShipmentInterface;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Sales\Exception\DocumentValidationException;
+use Magento\Sales\Model\Order\Item;
+use Magento\Sales\Model\ValidatorInterface;
+
+/**
+ * Class QuantityValidator
+ */
+class QuantityValidator implements ValidatorInterface
+{
+    /**
+     * @var OrderRepositoryInterface
+     */
+    private $orderRepository;
+
+    /**
+     * InvoiceValidator constructor.
+     * @param OrderRepositoryInterface $orderRepository
+     */
+    public function __construct(
+        OrderRepositoryInterface $orderRepository
+    ) {
+        $this->orderRepository = $orderRepository;
+    }
+
+    /**
+     * @param ShipmentInterface $entity
+     * @return array
+     * @throws DocumentValidationException
+     * @throws NoSuchEntityException
+     */
+    public function validate($entity)
+    {
+        if ($entity->getOrderId() === null) {
+            return [__('Order Id is required for shipment document')];
+        }
+
+        if (empty($entity->getItems())) {
+            return [__('You can\'t create a shipment without products.')];
+        }
+        $messages = [];
+
+        $order = $this->orderRepository->get($entity->getOrderId());
+        $orderItemsById = $this->getOrderItems($order);
+
+        $totalQuantity = 0;
+        foreach ($entity->getItems() as $item) {
+            if (!isset($orderItemsById[$item->getOrderItemId()])) {
+                $messages[] = __(
+                    'The shipment contains product SKU "%1" that is not part of the original order.',
+                    $item->getSku()
+                );
+                continue;
+            }
+            $orderItem = $orderItemsById[$item->getOrderItemId()];
+
+            if (!$this->isQtyAvailable($orderItem, $item->getQty())) {
+                $messages[] =__(
+                    'The quantity to ship must not be greater than the unshipped quantity'
+                    . ' for product SKU "%1".',
+                    $orderItem->getSku()
+                );
+            } else {
+                $totalQuantity += $item->getQty();
+            }
+        }
+        if ($totalQuantity <= 0) {
+            $messages[] = __('You can\'t create a shipment without products.');
+        }
+
+        return $messages;
+    }
+
+    /**
+     * @param OrderInterface $order
+     * @return OrderItemInterface[]
+     */
+    private function getOrderItems(OrderInterface $order)
+    {
+        $orderItemsById = [];
+        foreach ($order->getItems() as $item) {
+            $orderItemsById[$item->getItemId()] = $item;
+        }
+
+        return $orderItemsById;
+    }
+
+    /**
+     * @param Item $orderItem
+     * @param int $qty
+     * @return bool
+     */
+    private function isQtyAvailable(Item $orderItem, $qty)
+    {
+        return $qty <= $orderItem->getQtyToShip() || $orderItem->isDummy(true);
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Validation/TrackValidator.php b/app/code/Magento/Sales/Model/Order/Shipment/Validation/TrackValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..55970d37c597d7af6d0369e26b2085ab431108ed
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Validation/TrackValidator.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Shipment\Validation;
+
+use Magento\Sales\Api\Data\ShipmentInterface;
+use Magento\Sales\Model\ValidatorInterface;
+
+/**
+ * Class TrackRequiredField
+ */
+class TrackValidator implements ValidatorInterface
+{
+    /**
+     * @param object|ShipmentInterface $entity
+     * @return array
+     */
+    public function validate($entity)
+    {
+        $messages = [];
+        if (!$entity->getTracks()) {
+            return $messages;
+        }
+        foreach ($entity->getTracks() as $track) {
+            if (!$track->getTrackNumber()) {
+                $messages[] = __('Please enter a tracking number.');
+            }
+        }
+        return $messages;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..d10f84d8155432954ec95c75f9b49108767f2dd3
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/ShipmentDocumentFactory.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order;
+
+use Magento\Sales\Api\Data\ShipmentInterface;
+use Magento\Sales\Api\Data\ShipmentItemCreationInterface;
+use Magento\Sales\Api\Data\ShipmentPackageCreationInterface;
+use Magento\Sales\Api\Data\ShipmentTrackCreationInterface;
+use Magento\Framework\EntityManager\HydratorPool;
+use Magento\Sales\Model\Order\Shipment\TrackFactory;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\Data\ShipmentCommentCreationInterface;
+use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface;
+
+/**
+ * Class ShipmentDocumentFactory
+ *
+ * @api
+ */
+class ShipmentDocumentFactory
+{
+    /**
+     * @var ShipmentFactory
+     */
+    private $shipmentFactory;
+
+    /**
+     * @var TrackFactory
+     */
+    private $trackFactory;
+
+    /**
+     * @var HydratorPool
+     */
+    private $hydratorPool;
+
+    /**
+     * ShipmentDocumentFactory constructor.
+     *
+     * @param ShipmentFactory $shipmentFactory
+     * @param HydratorPool $hydratorPool
+     * @param TrackFactory $trackFactory
+     */
+    public function __construct(
+        ShipmentFactory $shipmentFactory,
+        HydratorPool $hydratorPool,
+        TrackFactory $trackFactory
+    ) {
+        $this->shipmentFactory = $shipmentFactory;
+        $this->trackFactory = $trackFactory;
+        $this->hydratorPool = $hydratorPool;
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @param OrderInterface $order
+     * @param ShipmentItemCreationInterface[] $items
+     * @param ShipmentTrackCreationInterface[] $tracks
+     * @param ShipmentCommentCreationInterface|null $comment
+     * @param bool $appendComment
+     * @param ShipmentPackageCreationInterface[] $packages
+     * @param ShipmentCreationArgumentsInterface|null $arguments
+     * @return ShipmentInterface
+     */
+    public function create(
+        OrderInterface $order,
+        array $items = [],
+        array $tracks = [],
+        ShipmentCommentCreationInterface $comment = null,
+        $appendComment = false,
+        array $packages = [],
+        ShipmentCreationArgumentsInterface $arguments = null
+    ) {
+        $shipmentItems = $this->itemsToArray($items);
+        /** @var Shipment $shipment */
+        $shipment = $this->shipmentFactory->create(
+            $order,
+            $shipmentItems
+        );
+        $this->prepareTracks($shipment, $tracks);
+        if ($comment) {
+            $shipment->addComment(
+                $comment->getComment(),
+                $appendComment,
+                $comment->getIsVisibleOnFront()
+            );
+        }
+
+        return $shipment;
+    }
+
+    /**
+     * Adds tracks to the shipment.
+     *
+     * @param ShipmentInterface $shipment
+     * @param ShipmentTrackCreationInterface[] $tracks
+     * @return ShipmentInterface
+     */
+    private function prepareTracks(\Magento\Sales\Api\Data\ShipmentInterface $shipment, array $tracks)
+    {
+        foreach ($tracks as $track) {
+            $hydrator = $this->hydratorPool->getHydrator(
+                \Magento\Sales\Api\Data\ShipmentTrackCreationInterface::class
+            );
+            $shipment->addTrack($this->trackFactory->create(['data' => $hydrator->extract($track)]));
+        }
+        return $shipment;
+    }
+
+    /**
+     * Convert items to array
+     *
+     * @param ShipmentItemCreationInterface[] $items
+     * @return array
+     */
+    private function itemsToArray(array $items = [])
+    {
+        $shipmentItems = [];
+        foreach ($items as $item) {
+            $shipmentItems[$item->getOrderItemId()] = $item->getQty();
+        }
+        return $shipmentItems;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/ShipmentFactory.php b/app/code/Magento/Sales/Model/Order/ShipmentFactory.php
index 2ac012760ee4704be9defe05cfe7c2d01529359e..a8839c75375870ce03294ca3d585635fdff84f14 100644
--- a/app/code/Magento/Sales/Model/Order/ShipmentFactory.php
+++ b/app/code/Magento/Sales/Model/Order/ShipmentFactory.php
@@ -5,6 +5,10 @@
  */
 namespace Magento\Sales\Model\Order;
 
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface;
+
 /**
  * Factory class for @see \Magento\Sales\Api\Data\ShipmentInterface
  */
@@ -72,6 +76,8 @@ class ShipmentFactory
      * @param \Magento\Sales\Model\Order $order
      * @param array $items
      * @return \Magento\Sales\Api\Data\ShipmentInterface
+     * @throws LocalizedException
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
     protected function prepareItems(
         \Magento\Sales\Api\Data\ShipmentInterface $shipment,
@@ -79,7 +85,6 @@ class ShipmentFactory
         array $items = []
     ) {
         $totalQty = 0;
-
         foreach ($order->getAllItems() as $orderItem) {
             if (!$this->canShipItem($orderItem, $items)) {
                 continue;
@@ -103,7 +108,7 @@ class ShipmentFactory
                             $qty = $bundleSelectionAttributes['qty'] * $items[$orderItem->getParentItemId()];
                             $qty = min($qty, $orderItem->getSimpleQtyToShip());
 
-                            $item->setQty($qty);
+                            $item->setQty($this->castQty($orderItem, $qty));
                             $shipment->addItem($item);
 
                             continue;
@@ -126,10 +131,9 @@ class ShipmentFactory
 
             $totalQty += $qty;
 
-            $item->setQty($qty);
+            $item->setQty($this->castQty($orderItem, $qty));
             $shipment->addItem($item);
         }
-
         return $shipment->setTotalQty($totalQty);
     }
 
@@ -211,4 +215,20 @@ class ShipmentFactory
             return $item->getQtyToShip() > 0;
         }
     }
+
+    /**
+     * @param Item $item
+     * @param string|int|float $qty
+     * @return float|int
+     */
+    private function castQty(\Magento\Sales\Model\Order\Item $item, $qty)
+    {
+        if ($item->getIsQtyDecimal()) {
+            $qty = (double)$qty;
+        } else {
+            $qty = (int)$qty;
+        }
+
+        return $qty > 0 ? $qty : 0;
+    }
 }
diff --git a/app/code/Magento/Sales/Model/Order/Validation/CanInvoice.php b/app/code/Magento/Sales/Model/Order/Validation/CanInvoice.php
new file mode 100644
index 0000000000000000000000000000000000000000..bb14dc1bb5180758825966d47d7f92bb06ddacda
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Validation/CanInvoice.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Validation;
+
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Model\Order;
+use Magento\Sales\Model\ValidatorInterface;
+
+/**
+ * Class CanInvoice
+ */
+class CanInvoice implements ValidatorInterface
+{
+    /**
+     * @param OrderInterface $entity
+     * @return array
+     */
+    public function validate($entity)
+    {
+        $messages = [];
+
+        if (!$this->isStateReadyForInvoice($entity)) {
+            $messages[] = __('An invoice cannot be created when an order has a status of %1', $entity->getStatus());
+        } elseif (!$this->canInvoice($entity)) {
+            $messages[] = __('The order does not allow an invoice to be created.');
+        }
+
+        return $messages;
+    }
+
+    /**
+     * @param OrderInterface $order
+     * @return bool
+     */
+    private function isStateReadyForInvoice(OrderInterface $order)
+    {
+        if ($order->getState() === Order::STATE_PAYMENT_REVIEW ||
+            $order->getState() === Order::STATE_HOLDED ||
+            $order->getState() === Order::STATE_CANCELED ||
+            $order->getState() === Order::STATE_COMPLETE ||
+            $order->getState() === Order::STATE_CLOSED
+        ) {
+            return false;
+        };
+
+        return true;
+    }
+
+    /**
+     * @param OrderInterface $order
+     * @return bool
+     */
+    private function canInvoice(OrderInterface $order)
+    {
+        /** @var \Magento\Sales\Model\Order\Item $item */
+        foreach ($order->getItems() as $item) {
+            if ($item->getQtyToInvoice() > 0 && !$item->getLockedDoInvoice()) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Order/Validation/CanShip.php b/app/code/Magento/Sales/Model/Order/Validation/CanShip.php
new file mode 100644
index 0000000000000000000000000000000000000000..46638a62483e6ce0f45a1c79d2774fe2c34d7970
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Order/Validation/CanShip.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model\Order\Validation;
+
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Model\Order;
+use Magento\Sales\Model\ValidatorInterface;
+
+/**
+ * Class CanShip
+ */
+class CanShip implements ValidatorInterface
+{
+    /**
+     * @param OrderInterface $entity
+     * @return array
+     */
+    public function validate($entity)
+    {
+        $messages = [];
+        if (!$this->isStateReadyForShipment($entity)) {
+            $messages[] = __('A shipment cannot be created when an order has a status of %1', $entity->getStatus());
+        } elseif (!$this->canShip($entity)) {
+            $messages[] = __('The order does not allow a shipment to be created.');
+        }
+
+        return $messages;
+    }
+
+    /**
+     * @param OrderInterface $order
+     * @return bool
+     */
+    private function isStateReadyForShipment(OrderInterface $order)
+    {
+        if ($order->getState() === Order::STATE_PAYMENT_REVIEW ||
+            $order->getState() === Order::STATE_HOLDED ||
+            $order->getIsVirtual() ||
+            $order->getState() === Order::STATE_CANCELED
+        ) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @param OrderInterface $order
+     * @return bool
+     */
+    private function canShip(OrderInterface $order)
+    {
+        /** @var \Magento\Sales\Model\Order\Item $item */
+        foreach ($order->getItems() as $item) {
+            if ($item->getQtyToShip() > 0 && !$item->getIsVirtual() && !$item->getLockedDoShip()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Plugin/Authorization.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Plugin/Authorization.php
index a44a7b78717975a0945ab23a7976c1fc009e371e..f6b459ba4638d0d5db59fe8fb06d3054f5299ec3 100644
--- a/app/code/Magento/Sales/Model/ResourceModel/Order/Plugin/Authorization.php
+++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Plugin/Authorization.php
@@ -8,6 +8,8 @@ namespace Magento\Sales\Model\ResourceModel\Order\Plugin;
 
 use Magento\Authorization\Model\UserContextInterface;
 use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Sales\Model\Order;
+use Magento\Sales\Model\ResourceModel\Order as ResourceOrder;
 
 class Authorization
 {
@@ -20,33 +22,28 @@ class Authorization
      * @param UserContextInterface $userContext
      */
     public function __construct(
-        \Magento\Authorization\Model\UserContextInterface $userContext
+        UserContextInterface $userContext
     ) {
         $this->userContext = $userContext;
     }
 
     /**
-     * Checks if order is allowed
-     *
-     * @param \Magento\Sales\Model\ResourceModel\Order $subject
-     * @param callable $proceed
+     * @param ResourceOrder $subject
+     * @param ResourceOrder $result
      * @param \Magento\Framework\Model\AbstractModel $order
-     * @param mixed $value
-     * @param null|string $field
-     * @return \Magento\Sales\Model\Order
+     * @return ResourceOrder
      * @throws NoSuchEntityException
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundLoad(
-        \Magento\Sales\Model\ResourceModel\Order $subject,
-        \Closure $proceed,
-        \Magento\Framework\Model\AbstractModel $order,
-        $value,
-        $field = null
+    public function afterLoad(
+        ResourceOrder $subject,
+        ResourceOrder $result,
+        \Magento\Framework\Model\AbstractModel $order
     ) {
-        $result = $proceed($order, $value, $field);
-        if (!$this->isAllowed($order)) {
-            throw NoSuchEntityException::singleField('orderId', $order->getId());
+        if ($order instanceof Order) {
+            if (!$this->isAllowed($order)) {
+                throw NoSuchEntityException::singleField('orderId', $order->getId());
+            }
         }
         return $result;
     }
@@ -57,7 +54,7 @@ class Authorization
      * @param \Magento\Sales\Model\Order $order
      * @return bool
      */
-    protected function isAllowed(\Magento\Sales\Model\Order $order)
+    protected function isAllowed(Order $order)
     {
         return $this->userContext->getUserType() == UserContextInterface::USER_TYPE_CUSTOMER
             ? $order->getCustomerId() == $this->userContext->getUserId()
diff --git a/app/code/Magento/Sales/Model/ShipOrder.php b/app/code/Magento/Sales/Model/ShipOrder.php
new file mode 100644
index 0000000000000000000000000000000000000000..d051144cf73ca166c30ebd6eb9f626306b1720a6
--- /dev/null
+++ b/app/code/Magento/Sales/Model/ShipOrder.php
@@ -0,0 +1,206 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model;
+
+use Magento\Framework\App\ResourceConnection;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Sales\Api\ShipmentRepositoryInterface;
+use Magento\Sales\Api\ShipOrderInterface;
+use Magento\Sales\Model\Order\Config as OrderConfig;
+use Magento\Sales\Model\Order\OrderStateResolverInterface;
+use Magento\Sales\Model\Order\OrderValidatorInterface;
+use Magento\Sales\Model\Order\ShipmentDocumentFactory;
+use Magento\Sales\Model\Order\Shipment\NotifierInterface;
+use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface;
+use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator;
+use Magento\Sales\Model\Order\Shipment\Validation\TrackValidator;
+use Magento\Sales\Model\Order\Validation\CanShip;
+use Magento\Sales\Model\Order\Shipment\OrderRegistrarInterface;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Class ShipOrder
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class ShipOrder implements ShipOrderInterface
+{
+    /**
+     * @var ResourceConnection
+     */
+    private $resourceConnection;
+
+    /**
+     * @var OrderRepositoryInterface
+     */
+    private $orderRepository;
+
+    /**
+     * @var ShipmentDocumentFactory
+     */
+    private $shipmentDocumentFactory;
+
+    /**
+     * @var ShipmentValidatorInterface
+     */
+    private $shipmentValidator;
+
+    /**
+     * @var OrderStateResolverInterface
+     */
+    private $orderStateResolver;
+
+    /**
+     * @var OrderConfig
+     */
+    private $config;
+
+    /**
+     * @var ShipmentRepositoryInterface
+     */
+    private $shipmentRepository;
+
+    /**
+     * @var NotifierInterface
+     */
+    private $notifierInterface;
+
+    /**
+     * @var LoggerInterface
+     */
+    private $logger;
+
+    /**
+     * @var OrderValidatorInterface
+     */
+    private $orderValidator;
+
+    /**
+     * @var OrderRegistrarInterface
+     */
+    private $orderRegistrar;
+
+    /**
+     * @param ResourceConnection $resourceConnection
+     * @param OrderRepositoryInterface $orderRepository
+     * @param ShipmentDocumentFactory $shipmentDocumentFactory
+     * @param ShipmentValidatorInterface $shipmentValidator
+     * @param OrderValidatorInterface $orderValidator
+     * @param OrderStateResolverInterface $orderStateResolver
+     * @param OrderConfig $config
+     * @param ShipmentRepositoryInterface $shipmentRepository
+     * @param NotifierInterface $notifierInterface
+     * @param OrderRegistrarInterface $orderRegistrar
+     * @param LoggerInterface $logger
+     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+     */
+    public function __construct(
+        ResourceConnection $resourceConnection,
+        OrderRepositoryInterface $orderRepository,
+        ShipmentDocumentFactory $shipmentDocumentFactory,
+        ShipmentValidatorInterface $shipmentValidator,
+        OrderValidatorInterface $orderValidator,
+        OrderStateResolverInterface $orderStateResolver,
+        OrderConfig $config,
+        ShipmentRepositoryInterface $shipmentRepository,
+        NotifierInterface $notifierInterface,
+        OrderRegistrarInterface $orderRegistrar,
+        LoggerInterface $logger
+    ) {
+        $this->resourceConnection = $resourceConnection;
+        $this->orderRepository = $orderRepository;
+        $this->shipmentDocumentFactory = $shipmentDocumentFactory;
+        $this->shipmentValidator = $shipmentValidator;
+        $this->orderValidator = $orderValidator;
+        $this->orderStateResolver = $orderStateResolver;
+        $this->config = $config;
+        $this->shipmentRepository = $shipmentRepository;
+        $this->notifierInterface = $notifierInterface;
+        $this->logger = $logger;
+        $this->orderRegistrar = $orderRegistrar;
+    }
+
+    /**
+     * @param int $orderId
+     * @param \Magento\Sales\Api\Data\ShipmentItemCreationInterface[] $items
+     * @param bool $notify
+     * @param bool $appendComment
+     * @param \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|null $comment
+     * @param \Magento\Sales\Api\Data\ShipmentTrackCreationInterface[] $tracks
+     * @param \Magento\Sales\Api\Data\ShipmentPackageCreationInterface[] $packages
+     * @param \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface|null $arguments
+     * @return int
+     * @throws \Magento\Sales\Api\Exception\DocumentValidationExceptionInterface
+     * @throws \Magento\Sales\Api\Exception\CouldNotShipExceptionInterface
+     * @throws \Magento\Framework\Exception\InputException
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws \DomainException
+     */
+    public function execute(
+        $orderId,
+        array $items = [],
+        $notify = false,
+        $appendComment = false,
+        \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null,
+        array $tracks = [],
+        array $packages = [],
+        \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface $arguments = null
+    ) {
+        $connection = $this->resourceConnection->getConnection('sales');
+        $order = $this->orderRepository->get($orderId);
+        $shipment = $this->shipmentDocumentFactory->create(
+            $order,
+            $items,
+            $tracks,
+            $comment,
+            ($appendComment && $notify),
+            $packages,
+            $arguments
+        );
+        $orderValidationResult = $this->orderValidator->validate(
+            $order,
+            [
+                CanShip::class
+            ]
+        );
+        $shipmentValidationResult = $this->shipmentValidator->validate(
+            $shipment,
+            [
+                QuantityValidator::class,
+                TrackValidator::class
+            ]
+        );
+        $validationMessages = array_merge($orderValidationResult, $shipmentValidationResult);
+        if (!empty($validationMessages)) {
+            throw new \Magento\Sales\Exception\DocumentValidationException(
+                __("Shipment Document Validation Error(s):\n" . implode("\n", $validationMessages))
+            );
+        }
+        $connection->beginTransaction();
+        try {
+            $this->orderRegistrar->register($order, $shipment);
+            $order->setState(
+                $this->orderStateResolver->getStateForOrder($order, [OrderStateResolverInterface::IN_PROGRESS])
+            );
+            $order->setStatus($this->config->getStateDefaultStatus($order->getState()));
+            $this->shipmentRepository->save($shipment);
+            $this->orderRepository->save($order);
+            $connection->commit();
+        } catch (\Exception $e) {
+            $this->logger->critical($e);
+            $connection->rollBack();
+            throw new \Magento\Sales\Exception\CouldNotShipException(
+                __('Could not save a shipment, see error log for details')
+            );
+        }
+        if ($notify) {
+            if (!$appendComment) {
+                $comment = null;
+            }
+            $this->notifierInterface->notify($order, $shipment, $comment);
+        }
+        return $shipment->getEntityId();
+    }
+}
diff --git a/app/code/Magento/Sales/Model/Validator.php b/app/code/Magento/Sales/Model/Validator.php
new file mode 100644
index 0000000000000000000000000000000000000000..b8d57ded2970209d5d2184945fbb22f13de47dbd
--- /dev/null
+++ b/app/code/Magento/Sales/Model/Validator.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model;
+
+use Magento\Framework\Exception\ConfigurationMismatchException;
+use Magento\Framework\ObjectManagerInterface;
+
+/**
+ * Class Validator
+ *
+ * @internal
+ */
+class Validator
+{
+    /**
+     * @var ObjectManagerInterface
+     */
+    private $objectManager;
+
+    /**
+     * Validator constructor.
+     *
+     * @param ObjectManagerInterface $objectManager
+     */
+    public function __construct(ObjectManagerInterface $objectManager)
+    {
+        $this->objectManager = $objectManager;
+    }
+
+    /**
+     * @param object $entity
+     * @param ValidatorInterface[] $validators
+     * @return string[]
+     * @throws ConfigurationMismatchException
+     */
+    public function validate($entity, array $validators)
+    {
+        $messages = [];
+        foreach ($validators as $validatorName) {
+            $validator = $this->objectManager->get($validatorName);
+            if (!$validator instanceof ValidatorInterface) {
+                throw new ConfigurationMismatchException(
+                    __(
+                        sprintf('Validator %s is not instance of general validator interface', $validatorName)
+                    )
+                );
+            }
+            $messages = array_merge($messages, $validator->validate($entity));
+        }
+
+        return $messages;
+    }
+}
diff --git a/app/code/Magento/Sales/Model/ValidatorInterface.php b/app/code/Magento/Sales/Model/ValidatorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..1882782e314f7b47b507c042697f223eadbf36b7
--- /dev/null
+++ b/app/code/Magento/Sales/Model/ValidatorInterface.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Model;
+
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Sales\Exception\DocumentValidationException;
+
+/**
+ * Interface ValidatorInterface
+ */
+interface ValidatorInterface
+{
+    /**
+     * @param object $entity
+     * @return array
+     * @throws DocumentValidationException
+     * @throws NoSuchEntityException
+     */
+    public function validate($entity);
+}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/OrderInvoiceTest.php b/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php
similarity index 89%
rename from app/code/Magento/Sales/Test/Unit/Model/OrderInvoiceTest.php
rename to app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php
index bc36da112aa291e82e5f06af887334095544f43f..6dfa929acb6290e0e95bf2011b76ec9811691ecc 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/OrderInvoiceTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php
@@ -14,21 +14,23 @@ use Magento\Sales\Api\Data\OrderInterface;
 use Magento\Sales\Api\OrderRepositoryInterface;
 use Magento\Sales\Model\Order;
 use Magento\Sales\Model\Order\Config as OrderConfig;
+use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface;
 use Magento\Sales\Model\Order\Invoice\NotifierInterface;
 use Magento\Sales\Model\Order\InvoiceDocumentFactory;
 use Magento\Sales\Model\Order\InvoiceRepository;
-use Magento\Sales\Model\Order\InvoiceValidatorInterface;
 use Magento\Sales\Model\Order\OrderStateResolverInterface;
+use Magento\Sales\Model\Order\OrderValidatorInterface;
 use Magento\Sales\Model\Order\PaymentAdapterInterface;
-use Magento\Sales\Model\OrderInvoice;
+use Magento\Sales\Model\InvoiceOrder;
 use Psr\Log\LoggerInterface;
 
 /**
- * Class OrderInvoiceTest
+ * Class InvoiceOrderTest
+ * 
  * @SuppressWarnings(PHPMD.TooManyFields)
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
-class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
+class InvoiceOrderTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject
@@ -50,6 +52,11 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
      */
     private $invoiceValidatorMock;
 
+    /**
+     * @var OrderValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderValidatorMock;
+
     /**
      * @var PaymentAdapterInterface|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -76,9 +83,9 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
     private $notifierInterfaceMock;
 
     /**
-     * @var OrderInvoice|\PHPUnit_Framework_MockObject_MockObject
+     * @var InvoiceOrder|\PHPUnit_Framework_MockObject_MockObject
      */
-    private $orderInvoice;
+    private $invoiceOrder;
 
     /**
      * @var InvoiceCreationArgumentsInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -128,6 +135,10 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
+        $this->orderValidatorMock = $this->getMockBuilder(OrderValidatorInterface::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
         $this->paymentAdapterMock = $this->getMockBuilder(PaymentAdapterInterface::class)
             ->disableOriginalConstructor()
             ->getMock();
@@ -172,11 +183,12 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
-        $this->orderInvoice = new OrderInvoice(
+        $this->invoiceOrder = new InvoiceOrder(
             $this->resourceConnectionMock,
             $this->orderRepositoryMock,
             $this->invoiceDocumentFactoryMock,
             $this->invoiceValidatorMock,
+            $this->orderValidatorMock,
             $this->paymentAdapterMock,
             $this->orderStateResolverMock,
             $this->configMock,
@@ -212,7 +224,11 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
 
         $this->invoiceValidatorMock->expects($this->once())
             ->method('validate')
-            ->with($this->invoiceMock, $this->orderMock)
+            ->with($this->invoiceMock)
+            ->willReturn([]);
+        $this->orderValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->orderMock)
             ->willReturn([]);
 
         $this->paymentAdapterMock->expects($this->once())
@@ -271,7 +287,7 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             2,
-            $this->orderInvoice->execute(
+            $this->invoiceOrder->execute(
                 $orderId,
                 $capture,
                 $items,
@@ -311,10 +327,14 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
 
         $this->invoiceValidatorMock->expects($this->once())
             ->method('validate')
-            ->with($this->invoiceMock, $this->orderMock)
+            ->with($this->invoiceMock)
             ->willReturn($errorMessages);
+        $this->orderValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->orderMock)
+            ->willReturn([]);
 
-        $this->orderInvoice->execute(
+        $this->invoiceOrder->execute(
             $orderId,
             $capture,
             $items,
@@ -356,7 +376,11 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
 
         $this->invoiceValidatorMock->expects($this->once())
             ->method('validate')
-            ->with($this->invoiceMock, $this->orderMock)
+            ->with($this->invoiceMock)
+            ->willReturn([]);
+        $this->orderValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->orderMock)
             ->willReturn([]);
         $e = new \Exception();
 
@@ -372,7 +396,7 @@ class OrderInvoiceTest extends \PHPUnit_Framework_TestCase
         $this->adapterInterface->expects($this->once())
             ->method('rollBack');
 
-        $this->orderInvoice->execute(
+        $this->invoiceOrder->execute(
             $orderId,
             $capture,
             $items,
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceValidatorTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceQuantityValidatorTest.php
similarity index 66%
rename from app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceValidatorTest.php
rename to app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceQuantityValidatorTest.php
index 6fdfdb61b36359d367200e8506ef9484200c723e..8d800e12a6ff0a562330aee3ea09f25f44fae23d 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceValidatorTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/InvoiceQuantityValidatorTest.php
@@ -6,15 +6,16 @@
 
 namespace Magento\Sales\Test\Unit\Model\Order;
 
+use Magento\Sales\Api\OrderRepositoryInterface;
 use Magento\Sales\Model\Order;
 
 /**
  * Test for \Magento\Sales\Model\Order\InvoiceValidator class
  */
-class InvoiceValidatorTest extends \PHPUnit_Framework_TestCase
+class InvoiceQuantityValidatorTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\Sales\Model\Order\InvoiceValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Sales\Model\Order\InvoiceQuantityValidator|\PHPUnit_Framework_MockObject_MockObject
      */
     private $model;
 
@@ -24,14 +25,14 @@ class InvoiceValidatorTest extends \PHPUnit_Framework_TestCase
     private $objectManager;
 
     /**
-     * @var \Magento\Sales\Model\Order\OrderValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Sales\Api\Data\OrderInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    private $orderValidatorMock;
+    private $orderMock;
 
     /**
-     * @var \Magento\Sales\Api\Data\OrderInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Sales\Api\OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    private $orderMock;
+    private $orderRepositoryMock;
 
     /**
      * @var \Magento\Sales\Api\Data\InvoiceInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -42,24 +43,21 @@ class InvoiceValidatorTest extends \PHPUnit_Framework_TestCase
     {
         $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
 
-        $this->orderValidatorMock = $this->getMockBuilder(\Magento\Sales\Model\Order\OrderValidatorInterface::class)
-            ->disableOriginalConstructor()
-            ->setMethods(['canInvoice'])
-            ->getMockForAbstractClass();
-
         $this->orderMock = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderInterface::class)
             ->disableOriginalConstructor()
-            ->setMethods(['getStatus'])
             ->getMockForAbstractClass();
 
         $this->invoiceMock = $this->getMockBuilder(\Magento\Sales\Api\Data\InvoiceInterface::class)
             ->disableOriginalConstructor()
             ->setMethods(['getTotalQty', 'getItems'])
             ->getMockForAbstractClass();
-
+        $this->orderRepositoryMock = $this->getMockBuilder(
+            OrderRepositoryInterface::class
+        )->disableOriginalConstructor()->getMockForAbstractClass();
+        $this->orderRepositoryMock->expects($this->any())->method('get')->willReturn($this->orderMock);
         $this->model = $this->objectManager->getObject(
-            \Magento\Sales\Model\Order\InvoiceValidator::class,
-            ['orderValidator' => $this->orderValidatorMock]
+            \Magento\Sales\Model\Order\InvoiceQuantityValidator::class,
+            ['orderRepository' => $this->orderRepositoryMock]
         );
     }
 
@@ -75,39 +73,12 @@ class InvoiceValidatorTest extends \PHPUnit_Framework_TestCase
         $this->orderMock->expects($this->once())
             ->method('getItems')
             ->willReturn([$orderItemMock]);
-        $this->orderValidatorMock->expects($this->once())
-            ->method('canInvoice')
-            ->with($this->orderMock)
-            ->willReturn(true);
-        $this->assertEquals(
-            $expectedResult,
-            $this->model->validate($this->invoiceMock, $this->orderMock)
-        );
-    }
-
-    public function testValidateCanNotInvoiceOrder()
-    {
-        $orderStatus = 'Test Status';
-        $expectedResult = [__('An invoice cannot be created when an order has a status of %1.', $orderStatus)];
-        $invoiceItemMock = $this->getInvoiceItemMock(1, 1);
-        $this->invoiceMock->expects($this->once())
-            ->method('getItems')
-            ->willReturn([$invoiceItemMock]);
-
-        $orderItemMock = $this->getOrderItemMock(1, 1, true);
-        $this->orderMock->expects($this->once())
-            ->method('getItems')
-            ->willReturn([$orderItemMock]);
-        $this->orderMock->expects($this->once())
-            ->method('getStatus')
-            ->willReturn($orderStatus);
-        $this->orderValidatorMock->expects($this->once())
-            ->method('canInvoice')
-            ->with($this->orderMock)
-            ->willReturn(false);
+        $this->invoiceMock->expects($this->exactly(2))
+            ->method('getOrderId')
+            ->willReturn(1);
         $this->assertEquals(
             $expectedResult,
-            $this->model->validate($this->invoiceMock, $this->orderMock)
+            $this->model->validate($this->invoiceMock)
         );
     }
 
@@ -125,13 +96,12 @@ class InvoiceValidatorTest extends \PHPUnit_Framework_TestCase
         $this->orderMock->expects($this->once())
             ->method('getItems')
             ->willReturn([$orderItemMock]);
-        $this->orderValidatorMock->expects($this->once())
-            ->method('canInvoice')
-            ->with($this->orderMock)
-            ->willReturn(true);
+        $this->invoiceMock->expects($this->exactly(2))
+            ->method('getOrderId')
+            ->willReturn(1);
         $this->assertEquals(
             $expectedResult,
-            $this->model->validate($this->invoiceMock, $this->orderMock)
+            $this->model->validate($this->invoiceMock)
         );
     }
 
@@ -146,13 +116,21 @@ class InvoiceValidatorTest extends \PHPUnit_Framework_TestCase
         $this->orderMock->expects($this->once())
             ->method('getItems')
             ->willReturn([]);
-        $this->orderValidatorMock->expects($this->once())
-            ->method('canInvoice')
-            ->with($this->orderMock)
-            ->willReturn(true);
+        $this->invoiceMock->expects($this->exactly(2))
+            ->method('getOrderId')
+            ->willReturn(1);
+        $this->assertEquals(
+            $expectedResult,
+            $this->model->validate($this->invoiceMock)
+        );
+    }
+
+    public function testValidateNoOrder()
+    {
+        $expectedResult = [__('Order Id is required for invoice document')];
         $this->assertEquals(
             $expectedResult,
-            $this->model->validate($this->invoiceMock, $this->orderMock)
+            $this->model->validate($this->invoiceMock)
         );
     }
 
@@ -169,13 +147,12 @@ class InvoiceValidatorTest extends \PHPUnit_Framework_TestCase
         $this->orderMock->expects($this->once())
             ->method('getItems')
             ->willReturn([$orderItemMock]);
-        $this->orderValidatorMock->expects($this->once())
-            ->method('canInvoice')
-            ->with($this->orderMock)
-            ->willReturn(true);
+        $this->invoiceMock->expects($this->exactly(2))
+            ->method('getOrderId')
+            ->willReturn(1);
         $this->assertEquals(
             $expectedResult,
-            $this->model->validate($this->invoiceMock, $this->orderMock)
+            $this->model->validate($this->invoiceMock)
         );
     }
 
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/OrderRegistrarTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/OrderRegistrarTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e5bff791edcca99d72918eaacb915a4257b60e37
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/OrderRegistrarTest.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Test\Unit\Model\Order\Shipment;
+
+class OrderRegistrarTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Sales\Model\Order\Shipment\OrderRegistrar
+     */
+    private $model;
+
+    /**
+     * @var \Magento\Sales\Api\Data\OrderInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderMock;
+
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentMock;
+
+    protected function setUp()
+    {
+        $this->orderMock = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+        $this->shipmentMock = $this->getMockBuilder(\Magento\Sales\Api\Data\ShipmentInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->model = new \Magento\Sales\Model\Order\Shipment\OrderRegistrar();
+    }
+
+    public function testRegister()
+    {
+        $item1 = $this->getShipmentItemMock();
+        $item1->expects($this->once())
+            ->method('getQty')
+            ->willReturn(0);
+        $item1->expects($this->never())
+            ->method('register');
+
+        $item2 = $this->getShipmentItemMock();
+        $item2->expects($this->once())
+            ->method('getQty')
+            ->willReturn(0.5);
+        $item2->expects($this->once())
+            ->method('register');
+
+        $items = [$item1, $item2];
+        $this->shipmentMock->expects($this->once())
+            ->method('getItems')
+            ->willReturn($items);
+        $this->assertEquals(
+            $this->orderMock,
+            $this->model->register($this->orderMock, $this->shipmentMock)
+        );
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject
+     */
+    private function getShipmentItemMock()
+    {
+        return $this->getMockBuilder(\Magento\Sales\Api\Data\ShipmentItemInterface::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['register'])
+            ->getMockForAbstractClass();
+    }
+}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8373c7e57d0fe32a400a02f0f77362b9676bf3f6
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php
@@ -0,0 +1,361 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Test\Unit\Model\Order\Shipment\Sender;
+
+/**
+ * Unit test for email notification sender for Shipment.
+ *
+ * @SuppressWarnings(PHPMD.TooManyFields)
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class EmailSenderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Sales\Model\Order\Shipment\Sender\EmailSender
+     */
+    private $subject;
+
+    /**
+     * @var \Magento\Sales\Model\Order|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderMock;
+
+    /**
+     * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeMock;
+
+    /**
+     * @var \Magento\Sales\Model\Order\Email\Sender|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $senderMock;
+
+    /**
+     * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $loggerMock;
+
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentMock;
+
+    /**
+     * @var \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $commentMock;
+
+    /**
+     * @var \Magento\Sales\Model\Order\Address|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $addressMock;
+
+    /**
+     * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $globalConfigMock;
+
+    /**
+     * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $eventManagerMock;
+
+    /**
+     * @var \Magento\Payment\Model\Info|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $paymentInfoMock;
+
+    /**
+     * @var \Magento\Payment\Helper\Data|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $paymentHelperMock;
+
+    /**
+     * @var \Magento\Sales\Model\ResourceModel\Order\Shipment|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentResourceMock;
+
+    /**
+     * @var \Magento\Sales\Model\Order\Address\Renderer|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $addressRendererMock;
+
+    /**
+     * @var \Magento\Sales\Model\Order\Email\Container\Template|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $templateContainerMock;
+
+    /**
+     * @var \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $identityContainerMock;
+
+    /**
+     * @var \Magento\Sales\Model\Order\Email\SenderBuilderFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $senderBuilderFactoryMock;
+
+    /**
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
+    protected function setUp()
+    {
+        $this->orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class)
+            ->setMethods(['getStoreId'])
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->storeMock->expects($this->any())
+            ->method('getStoreId')
+            ->willReturn(1);
+        $this->orderMock->expects($this->any())
+            ->method('getStore')
+            ->willReturn($this->storeMock);
+
+        $this->senderMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Email\Sender::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['send', 'sendCopyTo'])
+            ->getMock();
+
+        $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->shipmentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Shipment::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['setSendEmail', 'setEmailSent'])
+            ->getMock();
+
+        $this->commentMock = $this->getMockBuilder(\Magento\Sales\Api\Data\ShipmentCommentCreationInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->commentMock->expects($this->any())
+            ->method('getComment')
+            ->willReturn('Comment text');
+
+        $this->addressMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Address::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->orderMock->expects($this->any())
+            ->method('getBillingAddress')
+            ->willReturn($this->addressMock);
+        $this->orderMock->expects($this->any())
+            ->method('getShippingAddress')
+            ->willReturn($this->addressMock);
+
+        $this->globalConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->eventManagerMock = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->paymentInfoMock = $this->getMockBuilder(\Magento\Payment\Model\Info::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->orderMock->expects($this->any())
+            ->method('getPayment')
+            ->willReturn($this->paymentInfoMock);
+
+        $this->paymentHelperMock = $this->getMockBuilder(\Magento\Payment\Helper\Data::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->paymentHelperMock->expects($this->any())
+            ->method('getInfoBlockHtml')
+            ->with($this->paymentInfoMock, 1)
+            ->willReturn('Payment Info Block');
+
+        $this->shipmentResourceMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Shipment::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->addressRendererMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Address\Renderer::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->addressRendererMock->expects($this->any())
+            ->method('format')
+            ->with($this->addressMock, 'html')
+            ->willReturn('Formatted address');
+
+        $this->templateContainerMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Email\Container\Template::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->identityContainerMock = $this->getMockBuilder(
+            \Magento\Sales\Model\Order\Email\Container\ShipmentIdentity::class
+        )
+        ->disableOriginalConstructor()
+        ->getMock();
+
+        $this->identityContainerMock->expects($this->any())
+            ->method('getStore')
+            ->willReturn($this->storeMock);
+
+        $this->senderBuilderFactoryMock = $this->getMockBuilder(
+            \Magento\Sales\Model\Order\Email\SenderBuilderFactory::class
+        )
+        ->disableOriginalConstructor()
+        ->setMethods(['create'])
+        ->getMock();
+
+        $this->subject = new \Magento\Sales\Model\Order\Shipment\Sender\EmailSender(
+            $this->templateContainerMock,
+            $this->identityContainerMock,
+            $this->senderBuilderFactoryMock,
+            $this->loggerMock,
+            $this->addressRendererMock,
+            $this->paymentHelperMock,
+            $this->shipmentResourceMock,
+            $this->globalConfigMock,
+            $this->eventManagerMock
+        );
+    }
+
+    /**
+     * @param int $configValue
+     * @param bool $forceSyncMode
+     * @param bool $isComment
+     * @param bool $emailSendingResult
+     *
+     * @dataProvider sendDataProvider
+     *
+     * @return void
+     *
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
+    public function testSend($configValue, $forceSyncMode, $isComment, $emailSendingResult)
+    {
+        $this->globalConfigMock->expects($this->once())
+            ->method('getValue')
+            ->with('sales_email/general/async_sending')
+            ->willReturn($configValue);
+
+        if (!$isComment) {
+            $this->commentMock = null;
+        }
+
+        $this->shipmentMock->expects($this->once())
+            ->method('setSendEmail')
+            ->with(true);
+
+        if (!$configValue || $forceSyncMode) {
+            $transport = [
+                'order' => $this->orderMock,
+                'shipment' => $this->shipmentMock,
+                'comment' => $isComment ? 'Comment text' : '',
+                'billing' => $this->addressMock,
+                'payment_html' => 'Payment Info Block',
+                'store' => $this->storeMock,
+                'formattedShippingAddress' => 'Formatted address',
+                'formattedBillingAddress' => 'Formatted address',
+            ];
+
+            $this->eventManagerMock->expects($this->once())
+                ->method('dispatch')
+                ->with(
+                    'email_shipment_set_template_vars_before',
+                    [
+                        'sender' => $this->subject,
+                        'transport' => $transport,
+                    ]
+                );
+
+            $this->templateContainerMock->expects($this->once())
+                ->method('setTemplateVars')
+                ->with($transport);
+
+            $this->identityContainerMock->expects($this->once())
+                ->method('isEnabled')
+                ->willReturn($emailSendingResult);
+
+            if ($emailSendingResult) {
+                $this->senderBuilderFactoryMock->expects($this->once())
+                    ->method('create')
+                    ->willReturn($this->senderMock);
+
+                $this->senderMock->expects($this->once())
+                    ->method('send');
+
+                $this->senderMock->expects($this->once())
+                    ->method('sendCopyTo');
+
+                $this->shipmentMock->expects($this->once())
+                    ->method('setEmailSent')
+                    ->with(true);
+
+                $this->shipmentResourceMock->expects($this->once())
+                    ->method('saveAttribute')
+                    ->with($this->shipmentMock, ['send_email', 'email_sent']);
+
+                $this->assertTrue(
+                    $this->subject->send(
+                        $this->orderMock,
+                        $this->shipmentMock,
+                        $this->commentMock,
+                        $forceSyncMode
+                    )
+                );
+            } else {
+                $this->shipmentResourceMock->expects($this->once())
+                    ->method('saveAttribute')
+                    ->with($this->shipmentMock, 'send_email');
+
+                $this->assertFalse(
+                    $this->subject->send(
+                        $this->orderMock,
+                        $this->shipmentMock,
+                        $this->commentMock,
+                        $forceSyncMode
+                    )
+                );
+            }
+        } else {
+            $this->shipmentMock->expects($this->once())
+                ->method('setEmailSent')
+                ->with(null);
+
+            $this->shipmentResourceMock->expects($this->at(0))
+                ->method('saveAttribute')
+                ->with($this->shipmentMock, 'email_sent');
+            $this->shipmentResourceMock->expects($this->at(1))
+                ->method('saveAttribute')
+                ->with($this->shipmentMock, 'send_email');
+
+            $this->assertFalse(
+                $this->subject->send(
+                    $this->orderMock,
+                    $this->shipmentMock,
+                    $this->commentMock,
+                    $forceSyncMode
+                )
+            );
+        }
+    }
+
+    /**
+     * @return array
+     */
+    public function sendDataProvider()
+    {
+        return [
+            'Successful sync sending with comment' => [0, false, true, true],
+            'Successful sync sending without comment' => [0, false, false, true],
+            'Failed sync sending with comment' => [0, false, true, false],
+            'Successful forced sync sending with comment' => [1, true, true, true],
+            'Async sending' => [1, false, false, false],
+        ];
+    }
+}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Validation/QuantityValidatorTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Validation/QuantityValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..01cccd2458695247f2c67f8e35fd40a71dc1b757
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Validation/QuantityValidatorTest.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Test\Unit\Model\Order\Shipment\Validation;
+
+use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Sales\Api\Data\ShipmentInterface;
+use Magento\Sales\Api\Data\ShipmentItemInterface;
+
+/**
+ * Class QuantityValidatorTest
+ */
+class QuantityValidatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var QuantityValidator
+     */
+    private $validator;
+
+    /**
+     * @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentMock;
+
+    /**
+     * @var ShipmentItemInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentItemMock;
+
+    protected function setUp()
+    {
+        $objectManagerHelper = new ObjectManager($this);
+        $this->shipmentMock = $this->getMockBuilder(ShipmentInterface::class)
+            ->getMock();
+        $this->shipmentItemMock = $this->getMockBuilder(ShipmentItemInterface::class)
+            ->getMock();
+        $this->validator = $objectManagerHelper->getObject(QuantityValidator::class);
+    }
+
+    public function testValidateTrackWithoutOrderId()
+    {
+        $this->shipmentMock->expects($this->once())
+            ->method('getOrderId')
+            ->willReturn(null);
+        $this->assertEquals(
+            [__('Order Id is required for shipment document')],
+            $this->validator->validate($this->shipmentMock)
+        );
+    }
+
+    public function testValidateTrackWithoutItems()
+    {
+        $this->shipmentMock->expects($this->once())
+            ->method('getOrderId')
+            ->willReturn(1);
+        $this->shipmentMock->expects($this->once())
+            ->method('getItems')
+            ->willReturn(null);
+        $this->assertEquals(
+            [__('You can\'t create a shipment without products.')],
+            $this->validator->validate($this->shipmentMock)
+        );
+    }
+}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Validation/TrackValidatorTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Validation/TrackValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d8d951ccf18acd29649b8de05a5c3f97ce5a8c1
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Validation/TrackValidatorTest.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Test\Unit\Model\Order\Shipment\Validation;
+
+use Magento\Sales\Model\Order\Shipment\Validation\TrackValidator;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Sales\Api\Data\ShipmentInterface;
+use Magento\Sales\Api\Data\ShipmentTrackInterface;
+
+/**
+ * Class TrackValidatorTest
+ */
+class TrackValidatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var TrackValidator
+     */
+    private $validator;
+
+    /**
+     * @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentMock;
+
+    /**
+     * @var ShipmentTrackInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentTrackMock;
+
+    protected function setUp()
+    {
+        $objectManagerHelper = new ObjectManager($this);
+        $this->shipmentMock = $this->getMockBuilder(ShipmentInterface::class)
+            ->getMockForAbstractClass();
+        $this->shipmentTrackMock = $this->getMockBuilder(ShipmentTrackInterface::class)
+            ->getMockForAbstractClass();
+        $this->validator = $objectManagerHelper->getObject(TrackValidator::class);
+    }
+
+    public function testValidateTrackWithNumber()
+    {
+        $this->shipmentTrackMock->expects($this->once())
+            ->method('getTrackNumber')
+            ->willReturn('12345');
+        $this->shipmentMock->expects($this->exactly(2))
+            ->method('getTracks')
+            ->willReturn([$this->shipmentTrackMock]);
+        $this->assertEquals([], $this->validator->validate($this->shipmentMock));
+    }
+
+    public function testValidateTrackWithoutNumber()
+    {
+        $this->shipmentTrackMock->expects($this->once())
+            ->method('getTrackNumber')
+            ->willReturn(null);
+        $this->shipmentMock->expects($this->exactly(2))
+            ->method('getTracks')
+            ->willReturn([$this->shipmentTrackMock]);
+        $this->assertEquals([__('Please enter a tracking number.')], $this->validator->validate($this->shipmentMock));
+    }
+
+    public function testValidateTrackWithEmptyTracks()
+    {
+        $this->shipmentTrackMock->expects($this->never())
+            ->method('getTrackNumber');
+        $this->shipmentMock->expects($this->once())
+            ->method('getTracks')
+            ->willReturn([]);
+        $this->assertEquals([], $this->validator->validate($this->shipmentMock));
+    }
+}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b0677b050f6fbe4f7b1894a34481a6aa59fcecd9
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentDocumentFactoryTest.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Test\Unit\Model\Order;
+
+use Magento\Framework\EntityManager\HydratorPool;
+use Magento\Sales\Api\Data\ShipmentCommentCreationInterface;
+use Magento\Sales\Api\Data\ShipmentItemCreationInterface;
+use Magento\Sales\Api\Data\ShipmentTrackCreationInterface;
+use Magento\Sales\Model\Order\ShipmentFactory;
+use Magento\Sales\Model\Order\ShipmentDocumentFactory;
+use Magento\Sales\Model\Order;
+use Magento\Sales\Api\Data\ShipmentInterface;
+use Magento\Sales\Model\Order\Shipment\TrackFactory;
+use Magento\Sales\Model\Order\Shipment\Track;
+use Magento\Framework\EntityManager\HydratorInterface;
+
+/**
+ * Class ShipmentDocumentFactoryTest
+ */
+class ShipmentDocumentFactoryTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|ShipmentFactory
+     */
+    private $shipmentFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|Order
+     */
+    private $orderMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|ShipmentItemCreationInterface
+     */
+    private $itemMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|ShipmentCommentCreationInterface
+     */
+    private $commentMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|ShipmentInterface
+     */
+    private $shipmentMock;
+
+    /**
+     * @var ShipmentDocumentFactory
+     */
+    private $shipmentDocumentFactory;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|HydratorPool
+     */
+    private $hydratorPoolMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|TrackFactory
+     */
+    private $trackFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|HydratorInterface
+     */
+    private $hydratorMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|Track
+     */
+    private $trackMock;
+
+    protected function setUp()
+    {
+        $this->shipmentFactoryMock = $this->getMockBuilder(ShipmentFactory::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->orderMock = $this->getMockBuilder(Order::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->itemMock = $this->getMockBuilder(ShipmentItemCreationInterface::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->commentMock = $this->getMockBuilder(ShipmentCommentCreationInterface::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->shipmentMock = $this->getMockBuilder(ShipmentInterface::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['addComment', 'addTrack'])
+            ->getMockForAbstractClass();
+
+        $this->hydratorPoolMock = $this->getMockBuilder(HydratorPool::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->trackFactoryMock = $this->getMockBuilder(TrackFactory::class)
+            ->setMethods(['create'])
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->trackMock = $this->getMockBuilder(Track::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->hydratorMock = $this->getMockBuilder(HydratorInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->shipmentDocumentFactory = new ShipmentDocumentFactory(
+            $this->shipmentFactoryMock,
+            $this->hydratorPoolMock,
+            $this->trackFactoryMock
+        );
+    }
+
+    public function testCreate()
+    {
+        $trackNum = "123456789";
+        $trackData = [$trackNum];
+        $tracks = [$this->trackMock];
+        $appendComment = true;
+        $packages = [];
+        $items = [1 => 10];
+
+        $this->itemMock->expects($this->once())
+            ->method('getOrderItemId')
+            ->willReturn(1);
+
+        $this->itemMock->expects($this->once())
+            ->method('getQty')
+            ->willReturn(10);
+
+        $this->shipmentFactoryMock->expects($this->once())
+            ->method('create')
+            ->with(
+                $this->orderMock,
+                $items
+            )
+            ->willReturn($this->shipmentMock);
+
+        $this->shipmentMock->expects($this->once())
+            ->method('addTrack')
+            ->willReturnSelf();
+
+        $this->hydratorPoolMock->expects($this->once())
+            ->method('getHydrator')
+            ->with(ShipmentTrackCreationInterface::class)
+            ->willReturn($this->hydratorMock);
+
+        $this->hydratorMock->expects($this->once())
+            ->method('extract')
+            ->with($this->trackMock)
+            ->willReturn($trackData);
+
+        $this->trackFactoryMock->expects($this->once())
+            ->method('create')
+            ->with(['data' => $trackData])
+            ->willReturn($this->trackMock);
+
+        if ($appendComment) {
+            $comment = "New comment!";
+            $visibleOnFront = true;
+            $this->commentMock->expects($this->once())
+                ->method('getComment')
+                ->willReturn($comment);
+
+            $this->commentMock->expects($this->once())
+                ->method('getIsVisibleOnFront')
+                ->willReturn($visibleOnFront);
+
+            $this->shipmentMock->expects($this->once())
+                ->method('addComment')
+                ->with($comment, $appendComment, $visibleOnFront)
+                ->willReturnSelf();
+        }
+
+        $this->assertEquals(
+            $this->shipmentDocumentFactory->create(
+                $this->orderMock,
+                [$this->itemMock],
+                $tracks,
+                $this->commentMock,
+                $appendComment,
+                $packages
+            ),
+            $this->shipmentMock
+        );
+    }
+}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentFactoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentFactoryTest.php
index 3760934457a85eae8f3e4f6f4b8070b1ef7d716a..46d6ac62fc256a45b76edb12c787f4627f8f0ed7 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentFactoryTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentFactoryTest.php
@@ -5,10 +5,9 @@
  */
 namespace Magento\Sales\Test\Unit\Model\Order;
 
-use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
-
 /**
  * Unit test for shipment factory class.
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class ShipmentFactoryTest extends \PHPUnit_Framework_TestCase
 {
@@ -39,7 +38,7 @@ class ShipmentFactoryTest extends \PHPUnit_Framework_TestCase
      */
     protected function setUp()
     {
-        $objectManager = new ObjectManager($this);
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
 
         $this->converter = $this->getMock(
             \Magento\Sales\Model\Convert\Order::class,
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/OrderValidatorTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Validation/CanInvoiceTest.php
similarity index 77%
rename from app/code/Magento/Sales/Test/Unit/Model/Order/OrderValidatorTest.php
rename to app/code/Magento/Sales/Test/Unit/Model/Order/Validation/CanInvoiceTest.php
index 905d7c7a5b3f89951afcd261904a99df38f27ce5..dd76bc1e52586fe57a68d1e5c3278441a18eddd6 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/OrderValidatorTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Validation/CanInvoiceTest.php
@@ -4,17 +4,17 @@
  * See COPYING.txt for license details.
  */
 
-namespace Magento\Sales\Test\Unit\Model\Order;
+namespace Magento\Sales\Test\Unit\Model\Order\Validation;
 
 use Magento\Sales\Model\Order;
 
 /**
  * Test for \Magento\Sales\Model\Order\OrderValidator class
  */
-class OrderValidatorTest extends \PHPUnit_Framework_TestCase
+class CanInvoiceTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\Sales\Model\Order\OrderValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Sales\Model\Order\Validation\CanInvoice|\PHPUnit_Framework_MockObject_MockObject
      */
     private $model;
 
@@ -47,7 +47,7 @@ class OrderValidatorTest extends \PHPUnit_Framework_TestCase
             ->setMethods(['getQtyToInvoice', 'getLockedDoInvoice'])
             ->getMockForAbstractClass();
 
-        $this->model = new \Magento\Sales\Model\Order\OrderValidator();
+        $this->model = new \Magento\Sales\Model\Order\Validation\CanInvoice();
     }
 
     /**
@@ -62,9 +62,12 @@ class OrderValidatorTest extends \PHPUnit_Framework_TestCase
             ->willReturn($state);
         $this->orderMock->expects($this->never())
             ->method('getItems');
+        $this->orderMock->expects($this->once())
+            ->method('getStatus')
+            ->willReturn('status');
         $this->assertEquals(
-            false,
-            $this->model->canInvoice($this->orderMock)
+            [__('An invoice cannot be created when an order has a status of %1', 'status')],
+            $this->model->validate($this->orderMock)
         );
     }
 
@@ -93,9 +96,8 @@ class OrderValidatorTest extends \PHPUnit_Framework_TestCase
             ->method('getItems')
             ->willReturn([]);
 
-        $this->assertEquals(
-            false,
-            $this->model->canInvoice($this->orderMock)
+        $this->assertNotEmpty(
+            $this->model->validate($this->orderMock)
         );
     }
 
@@ -125,7 +127,7 @@ class OrderValidatorTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             $expectedResult,
-            $this->model->canInvoice($this->orderMock)
+            $this->model->validate($this->orderMock)
         );
     }
 
@@ -137,10 +139,10 @@ class OrderValidatorTest extends \PHPUnit_Framework_TestCase
     public function canInvoiceDataProvider()
     {
         return [
-            [0, null, false],
-            [-1, null, false],
-            [1, true, false],
-            [0.5, false, true],
+            [0, null, [__('The order does not allow an invoice to be created.')]],
+            [-1, null, [__('The order does not allow an invoice to be created.')]],
+            [1, true, [__('The order does not allow an invoice to be created.')]],
+            [0.5, false, []],
         ];
     }
 }
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Validation/CanShipTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Validation/CanShipTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..11d99fbb9cced0e9a9d88de161898df42c5a71d7
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Validation/CanShipTest.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Sales\Test\Unit\Model\Order\Validation;
+
+use Magento\Sales\Model\Order;
+
+/**
+ * Test for \Magento\Sales\Model\Order\Validation\CanShip class
+ */
+class CanShipTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Sales\Model\Order\Validation\CanShip|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $model;
+
+    /**
+     * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var \Magento\Sales\Api\Data\OrderInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderMock;
+
+    /**
+     * @var \Magento\Sales\Api\Data\OrderItemInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderItemMock;
+
+    protected function setUp()
+    {
+        $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+        $this->orderMock = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderInterface::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getStatus', 'getItems'])
+            ->getMockForAbstractClass();
+
+        $this->orderItemMock = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderItemInterface::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getQtyToShip', 'getLockedDoShip'])
+            ->getMockForAbstractClass();
+
+        $this->model = new \Magento\Sales\Model\Order\Validation\CanShip();
+    }
+
+    /**
+     * @param string $state
+     *
+     * @dataProvider canShipWrongStateDataProvider
+     */
+    public function testCanShipWrongState($state)
+    {
+        $this->orderMock->expects($this->any())
+            ->method('getState')
+            ->willReturn($state);
+        $this->orderMock->expects($this->once())
+            ->method('getStatus')
+            ->willReturn('status');
+        $this->orderMock->expects($this->never())
+            ->method('getItems');
+        $this->assertEquals(
+            [__('A shipment cannot be created when an order has a status of %1', 'status')],
+            $this->model->validate($this->orderMock)
+        );
+    }
+
+    /**
+     * Data provider for testCanShipWrongState
+     * @return array
+     */
+    public function canShipWrongStateDataProvider()
+    {
+        return [
+            [Order::STATE_PAYMENT_REVIEW],
+            [Order::STATE_HOLDED],
+            [Order::STATE_CANCELED],
+        ];
+    }
+
+    public function testCanShipNoItems()
+    {
+        $this->orderMock->expects($this->any())
+            ->method('getState')
+            ->willReturn(Order::STATE_PROCESSING);
+
+        $this->orderMock->expects($this->once())
+            ->method('getItems')
+            ->willReturn([]);
+
+        $this->assertNotEmpty(
+            $this->model->validate($this->orderMock)
+        );
+    }
+
+    /**
+     * @param float $qtyToShipment
+     * @param bool|null $itemLockedDoShipment
+     * @param bool $expectedResult
+     *
+     * @dataProvider canShipDataProvider
+     */
+    public function testCanShip($qtyToShipment, $itemLockedDoShipment, $expectedResult)
+    {
+        $this->orderMock->expects($this->any())
+            ->method('getState')
+            ->willReturn(Order::STATE_PROCESSING);
+
+        $items = [$this->orderItemMock];
+        $this->orderMock->expects($this->once())
+            ->method('getItems')
+            ->willReturn($items);
+        $this->orderItemMock->expects($this->any())
+            ->method('getQtyToShip')
+            ->willReturn($qtyToShipment);
+        $this->orderItemMock->expects($this->any())
+            ->method('getLockedDoShip')
+            ->willReturn($itemLockedDoShipment);
+
+        $this->assertEquals(
+            $expectedResult,
+            $this->model->validate($this->orderMock)
+        );
+    }
+
+    /**
+     * Data provider for testCanShip
+     *
+     * @return array
+     */
+    public function canShipDataProvider()
+    {
+        return [
+            [0, null, [__('The order does not allow a shipment to be created.')]],
+            [-1, null, [__('The order does not allow a shipment to be created.')]],
+            [1, true, [__('The order does not allow a shipment to be created.')]],
+            [0.5, false, []],
+        ];
+    }
+}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Plugin/AuthorizationTest.php b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Plugin/AuthorizationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..eeee0b2b910ea3d4d793952471fa1958de618f30
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Plugin/AuthorizationTest.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Test\Unit\Model\ResourceModel\Order\Plugin;
+
+use Magento\Authorization\Model\UserContextInterface;
+use Magento\Sales\Model\ResourceModel\Order as ResourceOrder;
+use Magento\Sales\Model\Order;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Sales\Model\ResourceModel\Order\Plugin\Authorization;
+
+class AuthorizationTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * @var UserContextInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $userContextMock;
+
+    /**
+     * @var ResourceOrder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $subjectMock;
+
+    /**
+     * @var Order|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderMock;
+
+    /**
+     * @var Authorization
+     */
+    private $plugin;
+
+    protected function setUp()
+    {
+        $this->objectManager = new ObjectManager($this);
+        $this->userContextMock = $this->getMockBuilder(UserContextInterface::class)
+            ->setMethods(['getUserType', 'getUserId'])
+            ->getMockForAbstractClass();
+        $this->subjectMock = $this->getMockBuilder(ResourceOrder::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->orderMock = $this->getMockBuilder(Order::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getCustomerId', 'getId'])
+            ->getMock();
+        $this->plugin = $this->objectManager->getObject(
+            Authorization::class,
+            ['userContext' => $this->userContextMock]
+        );
+    }
+
+    public function testAfterLoad()
+    {
+        $this->userContextMock->expects($this->once())
+            ->method('getUserType')
+            ->willReturn('testType');
+        $this->assertEquals(
+            $this->subjectMock,
+            $this->plugin->afterLoad($this->subjectMock, $this->subjectMock, $this->orderMock)
+        );
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedExceptionMessage No such entity with orderId = 1
+     */
+    public function testAfterLoadWithException()
+    {
+        $this->userContextMock->expects($this->once())
+            ->method('getUserType')
+            ->willReturn(UserContextInterface::USER_TYPE_CUSTOMER);
+        $this->orderMock->expects($this->once())
+            ->method('getCustomerId')
+            ->willReturn(1);
+        $this->userContextMock->expects($this->once())
+            ->method('getUserId')
+            ->willReturn(2);
+        $this->orderMock->expects($this->once())
+            ->method('getId')
+            ->willReturn(1);
+        $this->plugin->afterLoad($this->subjectMock, $this->subjectMock, $this->orderMock);
+    }
+}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/ShipOrderTest.php b/app/code/Magento/Sales/Test/Unit/Model/ShipOrderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b719babf209f0ceb98c4d019f1e5929b23e7e11f
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Unit/Model/ShipOrderTest.php
@@ -0,0 +1,430 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Test\Unit\Model;
+
+use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\DB\Adapter\AdapterInterface;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\Data\ShipmentCommentCreationInterface;
+use Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface;
+use Magento\Sales\Api\Data\ShipmentInterface;
+use Magento\Sales\Api\Data\ShipmentPackageInterface;
+use Magento\Sales\Api\Data\ShipmentTrackCreationInterface;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Sales\Api\ShipmentRepositoryInterface;
+use Magento\Sales\Model\Order;
+use Magento\Sales\Model\Order\Config as OrderConfig;
+use Magento\Sales\Model\Order\OrderStateResolverInterface;
+use Magento\Sales\Model\Order\OrderValidatorInterface;
+use Magento\Sales\Model\Order\ShipmentDocumentFactory;
+use Magento\Sales\Model\Order\Shipment\NotifierInterface;
+use Magento\Sales\Model\Order\Shipment\OrderRegistrarInterface;
+use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface;
+use Magento\Sales\Model\ShipOrder;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Class ShipOrderTest
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.TooManyFields)
+ */
+class ShipOrderTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $resourceConnectionMock;
+
+    /**
+     * @var OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderRepositoryMock;
+
+    /**
+     * @var ShipmentDocumentFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentDocumentFactoryMock;
+
+    /**
+     * @var ShipmentValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentValidatorMock;
+
+    /**
+     * @var OrderValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderValidatorMock;
+
+    /**
+     * @var OrderRegistrarInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderRegistrarMock;
+
+    /**
+     * @var OrderStateResolverInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderStateResolverMock;
+
+    /**
+     * @var OrderConfig|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $configMock;
+
+    /**
+     * @var ShipmentRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentRepositoryMock;
+
+    /**
+     * @var NotifierInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $notifierInterfaceMock;
+
+    /**
+     * @var ShipOrder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $model;
+
+    /**
+     * @var ShipmentCreationArgumentsInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentCommentCreationMock;
+
+    /**
+     * @var ShipmentCommentCreationInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentCreationArgumentsMock;
+
+    /**
+     * @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $orderMock;
+
+    /**
+     * @var ShipmentInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentMock;
+
+    /**
+     * @var AdapterInterface
+     */
+    private $adapterMock;
+
+    /**
+     * @var ShipmentTrackCreationInterface
+     */
+    private $trackMock;
+
+    /**
+     * @var ShipmentPackageInterface
+     */
+    private $packageMock;
+
+    /**
+     * @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $loggerMock;
+
+    protected function setUp()
+    {
+        $this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->orderRepositoryMock = $this->getMockBuilder(OrderRepositoryInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->shipmentDocumentFactoryMock = $this->getMockBuilder(ShipmentDocumentFactory::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->shipmentValidatorMock = $this->getMockBuilder(ShipmentValidatorInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->orderValidatorMock = $this->getMockBuilder(OrderValidatorInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->orderRegistrarMock = $this->getMockBuilder(OrderRegistrarInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->orderStateResolverMock = $this->getMockBuilder(OrderStateResolverInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->configMock = $this->getMockBuilder(OrderConfig::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->shipmentRepositoryMock = $this->getMockBuilder(ShipmentRepositoryInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->notifierInterfaceMock = $this->getMockBuilder(NotifierInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->loggerMock = $this->getMockBuilder(LoggerInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->shipmentCommentCreationMock = $this->getMockBuilder(ShipmentCommentCreationInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->shipmentCreationArgumentsMock = $this->getMockBuilder(ShipmentCreationArgumentsInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->orderMock = $this->getMockBuilder(OrderInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->shipmentMock = $this->getMockBuilder(ShipmentInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->packageMock = $this->getMockBuilder(ShipmentPackageInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->trackMock = $this->getMockBuilder(ShipmentTrackCreationInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $this->adapterMock = $this->getMockBuilder(AdapterInterface::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+
+        $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+        $this->model = $helper->getObject(
+            ShipOrder::class,
+            [
+                'resourceConnection' => $this->resourceConnectionMock,
+                'orderRepository' => $this->orderRepositoryMock,
+                'shipmentRepository' => $this->shipmentRepositoryMock,
+                'shipmentDocumentFactory' => $this->shipmentDocumentFactoryMock,
+                'shipmentValidator' => $this->shipmentValidatorMock,
+                'orderValidator' => $this->orderValidatorMock,
+                'orderStateResolver' => $this->orderStateResolverMock,
+                'orderRegistrar' => $this->orderRegistrarMock,
+                'notifierInterface' => $this->notifierInterfaceMock,
+                'config' => $this->configMock,
+                'logger' => $this->loggerMock
+            ]
+        );
+    }
+
+    /**
+     * @dataProvider dataProvider
+     */
+    public function testExecute($orderId, $items, $notify, $appendComment)
+    {
+        $this->resourceConnectionMock->expects($this->once())
+            ->method('getConnection')
+            ->with('sales')
+            ->willReturn($this->adapterMock);
+
+        $this->orderRepositoryMock->expects($this->once())
+            ->method('get')
+            ->willReturn($this->orderMock);
+
+        $this->shipmentDocumentFactoryMock->expects($this->once())
+            ->method('create')
+            ->with(
+                $this->orderMock,
+                $items,
+                [$this->trackMock],
+                $this->shipmentCommentCreationMock,
+                ($appendComment && $notify),
+                [$this->packageMock],
+                $this->shipmentCreationArgumentsMock
+            )->willReturn($this->shipmentMock);
+
+        $this->shipmentValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->shipmentMock)
+            ->willReturn([]);
+        $this->orderValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->orderMock)
+            ->willReturn([]);
+
+        $this->orderRegistrarMock->expects($this->once())
+            ->method('register')
+            ->with($this->orderMock, $this->shipmentMock)
+            ->willReturn($this->orderMock);
+
+        $this->orderStateResolverMock->expects($this->once())
+            ->method('getStateForOrder')
+            ->with($this->orderMock, [OrderStateResolverInterface::IN_PROGRESS])
+            ->willReturn(Order::STATE_PROCESSING);
+
+        $this->orderMock->expects($this->once())
+            ->method('setState')
+            ->with(Order::STATE_PROCESSING)
+            ->willReturnSelf();
+
+        $this->orderMock->expects($this->once())
+            ->method('getState')
+            ->willReturn(Order::STATE_PROCESSING);
+
+        $this->configMock->expects($this->once())
+            ->method('getStateDefaultStatus')
+            ->with(Order::STATE_PROCESSING)
+            ->willReturn('Processing');
+
+        $this->orderMock->expects($this->once())
+            ->method('setStatus')
+            ->with('Processing')
+            ->willReturnSelf();
+
+        $this->shipmentRepositoryMock->expects($this->once())
+            ->method('save')
+            ->with($this->shipmentMock)
+            ->willReturn($this->shipmentMock);
+
+        $this->orderRepositoryMock->expects($this->once())
+            ->method('save')
+            ->with($this->orderMock)
+            ->willReturn($this->orderMock);
+
+        if ($notify) {
+            $this->notifierInterfaceMock->expects($this->once())
+                ->method('notify')
+                ->with($this->orderMock, $this->shipmentMock, $this->shipmentCommentCreationMock);
+        }
+
+        $this->shipmentMock->expects($this->once())
+            ->method('getEntityId')
+            ->willReturn(2);
+
+        $this->assertEquals(
+            2,
+            $this->model->execute(
+                $orderId,
+                $items,
+                $notify,
+                $appendComment,
+                $this->shipmentCommentCreationMock,
+                [$this->trackMock],
+                [$this->packageMock],
+                $this->shipmentCreationArgumentsMock
+            )
+        );
+    }
+
+    /**
+     * @expectedException \Magento\Sales\Api\Exception\DocumentValidationExceptionInterface
+     */
+    public function testDocumentValidationException()
+    {
+        $orderId = 1;
+        $items = [1 => 2];
+        $notify = true;
+        $appendComment = true;
+        $errorMessages = ['error1', 'error2'];
+
+        $this->orderRepositoryMock->expects($this->once())
+            ->method('get')
+            ->willReturn($this->orderMock);
+
+        $this->shipmentDocumentFactoryMock->expects($this->once())
+            ->method('create')
+            ->with(
+                $this->orderMock,
+                $items,
+                [$this->trackMock],
+                $this->shipmentCommentCreationMock,
+                ($appendComment && $notify),
+                [$this->packageMock],
+                $this->shipmentCreationArgumentsMock
+            )->willReturn($this->shipmentMock);
+
+        $this->shipmentValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->shipmentMock)
+            ->willReturn($errorMessages);
+        $this->orderValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->orderMock)
+            ->willReturn([]);
+
+        $this->model->execute(
+            $orderId,
+            $items,
+            $notify,
+            $appendComment,
+            $this->shipmentCommentCreationMock,
+            [$this->trackMock],
+            [$this->packageMock],
+            $this->shipmentCreationArgumentsMock
+        );
+    }
+
+    /**
+     * @expectedException \Magento\Sales\Api\Exception\CouldNotShipExceptionInterface
+     */
+    public function testCouldNotInvoiceException()
+    {
+        $orderId = 1;
+        $this->resourceConnectionMock->expects($this->once())
+            ->method('getConnection')
+            ->with('sales')
+            ->willReturn($this->adapterMock);
+
+        $this->orderRepositoryMock->expects($this->once())
+            ->method('get')
+            ->willReturn($this->orderMock);
+
+        $this->shipmentDocumentFactoryMock->expects($this->once())
+            ->method('create')
+            ->with(
+                $this->orderMock
+            )->willReturn($this->shipmentMock);
+
+        $this->shipmentValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->shipmentMock)
+            ->willReturn([]);
+        $this->orderValidatorMock->expects($this->once())
+            ->method('validate')
+            ->with($this->orderMock)
+            ->willReturn([]);
+        $e = new \Exception();
+
+        $this->orderRegistrarMock->expects($this->once())
+            ->method('register')
+            ->with($this->orderMock, $this->shipmentMock)
+            ->willThrowException($e);
+
+        $this->loggerMock->expects($this->once())
+            ->method('critical')
+            ->with($e);
+
+        $this->adapterMock->expects($this->once())
+            ->method('rollBack');
+
+        $this->model->execute(
+            $orderId
+        );
+    }
+
+    /**
+     * @return array
+     */
+    public function dataProvider()
+    {
+        return [
+            'TestWithNotifyTrue' => [1, [1 => 2], true, true],
+            'TestWithNotifyFalse' => [1, [1 => 2], false, true],
+        ];
+    }
+}
diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml
index 383650c8688fc847ac030bc6c216293a925956bf..dfdb0f6a261c51490e99955bdd61fd1bae328e60 100644
--- a/app/code/Magento/Sales/etc/di.xml
+++ b/app/code/Magento/Sales/etc/di.xml
@@ -30,7 +30,9 @@
     <preference for="Magento\Sales\Api\Data\OrderStatusHistorySearchResultInterface" type="Magento\Sales\Model\ResourceModel\Order\Status\History\Collection"/>
     <preference for="Magento\Sales\Api\Data\ShipmentCommentInterface" type="Magento\Sales\Model\Order\Shipment\Comment"/>
     <preference for="Magento\Sales\Api\Data\ShipmentCommentSearchResultInterface" type="Magento\Sales\Model\ResourceModel\Order\Shipment\Comment\Collection"/>
+    <preference for="Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface" type="Magento\Sales\Model\Order\Shipment\CreationArguments"/>
     <preference for="Magento\Sales\Api\Data\ShipmentInterface" type="Magento\Sales\Model\Order\Shipment"/>
+    <preference for="Magento\Sales\Api\Data\ShipmentItemCreationInterface" type="Magento\Sales\Model\Order\Shipment\ItemCreation"/>
     <preference for="Magento\Sales\Api\Data\ShipmentItemInterface" type="Magento\Sales\Model\Order\Shipment\Item"/>
     <preference for="Magento\Sales\Api\Data\ShipmentItemSearchResultInterface" type="Magento\Sales\Model\ResourceModel\Order\Shipment\Item\Collection"/>
     <preference for="Magento\Sales\Api\Data\ShipmentSearchResultInterface" type="Magento\Sales\Model\ResourceModel\Order\Shipment\Collection"/>
@@ -49,7 +51,10 @@
     <preference for="Magento\Sales\Api\InvoiceItemRepositoryInterface" type="Magento\Sales\Api\Data\InvoiceItem\Repository"/>
     <preference for="Magento\Sales\Api\InvoiceRepositoryInterface" type="Magento\Sales\Model\Order\InvoiceRepository"/>
     <preference for="Magento\Sales\Api\InvoiceManagementInterface" type="Magento\Sales\Model\Service\InvoiceService"/>
-    <preference for="Magento\Sales\Api\InvoiceCreationArgumentsInterface" type="Magento\Sales\Model\Order\Invoice\CreationArguments"/>
+    <preference for="Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface" type="Magento\Sales\Model\Order\Invoice\CreationArguments"/>
+    <preference for="Magento\Sales\Api\Data\InvoiceItemCreationInterface" type="Magento\Sales\Model\Order\Invoice\ItemCreation"/>
+    <preference for="Magento\Sales\Api\Data\InvoiceCommentCreationInterface" type="Magento\Sales\Model\Order\Invoice\CommentCreation"/>
+    <preference for="Magento\Sales\Api\Data\ShipmentCommentCreationInterface" type="Magento\Sales\Model\Order\Shipment\CommentCreation"/>
     <preference for="Magento\Sales\Api\OrderAddressRepositoryInterface" type="Magento\Sales\Model\Order\AddressRepository"/>
     <preference for="Magento\Sales\Api\OrderCustomerManagementInterface" type="Magento\Sales\Model\Order\CustomerManagement"/>
     <preference for="Magento\Sales\Api\OrderItemRepositoryInterface" type="Magento\Sales\Model\Order\ItemRepository"/>
@@ -62,13 +67,14 @@
     <preference for="Magento\Sales\Api\ShipmentRepositoryInterface" type="Magento\Sales\Model\Order\ShipmentRepository"/>
     <preference for="Magento\Sales\Api\ShipmentManagementInterface" type="Magento\Sales\Model\Service\ShipmentService"/>
     <preference for="Magento\Sales\Api\ShipmentTrackRepositoryInterface" type="Magento\Sales\Api\Data\ShipmentTrack\Repository"/>
+    <preference for="Magento\Sales\Api\ShipOrderInterface" type="Magento\Sales\Model\ShipOrder"/>
     <preference for="Magento\Sales\Api\TransactionRepositoryInterface" type="Magento\Sales\Model\Order\Payment\Transaction\Repository"/>
     <preference for="Magento\Sales\Model\Order\Invoice\NotifierInterface" type="Magento\Sales\Model\Order\Invoice\Notifier"/>
-    <preference for="Magento\Sales\Model\Order\InvoiceValidatorInterface" type="Magento\Sales\Model\Order\InvoiceValidator"/>
+    <preference for="Magento\Sales\Model\Order\Shipment\NotifierInterface" type="Magento\Sales\Model\Order\Shipment\Notifier"/>
     <preference for="Magento\Sales\Model\Order\PaymentAdapterInterface" type="Magento\Sales\Model\Order\PaymentAdapter"/>
-    <preference for="Magento\Sales\Model\Order\OrderValidatorInterface" type="Magento\Sales\Model\Order\OrderValidator"/>
     <preference for="Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface" type="Magento\Sales\Model\Order\Payment\Transaction\Manager"/>
     <preference for="Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface" type="Magento\Sales\Model\Order\Payment\Transaction\Builder"/>
+    <preference for="Magento\Sales\Model\Order\Shipment\OrderRegistrarInterface" type="Magento\Sales\Model\Order\Shipment\OrderRegistrar"/>
     <preference for="Magento\Sales\Model\Spi\CreditmemoCommentResourceInterface" type="Magento\Sales\Model\ResourceModel\Order\Creditmemo\Comment"/>
     <preference for="Magento\Sales\Model\Spi\CreditmemoItemResourceInterface" type="Magento\Sales\Model\ResourceModel\Order\Creditmemo\Item"/>
     <preference for="Magento\Sales\Model\Spi\CreditmemoResourceInterface" type="Magento\Sales\Model\ResourceModel\Order\Creditmemo"/>
@@ -86,10 +92,14 @@
     <preference for="Magento\Sales\Model\Spi\ShipmentTrackResourceInterface" type="Magento\Sales\Model\ResourceModel\Order\Shipment\Track"/>
     <preference for="Magento\Sales\Model\Spi\TransactionResourceInterface" type="Magento\Sales\Model\ResourceModel\Order\Payment\Transaction"/>
     <preference for="Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface" type="Magento\Sales\Model\ResourceModel\Order\CollectionFactory"/>
-    <preference for="Magento\Sales\Api\Data\InvoiceCommentCreationInterface" type="Magento\Sales\Model\Order\Invoice\Comment"/>
-    <preference for="Magento\Sales\Api\Data\InvoiceItemCreationInterface" type="Magento\Sales\Model\Order\Invoice\ItemCreation"/>
-    <preference for="Magento\Sales\Api\OrderInvoiceInterface" type="Magento\Sales\Model\OrderInvoice"/>
+    <preference for="Magento\Sales\Api\InvoiceOrderInterface" type="Magento\Sales\Model\InvoiceOrder"/>
     <preference for="Magento\Sales\Model\Order\OrderStateResolverInterface" type="Magento\Sales\Model\Order\StateResolver"/>
+    <preference for="Magento\Sales\Api\Data\ShipmentTrackCreationInterface" type="Magento\Sales\Model\Order\Shipment\TrackCreation"/>
+    <preference for="Magento\Sales\Api\Data\ShipmentPackageInterface" type="Magento\Sales\Model\Order\Shipment\Package"/>
+    <preference for="Magento\Sales\Api\Data\ShipmentPackageCreationInterface" type="Magento\Sales\Model\Order\Shipment\PackageCreation"/>
+    <preference for="Magento\Sales\Model\Order\OrderValidatorInterface" type="Magento\Sales\Model\Order\OrderValidator"/>
+    <preference for="Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface" type="Magento\Sales\Model\Order\Invoice\InvoiceValidator"/>
+    <preference for="Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface" type="Magento\Sales\Model\Order\Shipment\ShipmentValidator"/>
     <type name="Magento\Sales\Model\ResourceModel\Report" shared="false"/>
     <type name="Magento\Sales\Model\Order\Pdf\Config\Reader">
         <arguments>
@@ -909,4 +919,18 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Sales\Model\Order\Shipment\Notifier">
+        <arguments>
+            <argument name="senders" xsi:type="array">
+                <item name="email" xsi:type="object">Magento\Sales\Model\Order\Shipment\Sender\EmailSender</item>
+            </argument>
+        </arguments>
+    </type>
+    <type name="Magento\Framework\EntityManager\HydratorPool">
+        <arguments>
+            <argument name="hydrators" xsi:type="array">
+                <item name="Magento\Sales\Api\Data\ShipmentTrackCreationInterface" xsi:type="string">Magento\Framework\EntityManager\HydratorInterface</item>
+            </argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Sales/etc/webapi.xml b/app/code/Magento/Sales/etc/webapi.xml
index 8d1b1fda5bc314fdbd3dec616f7af8d56f1d8191..4c7fe03a201f8b25c2af83219fd0576da4c2cd3e 100644
--- a/app/code/Magento/Sales/etc/webapi.xml
+++ b/app/code/Magento/Sales/etc/webapi.xml
@@ -235,6 +235,12 @@
             <resource ref="Magento_Sales::sales" />
         </resources>
     </route>
+    <route url="/V1/order/:orderId/ship" method="POST">
+        <service class="Magento\Sales\Api\ShipOrderInterface" method="execute"/>
+        <resources>
+            <resource ref="Magento_Sales::sales" />
+        </resources>
+    </route>
     <route url="/V1/orders/" method="POST">
         <service class="Magento\Sales\Api\OrderRepositoryInterface" method="save"/>
         <resources>
@@ -254,7 +260,7 @@
         </resources>
     </route>
     <route url="/V1/order/:orderId/invoice" method="POST">
-        <service class="Magento\Sales\Api\OrderInvoiceInterface" method="execute"/>
+        <service class="Magento\Sales\Api\InvoiceOrderInterface" method="execute"/>
         <resources>
             <resource ref="Magento_Sales::sales" />
         </resources>
diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
index adbd96624649d5c7aff56a95156579ed16cc7f9d..d265159bc630be220bc4a7a64b706e36786510ef 100644
--- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
+++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
@@ -7,8 +7,12 @@
 namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment;
 
 use Magento\Backend\App\Action;
-use Magento\Sales\Model\Order\Email\Sender\ShipmentSender;
+use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator;
 
+/**
+ * Class Save
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class Save extends \Magento\Backend\App\Action
 {
     /**
@@ -29,21 +33,26 @@ class Save extends \Magento\Backend\App\Action
     protected $labelGenerator;
 
     /**
-     * @var ShipmentSender
+     * @var \Magento\Sales\Model\Order\Email\Sender\ShipmentSender
      */
     protected $shipmentSender;
 
     /**
-     * @param Action\Context $context
+     * @var \Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface
+     */
+    private $shipmentValidator;
+
+    /**
+     * @param \Magento\Backend\App\Action\Context $context
      * @param \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader $shipmentLoader
      * @param \Magento\Shipping\Model\Shipping\LabelGenerator $labelGenerator
-     * @param ShipmentSender $shipmentSender
+     * @param \Magento\Sales\Model\Order\Email\Sender\ShipmentSender $shipmentSender
      */
     public function __construct(
-        Action\Context $context,
+        \Magento\Backend\App\Action\Context $context,
         \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader $shipmentLoader,
         \Magento\Shipping\Model\Shipping\LabelGenerator $labelGenerator,
-        ShipmentSender $shipmentSender
+        \Magento\Sales\Model\Order\Email\Sender\ShipmentSender $shipmentSender
     ) {
         $this->shipmentLoader = $shipmentLoader;
         $this->labelGenerator = $labelGenerator;
@@ -119,7 +128,14 @@ class Save extends \Magento\Backend\App\Action
                 $shipment->setCustomerNote($data['comment_text']);
                 $shipment->setCustomerNoteNotify(isset($data['comment_customer_notify']));
             }
-
+            $errorMessages = $this->getShipmentValidator()->validate($shipment, [QuantityValidator::class]);
+            if (!empty($errorMessages)) {
+                $this->messageManager->addError(
+                    __("Shipment Document Validation Error(s):\n" . implode("\n", $errorMessages))
+                );
+                $this->_redirect('*/*/new', ['order_id' => $this->getRequest()->getParam('order_id')]);
+                return;
+            }
             $shipment->register();
 
             $shipment->getOrder()->setCustomerNoteNotify(!empty($data['send_email']));
@@ -168,4 +184,19 @@ class Save extends \Magento\Backend\App\Action
             $this->_redirect('sales/order/view', ['order_id' => $shipment->getOrderId()]);
         }
     }
+
+    /**
+     * @return \Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface
+     * @deprecated
+     */
+    private function getShipmentValidator()
+    {
+        if ($this->shipmentValidator === null) {
+            $this->shipmentValidator = $this->_objectManager->get(
+                \Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface::class
+            );
+        }
+
+        return $this->shipmentValidator;
+    }
 }
diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/ShipmentLoader.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/ShipmentLoader.php
index b452c88887c9e59bea3711578dff9e6db886ba84..c4efe6f6507d5641d7ef7d9ed378c73a6038c1de 100644
--- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/ShipmentLoader.php
+++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/ShipmentLoader.php
@@ -1,6 +1,5 @@
 <?php
 /**
- *
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
diff --git a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php
index af89f1a9146be07f49a31f7c5b0ad90c5d3c96b0..f8d9c06dc8ec05c07c699c4b22e7b830c796e4dc 100644
--- a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php
+++ b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php
@@ -11,6 +11,9 @@ namespace Magento\Shipping\Test\Unit\Controller\Adminhtml\Order\Shipment;
 use Magento\Backend\App\Action;
 use Magento\Sales\Model\Order\Email\Sender\ShipmentSender;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface;
+use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator;
+
 /**
  * Class SaveTest
  *
@@ -88,6 +91,11 @@ class SaveTest extends \PHPUnit_Framework_TestCase
      */
     protected $saveAction;
 
+    /**
+     * @var ShipmentValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $shipmentValidatorMock;
+
     /**
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
@@ -210,6 +218,10 @@ class SaveTest extends \PHPUnit_Framework_TestCase
             ->method('getFormKeyValidator')
             ->will($this->returnValue($this->formKeyValidator));
 
+        $this->shipmentValidatorMock = $this->getMockBuilder(ShipmentValidatorInterface::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
         $this->saveAction = $objectManagerHelper->getObject(
             \Magento\Shipping\Controller\Adminhtml\Order\Shipment\Save::class,
             [
@@ -218,7 +230,8 @@ class SaveTest extends \PHPUnit_Framework_TestCase
                 'context' => $this->context,
                 'shipmentLoader' => $this->shipmentLoader,
                 'request' => $this->request,
-                'response' => $this->response
+                'response' => $this->response,
+                'shipmentValidator' => $this->shipmentValidatorMock
             ]
         );
     }
@@ -346,6 +359,11 @@ class SaveTest extends \PHPUnit_Framework_TestCase
                 ->will($this->returnValue($orderId));
             $this->prepareRedirect($path, $arguments);
 
+            $this->shipmentValidatorMock->expects($this->once())
+                ->method('validate')
+                ->with($shipment, [QuantityValidator::class])
+                ->willReturn([]);
+
             $this->saveAction->execute();
             $this->assertEquals($this->response, $this->saveAction->getResponse());
         }
diff --git a/app/code/Magento/Store/App/Action/Plugin/Context.php b/app/code/Magento/Store/App/Action/Plugin/Context.php
index e56b301733cca65b6cb7cfdfce4a8b870f75ee47..1b4545384255c3154212ce84cc81c1a83fa6dcbb 100644
--- a/app/code/Magento/Store/App/Action/Plugin/Context.php
+++ b/app/code/Magento/Store/App/Action/Plugin/Context.php
@@ -11,6 +11,8 @@ use Magento\Framework\Phrase;
 use Magento\Store\Api\StoreCookieManagerInterface;
 use Magento\Store\Api\StoreResolverInterface;
 use Magento\Store\Model\StoreManagerInterface;
+use Magento\Framework\App\Action\AbstractAction;
+use Magento\Framework\App\RequestInterface;
 
 /**
  * Class ContextPlugin
@@ -27,11 +29,6 @@ class Context
      */
     protected $httpContext;
 
-    /**
-     * @var \Magento\Framework\App\Request\Http
-     */
-    protected $httpRequest;
-
     /**
      * @var \Magento\Store\Model\StoreManagerInterface
      */
@@ -45,40 +42,35 @@ class Context
     /**
      * @param \Magento\Framework\Session\SessionManagerInterface $session
      * @param \Magento\Framework\App\Http\Context $httpContext
-     * @param \Magento\Framework\App\Request\Http $httpRequest
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param StoreCookieManagerInterface $storeCookieManager
      */
     public function __construct(
         \Magento\Framework\Session\SessionManagerInterface $session,
         \Magento\Framework\App\Http\Context $httpContext,
-        \Magento\Framework\App\Request\Http $httpRequest,
         \Magento\Store\Model\StoreManagerInterface $storeManager,
         StoreCookieManagerInterface $storeCookieManager
     ) {
         $this->session      = $session;
         $this->httpContext  = $httpContext;
-        $this->httpRequest  = $httpRequest;
         $this->storeManager = $storeManager;
         $this->storeCookieManager = $storeCookieManager;
     }
 
     /**
-     * @param \Magento\Framework\App\ActionInterface $subject
-     * @param callable $proceed
-     * @param \Magento\Framework\App\RequestInterface $request
-     * @return mixed
+     * Set store and currency to http context
+     *
+     * @param AbstractAction $subject
+     * @param RequestInterface $request
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDispatch(
-        \Magento\Framework\App\ActionInterface $subject,
-        \Closure $proceed,
-        \Magento\Framework\App\RequestInterface $request
-    ) {
+    public function beforeDispatch(AbstractAction $subject, RequestInterface $request)
+    {
         /** @var \Magento\Store\Model\Store $defaultStore */
         $defaultStore = $this->storeManager->getWebsite()->getDefaultStore();
 
-        $storeCode = $this->httpRequest->getParam(
+        $storeCode = $request->getParam(
             StoreResolverInterface::PARAM_NAME,
             $this->storeCookieManager->getStoreCodeFromCookie()
         );
@@ -103,6 +95,5 @@ class Context
             $this->session->getCurrencyCode() ?: $currentStore->getDefaultCurrencyCode(),
             $defaultStore->getDefaultCurrencyCode()
         );
-        return $proceed($request);
     }
 }
diff --git a/app/code/Magento/Store/App/Action/Plugin/StoreCheck.php b/app/code/Magento/Store/App/Action/Plugin/StoreCheck.php
index 56a7157e7004c2b9ec87460b45dcf68d082557b5..fcb9316220793669b93630eb36b879de7be9a426 100644
--- a/app/code/Magento/Store/App/Action/Plugin/StoreCheck.php
+++ b/app/code/Magento/Store/App/Action/Plugin/StoreCheck.php
@@ -23,17 +23,14 @@ class StoreCheck
     }
 
     /**
-     * @param \Magento\Framework\App\ActionInterface $subject
-     * @param callable $proceed
+     * @param \Magento\Framework\App\Action\AbstractAction $subject
      * @param \Magento\Framework\App\RequestInterface $request
-     *
-     * @return \Magento\Framework\App\ResponseInterface
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      * @throws \Magento\Framework\Exception\State\InitException
      */
-    public function aroundDispatch(
-        \Magento\Framework\App\ActionInterface $subject,
-        \Closure $proceed,
+    public function beforeDispatch(
+        \Magento\Framework\App\Action\AbstractAction $subject,
         \Magento\Framework\App\RequestInterface $request
     ) {
         if (!$this->_storeManager->getStore()->isActive()) {
@@ -41,6 +38,5 @@ class StoreCheck
                 __('Current store is not active.')
             );
         }
-        return $proceed($request);
     }
 }
diff --git a/app/code/Magento/Store/Test/Unit/App/Action/Plugin/ContextTest.php b/app/code/Magento/Store/Test/Unit/App/Action/Plugin/ContextTest.php
index 32aeb0b515124f1d552624d2a7a09b2ad520a85b..2411475b203818c28af71de117172236a5b321bf 100644
--- a/app/code/Magento/Store/Test/Unit/App/Action/Plugin/ContextTest.php
+++ b/app/code/Magento/Store/Test/Unit/App/Action/Plugin/ContextTest.php
@@ -3,14 +3,13 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
-// @codingStandardsIgnoreFile
-
 namespace Magento\Store\Test\Unit\App\Action\Plugin;
 
 use Magento\Framework\App\Http\Context;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 use Magento\Store\Model\StoreManagerInterface;
+use Magento\Framework\App\Action\AbstractAction;
+use Magento\Framework\App\RequestInterface;
 
 /**
  * Class ContextPluginTest
@@ -38,11 +37,6 @@ class ContextTest extends \PHPUnit_Framework_TestCase
      */
     protected $httpContextMock;
 
-    /**
-     * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $httpRequestMock;
-
     /**
      * @var \Magento\Store\Model\StoreManager|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -69,17 +63,12 @@ class ContextTest extends \PHPUnit_Framework_TestCase
     protected $websiteMock;
 
     /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var AbstractAction|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $subjectMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $requestMock;
 
@@ -88,67 +77,53 @@ class ContextTest extends \PHPUnit_Framework_TestCase
      */
     protected function setUp()
     {
-        $this->sessionMock = $this->getMock(
+        $this->sessionMock = $this->getMock(
             \Magento\Framework\Session\Generic::class,
             ['getCurrencyCode'],
             [],
             '',
             false
         );
-        $this->httpContextMock = $this->getMock(
+        $this->httpContextMock = $this->getMock(
             \Magento\Framework\App\Http\Context::class,
             [],
             [],
             '',
             false
         );
-        $this->httpRequestMock = $this->getMock(
-            \Magento\Framework\App\Request\Http::class,
-            ['getParam'],
-            [],
-            '',
-            false
-        );
         $this->storeManager = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class);
         $this->storeCookieManager = $this->getMock(\Magento\Store\Api\StoreCookieManagerInterface::class);
-        $this->storeMock = $this->getMock(
+        $this->storeMock = $this->getMock(
             \Magento\Store\Model\Store::class,
             [],
             [],
             '',
             false
         );
-        $this->currentStoreMock = $this->getMock(
+        $this->currentStoreMock = $this->getMock(
             \Magento\Store\Model\Store::class,
             [],
             [],
             '',
             false
         );
-        $this->websiteMock = $this->getMock(
+        $this->websiteMock = $this->getMock(
             \Magento\Store\Model\Website::class,
             ['getDefaultStore', '__wakeup'],
             [],
             '',
             false
         );
-        $this->closureMock = function () {
-            return 'ExpectedValue';
-        };
-        $this->subjectMock = $this->getMock(
-            \Magento\Framework\App\Action\Action::class,
-            [],
-            [],
-            '',
-            false
-        );
-        $this->requestMock = $this->getMock(\Magento\Framework\App\RequestInterface::class);
+        $this->requestMock = $this->getMockBuilder(RequestInterface::class)->getMockForAbstractClass();
+        $this->subjectMock = $this->getMockBuilder(AbstractAction::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
 
-        $this->plugin = (new ObjectManager($this))->getObject(\Magento\Store\App\Action\Plugin\Context::class,
+        $this->plugin = (new ObjectManager($this))->getObject(
+            \Magento\Store\App\Action\Plugin\Context::class,
             [
                 'session' => $this->sessionMock,
                 'httpContext' => $this->httpContextMock,
-                'httpRequest' => $this->httpRequestMock,
                 'storeManager' => $this->storeManager,
                 'storeCookieManager' => $this->storeCookieManager,
             ]
@@ -171,7 +146,7 @@ class ContextTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue(self::CURRENCY_CURRENT_STORE));
     }
 
-    public function testAroundDispatchCurrencyFromSession()
+    public function testBeforeDispatchCurrencyFromSession()
     {
         $this->storeMock->expects($this->once())
             ->method('getDefaultCurrencyCode')
@@ -184,7 +159,7 @@ class ContextTest extends \PHPUnit_Framework_TestCase
             ->method('getCode')
             ->willReturn('custom_store');
 
-        $this->httpRequestMock->expects($this->once())
+        $this->requestMock->expects($this->once())
             ->method('getParam')
             ->with($this->equalTo('___store'))
             ->will($this->returnValue('default'));
@@ -205,10 +180,7 @@ class ContextTest extends \PHPUnit_Framework_TestCase
             ->method('setValue')
             ->with(Context::CONTEXT_CURRENCY, self::CURRENCY_SESSION, self::CURRENCY_DEFAULT);
 
-        $this->assertEquals(
-            'ExpectedValue',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
     public function testDispatchCurrentStoreCurrency()
@@ -224,7 +196,7 @@ class ContextTest extends \PHPUnit_Framework_TestCase
             ->method('getCode')
             ->willReturn('custom_store');
 
-        $this->httpRequestMock->expects($this->once())
+        $this->requestMock->expects($this->once())
             ->method('getParam')
             ->with($this->equalTo('___store'))
             ->will($this->returnValue('default'));
@@ -241,10 +213,7 @@ class ContextTest extends \PHPUnit_Framework_TestCase
             ->method('setValue')
             ->with(Context::CONTEXT_CURRENCY, self::CURRENCY_CURRENT_STORE, self::CURRENCY_DEFAULT);
 
-        $this->assertEquals(
-            'ExpectedValue',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
     public function testDispatchStoreParameterIsArray()
@@ -266,7 +235,7 @@ class ContextTest extends \PHPUnit_Framework_TestCase
             ]
         ];
 
-        $this->httpRequestMock->expects($this->once())
+        $this->requestMock->expects($this->once())
             ->method('getParam')
             ->with($this->equalTo('___store'))
             ->will($this->returnValue($store));
@@ -284,11 +253,7 @@ class ContextTest extends \PHPUnit_Framework_TestCase
             ->method('setValue')
             ->with(Context::CONTEXT_CURRENCY, self::CURRENCY_CURRENT_STORE, self::CURRENCY_DEFAULT);
 
-        $result = $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock);
-        $this->assertEquals(
-            'ExpectedValue',
-            $result
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
     /**
@@ -314,10 +279,10 @@ class ContextTest extends \PHPUnit_Framework_TestCase
             ]
         ];
 
-        $this->httpRequestMock->expects($this->once())
+        $this->requestMock->expects($this->once())
             ->method('getParam')
             ->with($this->equalTo('___store'))
             ->will($this->returnValue($store));
-        $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock);
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 }
diff --git a/app/code/Magento/Store/Test/Unit/App/Action/Plugin/StoreCheckTest.php b/app/code/Magento/Store/Test/Unit/App/Action/Plugin/StoreCheckTest.php
index 1170377a6b27c8abf96648c5491ef68d6b35fa5e..7e443a05c1b25b2edfcce4499036d65010c215bf 100644
--- a/app/code/Magento/Store/Test/Unit/App/Action/Plugin/StoreCheckTest.php
+++ b/app/code/Magento/Store/Test/Unit/App/Action/Plugin/StoreCheckTest.php
@@ -3,9 +3,6 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
-// @codingStandardsIgnoreFile
-
 namespace Magento\Store\Test\Unit\App\Action\Plugin;
 
 class StoreCheckTest extends \PHPUnit_Framework_TestCase
@@ -26,17 +23,12 @@ class StoreCheckTest extends \PHPUnit_Framework_TestCase
     protected $_storeMock;
 
     /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\App\Action\AbstractAction|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $subjectMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $requestMock;
 
@@ -51,11 +43,10 @@ class StoreCheckTest extends \PHPUnit_Framework_TestCase
         )->will(
             $this->returnValue($this->_storeMock)
         );
-        $this->subjectMock = $this->getMock(\Magento\Framework\App\Action\Action::class, [], [], '', false);
-        $this->closureMock = function () {
-            return 'Expected';
-        };
         $this->requestMock = $this->getMock(\Magento\Framework\App\RequestInterface::class);
+        $this->subjectMock = $this->getMockBuilder(\Magento\Framework\App\Action\AbstractAction::class)
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
 
         $this->_plugin = new \Magento\Store\App\Action\Plugin\StoreCheck($this->_storeManagerMock);
     }
@@ -64,22 +55,15 @@ class StoreCheckTest extends \PHPUnit_Framework_TestCase
      * @expectedException \Magento\Framework\Exception\State\InitException
      * @expectedExceptionMessage Current store is not active.
      */
-    public function testAroundDispatchWhenStoreNotActive()
+    public function testBeforeDispatchWhenStoreNotActive()
     {
         $this->_storeMock->expects($this->any())->method('isActive')->will($this->returnValue(false));
-        $this->assertEquals(
-            'Expected',
-            $this->_plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->_plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
-    public function testAroundDispatchWhenStoreIsActive()
+    public function testBeforeDispatchWhenStoreIsActive()
     {
         $this->_storeMock->expects($this->any())->method('isActive')->will($this->returnValue(true));
-        $this->assertEquals(
-            'Expected',
-            $this->_plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->_plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
-
 }
diff --git a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php
index b2c5a972734a9663cbcfa3ae13f7cd9f42812ee7..959f9efd0334546196a12df2bf1b546f42c7d6d4 100644
--- a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php
+++ b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php
@@ -39,10 +39,7 @@ class RouteParamsResolverTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    /**
-     * @SuppressWarnings(PHPMD.UnusedLocalVariable)
-     */
-    public function testAroundSetRouteParamsScopeInParams()
+    public function testBeforeSetRouteParamsScopeInParams()
     {
         $storeCode = 'custom_store';
         $this->scopeConfigMock
@@ -66,20 +63,13 @@ class RouteParamsResolverTest extends \PHPUnit_Framework_TestCase
 
         $this->queryParamsResolverMock->expects($this->once())->method('setQueryParam')->with('___store', $storeCode);
 
-        $this->model->aroundSetRouteParams(
+        $this->model->beforeSetRouteParams(
             $routeParamsResolverMock,
-            function ($data, $unsetOldParams) {
-                $this->assertArrayNotHasKey('_scope_to_url', $data, 'This data item should have been unset.');
-                $this->assertArrayNotHasKey('_scope', $data, 'This data item should have been unset.');
-            },
             $data
         );
     }
 
-    /**
-     * @SuppressWarnings(PHPMD.UnusedLocalVariable)
-     */
-    public function testAroundSetRouteParamsScopeUseStoreInUrl()
+    public function testBeforeSetRouteParamsScopeUseStoreInUrl()
     {
         $storeCode = 'custom_store';
         $this->scopeConfigMock
@@ -103,20 +93,13 @@ class RouteParamsResolverTest extends \PHPUnit_Framework_TestCase
 
         $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam');
 
-        $this->model->aroundSetRouteParams(
+        $this->model->beforeSetRouteParams(
             $routeParamsResolverMock,
-            function ($data, $unsetOldParams) {
-                $this->assertArrayNotHasKey('_scope_to_url', $data, 'This data item should have been unset.');
-                $this->assertArrayNotHasKey('_scope', $data, 'This data item should have been unset.');
-            },
             $data
         );
     }
 
-    /**
-     * @SuppressWarnings(PHPMD.UnusedLocalVariable)
-     */
-    public function testAroundSetRouteParamsSingleStore()
+    public function testBeforeSetRouteParamsSingleStore()
     {
         $storeCode = 'custom_store';
         $this->scopeConfigMock
@@ -140,20 +123,13 @@ class RouteParamsResolverTest extends \PHPUnit_Framework_TestCase
 
         $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam');
 
-        $this->model->aroundSetRouteParams(
+        $this->model->beforeSetRouteParams(
             $routeParamsResolverMock,
-            function ($data, $unsetOldParams) {
-                $this->assertArrayNotHasKey('_scope_to_url', $data, 'This data item should have been unset.');
-                $this->assertArrayNotHasKey('_scope', $data, 'This data item should have been unset.');
-            },
             $data
         );
     }
 
-    /**
-     * @SuppressWarnings(PHPMD.UnusedLocalVariable)
-     */
-    public function testAroundSetRouteParamsNoScopeInParams()
+    public function testBeforeSetRouteParamsNoScopeInParams()
     {
         $storeCode = 'custom_store';
         $this->scopeConfigMock
@@ -185,11 +161,8 @@ class RouteParamsResolverTest extends \PHPUnit_Framework_TestCase
 
         $this->queryParamsResolverMock->expects($this->once())->method('setQueryParam')->with('___store', $storeCode);
 
-        $this->model->aroundSetRouteParams(
+        $this->model->beforeSetRouteParams(
             $routeParamsResolverMock,
-            function ($data, $unsetOldParams) {
-                $this->assertArrayNotHasKey('_scope_to_url', $data, 'This data item should have been unset.');
-            },
             $data
         );
     }
diff --git a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php
index 4435dfe108cffbffcfd458cc4a8a53dbdd8eb821..8ef0420a23cbe706d8df93abf18ae0fb573f709f 100644
--- a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php
+++ b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php
@@ -49,15 +49,12 @@ class RouteParamsResolver
      * Process scope query parameters.
      *
      * @param \Magento\Framework\Url\RouteParamsResolver $subject
-     * @param callable $proceed
      * @param array $data
      * @param bool $unsetOldParams
-     * @return \Magento\Framework\Url\RouteParamsResolver
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     * @return array
      */
-    public function aroundSetRouteParams(
+    public function beforeSetRouteParams(
         \Magento\Framework\Url\RouteParamsResolver $subject,
-        \Closure $proceed,
         array $data,
         $unsetOldParams = true
     ) {
@@ -78,6 +75,6 @@ class RouteParamsResolver
         }
         unset($data['_scope_to_url']);
 
-        return $proceed($data, $unsetOldParams);
+        return [$data, $unsetOldParams];
     }
 }
diff --git a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php
index 00dd9ff4cda98962934294e1ba52dfbc588b0e50..03927aed968889324c3e44df9dcb5f5fd1de36ac 100644
--- a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php
+++ b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php
@@ -6,9 +6,6 @@
 
 namespace Magento\Tax\Model\App\Action;
 
-use Magento\Customer\Model\Context;
-use Magento\Customer\Model\GroupManagement;
-
 /**
  * Class ContextPlugin
  */
@@ -74,21 +71,19 @@ class ContextPlugin
 
     /**
      * @param \Magento\Framework\App\ActionInterface $subject
-     * @param callable $proceed
      * @param \Magento\Framework\App\RequestInterface $request
      * @return mixed
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDispatch(
+    public function beforeDispatch(
         \Magento\Framework\App\ActionInterface $subject,
-        \Closure $proceed,
         \Magento\Framework\App\RequestInterface $request
     ) {
         if (!$this->customerSession->isLoggedIn() ||
             !$this->moduleManager->isEnabled('Magento_PageCache') ||
             !$this->cacheConfig->isEnabled() ||
             !$this->taxHelper->isCatalogPriceDisplayAffectedByTax()) {
-            return $proceed($request);
+            return;
         }
 
         $defaultBillingAddress = $this->customerSession->getDefaultTaxBillingAddress();
@@ -107,6 +102,5 @@ class ContextPlugin
                 0
             );
         }
-        return $proceed($request);
     }
 }
diff --git a/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php b/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php
index 7964353a74c96eb40ac652eb72c19730c6c135f8..a727beef10b0648934dbb21b89945ff2d1462193 100644
--- a/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php
+++ b/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php
@@ -71,18 +71,18 @@ class GrandTotalDetailsPlugin
 
     /**
      * @param \Magento\Quote\Model\Cart\TotalsConverter $subject
-     * @param \Closure $proceed
+     * @param \Magento\Quote\Api\Data\TotalSegmentInterface[] $totalSegments
      * @param \Magento\Quote\Model\Quote\Address\Total[] $addressTotals
+     *
      * @return \Magento\Quote\Api\Data\TotalSegmentInterface[]
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
-    public function aroundProcess(
+    public function afterProcess(
         \Magento\Quote\Model\Cart\TotalsConverter $subject,
-        \Closure $proceed,
+        array $totalSegments,
         array $addressTotals = []
     ) {
-        $totalSegments = $proceed($addressTotals);
 
         if (!array_key_exists($this->code, $addressTotals)) {
             return $totalSegments;
diff --git a/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php b/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php
index 6bf64c3300937045c7bde0a6f455f2d4667f4b10..a95ead9efe30405a51558dc8f7ade3ef23306ae4 100644
--- a/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php
+++ b/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php
@@ -111,9 +111,9 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
      * @param bool $cache
      * @param bool $taxEnabled
      * @param bool $loggedIn
-     * @dataProvider dataProviderAroundDispatch
+     * @dataProvider beforeDispatchDataProvider
      */
-    public function testAroundDispatch($cache, $taxEnabled, $loggedIn)
+    public function testBeforeDispatch($cache, $taxEnabled, $loggedIn)
     {
         $this->customerSessionMock->expects($this->any())
             ->method('isLoggedIn')
@@ -160,18 +160,14 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
 
             $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class);
             $request = $this->getMock(\Magento\Framework\App\Request\Http::class, ['getActionName'], [], '', false);
-            $expectedResult = 'expectedResult';
-            $proceed = function ($request) use ($expectedResult) {
-                return $expectedResult;
-            };
-            $this->contextPlugin->aroundDispatch($action, $proceed, $request);
+            $this->contextPlugin->beforeDispatch($action, $request);
         }
     }
 
     /**
      * @return array
      */
-    public function dataProviderAroundDispatch()
+    public function beforeDispatchDataProvider()
     {
         return [
             [false, false, false],
diff --git a/app/code/Magento/Tax/Test/Unit/Model/Quote/GrandTotalDetailsPluginTest.php b/app/code/Magento/Tax/Test/Unit/Model/Quote/GrandTotalDetailsPluginTest.php
index 52af1315ba76685ac64662f425bcfb9f6b11837e..693b0d437afc45853b5ab224d04c535f1af1ca92 100644
--- a/app/code/Magento/Tax/Test/Unit/Model/Quote/GrandTotalDetailsPluginTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Model/Quote/GrandTotalDetailsPluginTest.php
@@ -38,11 +38,6 @@ class GrandTotalDetailsPluginTest extends \PHPUnit_Framework_TestCase
      */
     protected $subjectMock;
 
-    /**
-     * @var \Closure|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $closureMock;
-
     /**
      * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
      */
@@ -153,7 +148,7 @@ class GrandTotalDetailsPluginTest extends \PHPUnit_Framework_TestCase
         return $taxDetailsMock;
     }
 
-    public function testAroundProcess()
+    public function testAfterProcess()
     {
         $taxRate = [
             'percent' => 8.25,
@@ -211,11 +206,7 @@ class GrandTotalDetailsPluginTest extends \PHPUnit_Framework_TestCase
             'tax' => $taxSegmentMock,
         ];
 
-        $this->closureMock = function () use ($totalSegments) {
-            return $totalSegments;
-        };
-
-        $result = $this->model->aroundProcess($this->subjectMock, $this->closureMock, $addressTotals);
+        $result = $this->model->afterProcess($this->subjectMock, $totalSegments, $addressTotals);
         $this->assertEquals($totalSegments, $result);
     }
 }
diff --git a/app/code/Magento/Theme/Model/Url/Plugin/Signature.php b/app/code/Magento/Theme/Model/Url/Plugin/Signature.php
index 2a4d878e80d7a1fec2a8daaf970298575b8a633a..8934c598feb79a438b442a39612f81673b3d8f4b 100644
--- a/app/code/Magento/Theme/Model/Url/Plugin/Signature.php
+++ b/app/code/Magento/Theme/Model/Url/Plugin/Signature.php
@@ -47,7 +47,7 @@ class Signature
      * Append signature to rendered base URL for static view files
      *
      * @param \Magento\Framework\Url\ScopeInterface $subject
-     * @param callable $proceed
+     * @param string $baseUrl
      * @param string $type
      * @param null $secure
      * @return string
@@ -55,13 +55,12 @@ class Signature
      *
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundGetBaseUrl(
+    public function afterGetBaseUrl(
         \Magento\Framework\Url\ScopeInterface $subject,
-        \Closure $proceed,
+        $baseUrl,
         $type = \Magento\Framework\UrlInterface::URL_TYPE_LINK,
         $secure = null
     ) {
-        $baseUrl = $proceed($type, $secure);
         if ($type == \Magento\Framework\UrlInterface::URL_TYPE_STATIC && $this->isUrlSignatureEnabled()) {
             $baseUrl .= $this->renderUrlSignature() . '/';
         }
diff --git a/app/code/Magento/Theme/Test/Unit/Model/Url/Plugin/SignatureTest.php b/app/code/Magento/Theme/Test/Unit/Model/Url/Plugin/SignatureTest.php
index 46097a13db12f48c6670649568e44e1cd6d67ee6..58cf9bcc31e5b8bb4a02cf9d5c93e5ca6f645bce 100644
--- a/app/code/Magento/Theme/Test/Unit/Model/Url/Plugin/SignatureTest.php
+++ b/app/code/Magento/Theme/Test/Unit/Model/Url/Plugin/SignatureTest.php
@@ -3,9 +3,6 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
-// @codingStandardsIgnoreFile
-
 namespace Magento\Theme\Test\Unit\Model\Url\Plugin;
 
 use \Magento\Theme\Model\Url\Plugin\Signature;
@@ -27,29 +24,25 @@ class SignatureTest extends \PHPUnit_Framework_TestCase
      */
     private $deploymentVersion;
 
-    /**
-     * @var callable
-     */
-    private $closureMock;
-
     protected function setUp()
     {
         $this->config = $this->getMock(\Magento\Framework\View\Url\ConfigInterface::class);
-        $this->deploymentVersion = $this->getMock(
-            \Magento\Framework\App\View\Deployment\Version::class, [], [], '', false
+        $this->deploymentVersion = $this->getMock(
+            \Magento\Framework\App\View\Deployment\Version::class,
+            [],
+            [],
+            '',
+            false
         );
-        $this->closureMock = function () {
-            return 'http://127.0.0.1/magento/pub/static/';
-        };
         $this->object = new Signature($this->config, $this->deploymentVersion);
     }
 
     /**
      * @param bool|int $fixtureConfigFlag
      * @param string $inputUrlType
-     * @dataProvider aroundGetBaseUrlInactiveDataProvider
+     * @dataProvider afterGetBaseUrlInactiveDataProvider
      */
-    public function testAroundGetBaseUrlInactive($fixtureConfigFlag, $inputUrlType)
+    public function testAfterGetBaseUrlInactive($fixtureConfigFlag, $inputUrlType)
     {
         $this->config
             ->expects($this->any())
@@ -59,11 +52,14 @@ class SignatureTest extends \PHPUnit_Framework_TestCase
         $this->deploymentVersion->expects($this->never())->method($this->anything());
 
         $url = $this->getMockForAbstractClass(\Magento\Framework\Url\ScopeInterface::class);
-        $actualResult = $this->object->aroundGetBaseUrl($url, $this->closureMock, $inputUrlType);
+        $actualResult = $this->object->afterGetBaseUrl($url, 'http://127.0.0.1/magento/pub/static/', $inputUrlType);
         $this->assertEquals('http://127.0.0.1/magento/pub/static/', $actualResult);
     }
 
-    public function aroundGetBaseUrlInactiveDataProvider()
+    /**
+     * @return array
+     */
+    public function afterGetBaseUrlInactiveDataProvider()
     {
         return [
             'disabled in config, relevant URL type'  => [0, \Magento\Framework\UrlInterface::URL_TYPE_STATIC],
@@ -81,8 +77,10 @@ class SignatureTest extends \PHPUnit_Framework_TestCase
         $this->deploymentVersion->expects($this->once())->method('getValue')->will($this->returnValue('123'));
 
         $url = $this->getMockForAbstractClass(\Magento\Framework\Url\ScopeInterface::class);
-        $actualResult = $this->object->aroundGetBaseUrl(
-            $url, $this->closureMock, \Magento\Framework\UrlInterface::URL_TYPE_STATIC
+        $actualResult = $this->object->afterGetBaseUrl(
+            $url,
+            'http://127.0.0.1/magento/pub/static/',
+            \Magento\Framework\UrlInterface::URL_TYPE_STATIC
         );
         $this->assertEquals('http://127.0.0.1/magento/pub/static/version123/', $actualResult);
     }
diff --git a/app/code/Magento/Vault/Plugin/PaymentVaultAttributesLoad.php b/app/code/Magento/Vault/Plugin/PaymentVaultAttributesLoad.php
index 810a8c5242da8ce24cbbb5ac15d63260f3853687..710b765d11effb155d17c02986ec561e0d54207d 100644
--- a/app/code/Magento/Vault/Plugin/PaymentVaultAttributesLoad.php
+++ b/app/code/Magento/Vault/Plugin/PaymentVaultAttributesLoad.php
@@ -10,6 +10,8 @@ namespace Magento\Vault\Plugin;
 use Magento\Sales\Api\Data\OrderPaymentExtensionInterface;
 use Magento\Sales\Api\Data\OrderPaymentInterface;
 use Magento\Vault\Api\PaymentTokenManagementInterface;
+use Magento\Sales\Api\Data\OrderPaymentExtensionFactory;
+use Magento\Vault\Api\Data\PaymentTokenInterface;
 
 /**
  * Plugin for loading vault payment extension attribute to order/payment entity
@@ -17,7 +19,7 @@ use Magento\Vault\Api\PaymentTokenManagementInterface;
 class PaymentVaultAttributesLoad
 {
     /**
-     * @var \Magento\Sales\Api\Data\OrderPaymentExtensionFactory
+     * @var OrderPaymentExtensionFactory
      */
     protected $paymentExtensionFactory;
 
@@ -27,11 +29,11 @@ class PaymentVaultAttributesLoad
     protected $paymentTokenManagement;
 
     /**
-     * @param \Magento\Sales\Api\Data\OrderPaymentExtensionFactory $paymentExtensionFactory
+     * @param OrderPaymentExtensionFactory $paymentExtensionFactory
      * @param PaymentTokenManagement|PaymentTokenManagementInterface $paymentTokenManagement
      */
     public function __construct(
-        \Magento\Sales\Api\Data\OrderPaymentExtensionFactory $paymentExtensionFactory,
+        OrderPaymentExtensionFactory $paymentExtensionFactory,
         PaymentTokenManagementInterface $paymentTokenManagement
     ) {
         $this->paymentExtensionFactory = $paymentExtensionFactory;
@@ -42,16 +44,13 @@ class PaymentVaultAttributesLoad
      * Load vault payment extension attribute to order/payment entity
      *
      * @param OrderPaymentInterface $payment
-     * @param \Closure $proceed
+     * @param OrderPaymentExtensionInterface|null $paymentExtension
      * @return OrderPaymentExtensionInterface
      */
-    public function aroundGetExtensionAttributes(
+    public function afterGetExtensionAttributes(
         OrderPaymentInterface $payment,
-        \Closure $proceed
+        OrderPaymentExtensionInterface $paymentExtension = null
     ) {
-        /** @var OrderPaymentExtensionInterface $paymentExtension */
-        $paymentExtension = $proceed();
-
         if ($paymentExtension === null) {
             $paymentExtension = $this->paymentExtensionFactory->create();
         }
@@ -59,7 +58,7 @@ class PaymentVaultAttributesLoad
         $paymentToken = $paymentExtension->getVaultPaymentToken();
         if ($paymentToken === null) {
             $paymentToken = $this->paymentTokenManagement->getByPaymentId($payment->getEntityId());
-            if ($paymentToken instanceof \Magento\Vault\Api\Data\PaymentTokenInterface) {
+            if ($paymentToken instanceof PaymentTokenInterface) {
                 $paymentExtension->setVaultPaymentToken($paymentToken);
             }
             $payment->setExtensionAttributes($paymentExtension);
diff --git a/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php b/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php
index c00f2f3491e03b16628d49ea34e5990e705c13e5..43344c196ec524e053842854db76c86951f70ad0 100644
--- a/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php
+++ b/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php
@@ -92,29 +92,27 @@ class ContextPlugin
 
     /**
      * @param \Magento\Framework\App\ActionInterface $subject
-     * @param callable $proceed
      * @param \Magento\Framework\App\RequestInterface $request
-     * @return mixed
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      * @SuppressWarnings(PHPMD.NPathComplexity)
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
-    public function aroundDispatch(
+    public function beforeDispatch(
         \Magento\Framework\App\ActionInterface $subject,
-        \Closure $proceed,
         \Magento\Framework\App\RequestInterface $request
     ) {
         if (!$this->weeeHelper->isEnabled() ||
             !$this->customerSession->isLoggedIn() ||
             !$this->moduleManager->isEnabled('Magento_PageCache') ||
             !$this->cacheConfig->isEnabled()) {
-            return $proceed($request);
+            return;
         }
 
         $basedOn = $this->taxHelper->getTaxBasedOn();
         if ($basedOn != 'shipping' && $basedOn != 'billing') {
-            return $proceed($request);
+            return;
         }
 
         $weeeTaxRegion = $this->getWeeeTaxRegion($basedOn);
@@ -124,7 +122,7 @@ class ContextPlugin
 
         if (!$countryId && !$regionId) {
             // country and region does not exist
-            return $proceed($request);
+            return;
         } else if ($countryId && !$regionId) {
             // country exist and region does not exist
             $regionId = 0;
@@ -158,7 +156,6 @@ class ContextPlugin
                 0
             );
         }
-        return $proceed($request);
     }
 
     /**
diff --git a/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php b/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php
index ebb0837d3c14474f27951a1158927d75cace4ed3..77c8070ca44eb51ff546a71a85545393657f6f94 100644
--- a/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php
+++ b/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php
@@ -123,7 +123,7 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    public function testAroundDispatchBasedOnDefault()
+    public function testBeforeDispatchBasedOnDefault()
     {
         $this->customerSessionMock->expects($this->once())
             ->method('isLoggedIn')
@@ -187,14 +187,11 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
 
         $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class);
         $request = $this->getMock(\Magento\Framework\App\Request\Http::class, ['getActionName'], [], '', false);
-        $expectedResult = 'expectedResult';
-        $proceed = function ($request) use ($expectedResult) {
-            return $expectedResult;
-        };
-        $this->contextPlugin->aroundDispatch($action, $proceed, $request);
+
+        $this->contextPlugin->beforeDispatch($action, $request);
     }
 
-    public function testAroundDispatchBasedOnOrigin()
+    public function testBeforeDispatchBasedOnOrigin()
     {
         $this->customerSessionMock->expects($this->once())
             ->method('isLoggedIn')
@@ -219,14 +216,11 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
 
         $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class);
         $request = $this->getMock(\Magento\Framework\App\Request\Http::class, ['getActionName'], [], '', false);
-        $expectedResult = 'expectedResult';
-        $proceed = function ($request) use ($expectedResult) {
-            return $expectedResult;
-        };
-        $this->contextPlugin->aroundDispatch($action, $proceed, $request);
+
+        $this->contextPlugin->beforeDispatch($action, $request);
     }
 
-    public function testAroundDispatchBasedOnBilling()
+    public function testBeforeDispatchBasedOnBilling()
     {
         $this->customerSessionMock->expects($this->once())
             ->method('isLoggedIn')
@@ -294,14 +288,11 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
 
         $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class);
         $request = $this->getMock(\Magento\Framework\App\Request\Http::class, ['getActionName'], [], '', false);
-        $expectedResult = 'expectedResult';
-        $proceed = function ($request) use ($expectedResult) {
-            return $expectedResult;
-        };
-        $this->contextPlugin->aroundDispatch($action, $proceed, $request);
+
+        $this->contextPlugin->beforeDispatch($action, $request);
     }
 
-    public function testAroundDispatchBasedOnShipping()
+    public function testBeforeDispatchBasedOnShipping()
     {
         $this->customerSessionMock->expects($this->once())
             ->method('isLoggedIn')
@@ -369,10 +360,7 @@ class ContextPluginTest extends \PHPUnit_Framework_TestCase
 
         $action = $this->objectManager->getObject(\Magento\Framework\App\Test\Unit\Action\Stub\ActionStub::class);
         $request = $this->getMock(\Magento\Framework\App\Request\Http::class, ['getActionName'], [], '', false);
-        $expectedResult = 'expectedResult';
-        $proceed = function ($request) use ($expectedResult) {
-            return $expectedResult;
-        };
-        $this->contextPlugin->aroundDispatch($action, $proceed, $request);
+
+        $this->contextPlugin->beforeDispatch($action, $request);
     }
 }
diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderInvoiceCreateTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderInvoiceCreateTest.php
index cb384134a7c68c63955a0d97ab27aaa6c4f4f002..60c9f54ea132c5a0af3970ac58b87710b7b1688c 100644
--- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderInvoiceCreateTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderInvoiceCreateTest.php
@@ -10,7 +10,7 @@ namespace Magento\Sales\Service\V1;
  */
 class OrderInvoiceCreateTest extends \Magento\TestFramework\TestCase\WebapiAbstract
 {
-    const SERVICE_READ_NAME = 'salesOrderInvoiceV1';
+    const SERVICE_READ_NAME = 'salesInvoiceOrderV1';
     const SERVICE_VERSION = 'V1';
 
     /**
diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipOrderTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipOrderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8de7c4dc7f65b9318eca04c2656f22aa08cc447f
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipOrderTest.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Sales\Service\V1;
+
+/**
+ * API test for creation of Shipment for certain Order.
+ */
+class ShipOrderTest extends \Magento\TestFramework\TestCase\WebapiAbstract
+{
+    const SERVICE_READ_NAME = 'salesShipOrderV1';
+    const SERVICE_VERSION = 'V1';
+
+    /**
+     * @var \Magento\Framework\ObjectManagerInterface
+     */
+    private $objectManager;
+
+    /**
+     * @var \Magento\Sales\Api\ShipmentRepositoryInterface
+     */
+    private $shipmentRepository;
+
+    protected function setUp()
+    {
+        $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+        $this->shipmentRepository = $this->objectManager->get(
+            \Magento\Sales\Api\ShipmentRepositoryInterface::class
+        );
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/Sales/_files/order_new.php
+     */
+    public function testShipOrder()
+    {
+        /** @var \Magento\Sales\Model\Order $existingOrder */
+        $existingOrder = $this->objectManager->create(\Magento\Sales\Model\Order::class)
+            ->loadByIncrementId('100000001');
+
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/order/' . $existingOrder->getId() . '/ship',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_READ_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_READ_NAME . 'execute',
+            ],
+        ];
+
+        $requestData = [
+            'orderId' => $existingOrder->getId(),
+            'items' => [],
+            'comment' => [
+                'comment' => 'Test Comment',
+                'is_visible_on_front' => 1,
+            ],
+            'tracks' => [
+                [
+                    'track_number' => 'TEST_TRACK_0001',
+                    'title' => 'Simple shipment track',
+                    'carrier_code' => 'UPS'
+                ]
+            ]
+        ];
+
+        /** @var \Magento\Sales\Api\Data\OrderItemInterface $item */
+        foreach ($existingOrder->getAllItems() as $item) {
+            $requestData['items'][] = [
+                'order_item_id' => $item->getItemId(),
+                'qty' => $item->getQtyOrdered(),
+            ];
+        }
+
+        $result = $this->_webApiCall($serviceInfo, $requestData);
+
+        $this->assertNotEmpty($result);
+
+        try {
+            $this->shipmentRepository->get($result);
+        } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+            $this->fail('Failed asserting that Shipment was created');
+        }
+
+        /** @var \Magento\Sales\Model\Order $updatedOrder */
+        $updatedOrder = $this->objectManager->create(\Magento\Sales\Model\Order::class)
+            ->loadByIncrementId('100000001');
+
+        $this->assertNotEquals(
+            $existingOrder->getStatus(),
+            $updatedOrder->getStatus(),
+            'Failed asserting that Order status was changed'
+        );
+    }
+}
diff --git a/lib/internal/Magento/Framework/EntityManager/CustomAttributesMapper.php b/lib/internal/Magento/Framework/EntityManager/CustomAttributesMapper.php
index 9147d47f3d9dd8c21cec93ff47de2e065a13dd9d..fe3a199da86a32bdb866894b53831fe4c5e8a246 100644
--- a/lib/internal/Magento/Framework/EntityManager/CustomAttributesMapper.php
+++ b/lib/internal/Magento/Framework/EntityManager/CustomAttributesMapper.php
@@ -55,8 +55,9 @@ class CustomAttributesMapper implements MapperInterface
      */
     public function entityToDatabase($entityType, $data)
     {
-        $metadata = $this->metadataPool->getMetadata($entityType);
-        if (!$metadata->getEavEntityType()) {
+        if (!$this->metadataPool->hasConfiguration($entityType)
+            || !$this->metadataPool->getMetadata($entityType)->getEavEntityType()
+        ) {
             return $data;
         }
         if (isset($data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) {
diff --git a/lib/internal/Magento/Framework/EntityManager/Test/Unit/CustomAttributesMapperTest.php b/lib/internal/Magento/Framework/EntityManager/Test/Unit/CustomAttributesMapperTest.php
index a39ad4afaa6eed43f552f14b5bdb85c16fa712dc..56977af1dd6eb5c62872572ec22f2024f7479223 100644
--- a/lib/internal/Magento/Framework/EntityManager/Test/Unit/CustomAttributesMapperTest.php
+++ b/lib/internal/Magento/Framework/EntityManager/Test/Unit/CustomAttributesMapperTest.php
@@ -48,12 +48,18 @@ class CustomAttributesMapperTest extends \PHPUnit_Framework_TestCase
 
         $metadataPool = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class)
             ->disableOriginalConstructor()
-            ->setMethods(['getMetadata'])
+            ->setMethods(['getMetadata', 'hasConfiguration'])
             ->getMock();
+        $metadataPool->expects($this->any())
+            ->method('hasConfiguration')
+            ->willReturn(true);
         $metadataPool->expects($this->any())
             ->method('getMetadata')
             ->with($this->equalTo(\Magento\Customer\Api\Data\AddressInterface::class))
             ->will($this->returnValue($metadata));
+        $metadataPool->expects($this->once())
+            ->method('hasConfiguration')
+            ->willReturn(true);
 
         $searchCriteriaBuilder = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteriaBuilder::class)
             ->disableOriginalConstructor()
@@ -76,6 +82,7 @@ class CustomAttributesMapperTest extends \PHPUnit_Framework_TestCase
                 'metadataPool' => $metadataPool,
                 'searchCriteriaBuilder' => $searchCriteriaBuilder
             ]);
+
         $actual = $customAttributesMapper->entityToDatabase(
             \Magento\Customer\Api\Data\AddressInterface::class,
             [
diff --git a/lib/internal/Magento/Framework/EntityManager/TypeResolver.php b/lib/internal/Magento/Framework/EntityManager/TypeResolver.php
index 28e2bdaa7094223d604cf174cd627982b6ecd0e9..2718162e80d669acfbc70eb78310b5911d7a668b 100644
--- a/lib/internal/Magento/Framework/EntityManager/TypeResolver.php
+++ b/lib/internal/Magento/Framework/EntityManager/TypeResolver.php
@@ -20,7 +20,8 @@ class TypeResolver
      */
     private $typeMapping = [
         \Magento\SalesRule\Model\Rule::class => \Magento\SalesRule\Api\Data\RuleInterface::class,
-        \Magento\SalesRule\Model\Rule\Interceptor::class => \Magento\SalesRule\Api\Data\RuleInterface::class
+        \Magento\SalesRule\Model\Rule\Interceptor::class => \Magento\SalesRule\Api\Data\RuleInterface::class,
+        \Magento\SalesRule\Model\Rule\Proxy::class => \Magento\SalesRule\Api\Data\RuleInterface::class
     ];
 
     /**
@@ -50,8 +51,7 @@ class TypeResolver
         $dataInterfaces = [];
         foreach ($interfaceNames as $interfaceName) {
             if (strpos($interfaceName, '\Api\Data\\')) {
-                $dataInterfaces[] = isset($this->config[$interfaceName])
-                    ? $this->config[$interfaceName] : $interfaceName;
+                $dataInterfaces[] = $interfaceName;
             }
         }
 
@@ -64,7 +64,9 @@ class TypeResolver
                 $this->typeMapping[$className] = $dataInterface;
             }
         }
-
+        if (empty($this->typeMapping[$className])) {
+            $this->typeMapping[$className] = reset($dataInterfaces);
+        }
         return $this->typeMapping[$className];
     }
 }
diff --git a/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php b/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php
index 1ff11552533b1e6514409402e778687483ae6d18..93945c607b53d7498a9031fbb475f30e015ecb6c 100644
--- a/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php
+++ b/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php
@@ -111,13 +111,13 @@ class InterfaceValidator
                     );
                     break;
                 case self::METHOD_AFTER:
-                    // TODO: Remove this condition check in scope of MAGETWO-56123
                     if (count($pluginMethodParameters) > 1) {
                         // remove result
                         array_shift($pluginMethodParameters);
+                        $matchedParameters = array_intersect_key($originMethodParameters, $pluginMethodParameters);
                         $this->validateMethodsParameters(
                             $pluginMethodParameters,
-                            $originMethodParameters,
+                            $matchedParameters,
                             $pluginClass,
                             $pluginMethod->getName()
                         );
diff --git a/lib/internal/Magento/Framework/Module/Plugin/DbStatusValidator.php b/lib/internal/Magento/Framework/Module/Plugin/DbStatusValidator.php
index 6f312529c12b40eaacc9d815411d4f065999b2d3..850d64b14ae0cd7efc4c067f919eb99245ba0414 100644
--- a/lib/internal/Magento/Framework/Module/Plugin/DbStatusValidator.php
+++ b/lib/internal/Magento/Framework/Module/Plugin/DbStatusValidator.php
@@ -1,22 +1,24 @@
 <?php
 /**
- * Validation of DB up to date state
- *
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
-// @codingStandardsIgnoreFile
-
 namespace Magento\Framework\Module\Plugin;
 
-use Magento\Framework\Cache\FrontendInterface;
+use Magento\Framework\Cache\FrontendInterface as FrontendCacheInterface;
 use Magento\Framework\Module\DbVersionInfo;
+use Magento\Framework\App\FrontController;
+use Magento\Framework\App\RequestInterface;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Phrase;
 
+/**
+ * Validation of DB up to date state
+ */
 class DbStatusValidator
 {
     /**
-     * @var FrontendInterface
+     * @var FrontendCacheInterface
      */
     private $cache;
 
@@ -26,47 +28,40 @@ class DbStatusValidator
     private $dbVersionInfo;
 
     /**
-     * @param FrontendInterface $cache
+     * @param FrontendCacheInterface $cache
      * @param DbVersionInfo $dbVersionInfo
      */
-    public function __construct(
-        FrontendInterface $cache,
-        DbVersionInfo $dbVersionInfo
-    ) {
+    public function __construct(FrontendCacheInterface $cache, DbVersionInfo $dbVersionInfo)
+    {
         $this->cache = $cache;
         $this->dbVersionInfo = $dbVersionInfo;
     }
 
     /**
-     * @param \Magento\Framework\App\FrontController $subject
-     * @param \Closure $proceed
-     * @param \Magento\Framework\App\RequestInterface $request
+     * Perform check if DB is up to date
+     *
+     * @param FrontController $subject
+     * @param RequestInterface $request
+     * @return void
+     * @throws LocalizedException
      *
-     * @throws \Magento\Framework\Exception\LocalizedException
-     * @return \Magento\Framework\App\ResponseInterface
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDispatch(
-        \Magento\Framework\App\FrontController $subject,
-        \Closure $proceed,
-        \Magento\Framework\App\RequestInterface $request
-    ) {
+    public function beforeDispatch(FrontController $subject, RequestInterface $request)
+    {
         if (!$this->cache->load('db_is_up_to_date')) {
             $errors = $this->dbVersionInfo->getDbVersionErrors();
+
             if ($errors) {
-                $formattedErrors = $this->formatErrors($errors);
-                throw new \Magento\Framework\Exception\LocalizedException(
-                    new \Magento\Framework\Phrase(
-                        'Please upgrade your database: Run "bin/magento setup:upgrade" from the Magento root directory.'
-                        . ' %1The following modules are outdated:%2%3',
-                        [PHP_EOL, PHP_EOL, implode(PHP_EOL, $formattedErrors)]
-                    )
-                );
+                $message = 'Please upgrade your database: '
+                    . "Run \"bin/magento setup:upgrade\" from the Magento root directory.\n"
+                    . "The following modules are outdated:\n%1";
+
+                throw new LocalizedException(new Phrase($message, [implode("\n", $this->formatErrors($errors))]));
             } else {
                 $this->cache->save('true', 'db_is_up_to_date');
             }
         }
-        return $proceed($request);
     }
 
     /**
@@ -78,12 +73,13 @@ class DbStatusValidator
     private function formatErrors($errorsData)
     {
         $formattedErrors = [];
+
         foreach ($errorsData as $error) {
-            $formattedErrors[] = $error[DbVersionInfo::KEY_MODULE] .
-                ' ' . $error[DbVersionInfo::KEY_TYPE] .
-                ': current version - ' . $error[DbVersionInfo::KEY_CURRENT ] .
-                ', required version - ' . $error[DbVersionInfo::KEY_REQUIRED];
+            $formattedErrors[] = $error[DbVersionInfo::KEY_MODULE] . ' ' . $error[DbVersionInfo::KEY_TYPE]
+                . ': current version - ' . $error[DbVersionInfo::KEY_CURRENT]
+                . ', required version - ' . $error[DbVersionInfo::KEY_REQUIRED];
         }
+
         return $formattedErrors;
     }
 }
diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/Plugin/DbStatusValidatorTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/Plugin/DbStatusValidatorTest.php
index fee050560815c293a1f7a724de0653e606e21fc8..c0563020110daa8b4ef5a4e7cd3824dee2a5d3b0 100644
--- a/lib/internal/Magento/Framework/Module/Test/Unit/Plugin/DbStatusValidatorTest.php
+++ b/lib/internal/Magento/Framework/Module/Test/Unit/Plugin/DbStatusValidatorTest.php
@@ -21,11 +21,6 @@ class DbStatusValidatorTest extends \PHPUnit_Framework_TestCase
      */
     protected $_cacheMock;
 
-    /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject
      */
@@ -49,9 +44,6 @@ class DbStatusValidatorTest extends \PHPUnit_Framework_TestCase
     protected function setUp()
     {
         $this->_cacheMock = $this->getMock(\Magento\Framework\Cache\FrontendInterface::class);
-        $this->closureMock = function () {
-            return 'Expected';
-        };
         $this->requestMock = $this->getMock(\Magento\Framework\App\RequestInterface::class);
         $this->subjectMock = $this->getMock(\Magento\Framework\App\FrontController::class, [], [], '', false);
         $moduleList = $this->getMockForAbstractClass(\Magento\Framework\Module\ModuleListInterface::class);
@@ -85,8 +77,8 @@ class DbStatusValidatorTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValueMap($returnMap));
 
         $this->assertEquals(
-            'Expected',
-            $this->_model->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
+            null,
+            $this->_model->beforeDispatch($this->subjectMock, $this->requestMock)
         );
     }
 
@@ -101,8 +93,8 @@ class DbStatusValidatorTest extends \PHPUnit_Framework_TestCase
         $this->moduleManager->expects($this->never())
             ->method('isDbDataUpToDate');
         $this->assertEquals(
-            'Expected',
-            $this->_model->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
+            null,
+            $this->_model->beforeDispatch($this->subjectMock, $this->requestMock)
         );
     }
 
@@ -125,7 +117,7 @@ class DbStatusValidatorTest extends \PHPUnit_Framework_TestCase
             ->method('getDbVersionErrors')
             ->will($this->returnValue($dbVersionErrors));
 
-        $this->_model->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock);
+        $this->_model->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/Test/Unit/Module/Plugin/DbStatusValidatorTest.php b/lib/internal/Magento/Framework/Test/Unit/Module/Plugin/DbStatusValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f22192639f77e62f52ccae93906b096b733934ba
--- /dev/null
+++ b/lib/internal/Magento/Framework/Test/Unit/Module/Plugin/DbStatusValidatorTest.php
@@ -0,0 +1,137 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Test\Unit\Module\Plugin;
+
+use Magento\Framework\Module\Plugin\DbStatusValidator as DbStatusValidatorPlugin;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Framework\Cache\FrontendInterface as FrontendCacheInterface;
+use Magento\Framework\Module\DbVersionInfo;
+use Magento\Framework\App\FrontController;
+use Magento\Framework\App\RequestInterface;
+use Magento\Framework\Exception\LocalizedException;
+
+class DbStatusValidatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var DbStatusValidatorPlugin
+     */
+    private $plugin;
+
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var FrontendCacheInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $cacheMock;
+
+    /**
+     * @var DbVersionInfo|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $dbVersionInfoMock;
+
+    /**
+     * @var FrontController|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $frontControllerMock;
+
+    /**
+     * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $requestMock;
+
+    protected function setUp()
+    {
+        $this->cacheMock = $this->getMockBuilder(FrontendCacheInterface::class)
+            ->getMockForAbstractClass();
+        $this->dbVersionInfoMock = $this->getMockBuilder(DbVersionInfo::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->frontControllerMock = $this->getMockBuilder(FrontController::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->requestMock = $this->getMockBuilder(RequestInterface::class)
+            ->getMockForAbstractClass();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->plugin = $this->objectManagerHelper->getObject(
+            DbStatusValidatorPlugin::class,
+            [
+                'cache' => $this->cacheMock,
+                'dbVersionInfo' => $this->dbVersionInfoMock
+            ]
+        );
+    }
+
+    public function testBeforeDispatchUpToDate()
+    {
+        $this->cacheMock->expects(static::any())
+            ->method('load')
+            ->with('db_is_up_to_date')
+            ->willReturn('cache_data');
+        $this->dbVersionInfoMock->expects(static::never())
+            ->method('getDbVersionErrors');
+        $this->cacheMock->expects(static::never())
+            ->method('save');
+
+        $this->plugin->beforeDispatch($this->frontControllerMock, $this->requestMock);
+    }
+
+    public function testBeforeDispatchOutOfDateNoErrors()
+    {
+        $this->cacheMock->expects(static::any())
+            ->method('load')
+            ->with('db_is_up_to_date')
+            ->willReturn(false);
+        $this->dbVersionInfoMock->expects(static::once())
+            ->method('getDbVersionErrors')
+            ->willReturn([]);
+        $this->cacheMock->expects(static::once())
+            ->method('save')
+            ->with('true', 'db_is_up_to_date', [], null)
+            ->willReturn(true);
+
+        $this->plugin->beforeDispatch($this->frontControllerMock, $this->requestMock);
+    }
+
+    public function testBeforeDispatchOutOfDateWithErrors()
+    {
+        $errors = [
+            [
+                DbVersionInfo::KEY_MODULE => 'Magento_Module1',
+                DbVersionInfo::KEY_TYPE => 'schema',
+                DbVersionInfo::KEY_CURRENT => '3.3.3',
+                DbVersionInfo::KEY_REQUIRED => '4.4.4'
+            ],
+            [
+                DbVersionInfo::KEY_MODULE => 'Magento_Module2',
+                DbVersionInfo::KEY_TYPE => 'data',
+                DbVersionInfo::KEY_CURRENT => '2.8.7',
+                DbVersionInfo::KEY_REQUIRED => '5.1.6'
+            ]
+        ];
+        $message = 'Please upgrade your database: '
+            . "Run \"bin/magento setup:upgrade\" from the Magento root directory.\n"
+            . "The following modules are outdated:\n"
+            . "Magento_Module1 schema: current version - 3.3.3, required version - 4.4.4\n"
+            . "Magento_Module2 data: current version - 2.8.7, required version - 5.1.6";
+
+        $this->cacheMock->expects(static::any())
+            ->method('load')
+            ->with('db_is_up_to_date')
+            ->willReturn(false);
+        $this->dbVersionInfoMock->expects(static::once())
+            ->method('getDbVersionErrors')
+            ->willReturn($errors);
+        $this->cacheMock->expects(static::never())
+            ->method('save');
+
+        $this->setExpectedException(LocalizedException::class, $message);
+        $this->plugin->beforeDispatch($this->frontControllerMock, $this->requestMock);
+    }
+}