diff --git a/app/code/Magento/Catalog/Observer/AddCatalogToTopmenuItemsObserver.php b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php
similarity index 74%
rename from app/code/Magento/Catalog/Observer/AddCatalogToTopmenuItemsObserver.php
rename to app/code/Magento/Catalog/Plugin/Block/Topmenu.php
index 608c7ee74b7cdda6409432f968734f2d1171a088..098b3282a1b107e56703ee4889f9ce005f25d223 100644
--- a/app/code/Magento/Catalog/Observer/AddCatalogToTopmenuItemsObserver.php
+++ b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php
@@ -3,17 +3,16 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-namespace Magento\Catalog\Observer;
+namespace Magento\Catalog\Plugin\Block;
 
 use Magento\Catalog\Model\Category;
 use Magento\Framework\Data\Collection;
 use Magento\Framework\Data\Tree\Node;
-use Magento\Framework\Event\ObserverInterface;
 
 /**
- * Observer that add Categories Tree to Topmenu
+ * Plugin for top menu block
  */
-class AddCatalogToTopmenuItemsObserver implements ObserverInterface
+class Topmenu
 {
     /**
      * Catalog category
@@ -38,11 +37,11 @@ class AddCatalogToTopmenuItemsObserver implements ObserverInterface
     private $layerResolver;
 
     /**
+     * Initialize dependencies.
+     *
      * @param \Magento\Catalog\Helper\Category $catalogCategory
-     * @param \Magento\Catalog\Model\Indexer\Category\Flat\State $categoryFlatState
      * @param \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
-     * @param \Magento\Catalog\Helper\Category $catalogCategory
      * @param \Magento\Catalog\Model\Layer\Resolver $layerResolver
      */
     public function __construct(
@@ -58,26 +57,27 @@ class AddCatalogToTopmenuItemsObserver implements ObserverInterface
     }
 
     /**
-     * Checking whether the using static urls in WYSIWYG allowed event
+     * Build category tree for menu block.
      *
-     * @param \Magento\Framework\Event\Observer $observer
+     * @param \Magento\Theme\Block\Html\Topmenu $subject
+     * @param string $outermostClass
+     * @param string $childrenWrapClass
+     * @param int $limit
      * @return void
+     * @SuppressWarnings("PMD.UnusedFormalParameter")
      */
-    public function execute(\Magento\Framework\Event\Observer $observer)
-    {
-        $block = $observer->getEvent()->getBlock();
-        $menuRootNode = $observer->getEvent()->getMenu();
-        $block->addIdentity(Category::CACHE_TAG);
-
+    public function beforeGetHtml(
+        \Magento\Theme\Block\Html\Topmenu $subject,
+        $outermostClass = '',
+        $childrenWrapClass = '',
+        $limit = 0
+    ) {
         $rootId = $this->storeManager->getStore()->getRootCategoryId();
         $storeId = $this->storeManager->getStore()->getId();
-
         /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
         $collection = $this->getCategoryTree($storeId, $rootId);
-
         $currentCategory = $this->getCurrentCategory();
-
-        $mapping = [$rootId => $menuRootNode];  // use nodes stack to avoid recursion
+        $mapping = [$rootId => $subject->getMenu()];  // use nodes stack to avoid recursion
         foreach ($collection as $category) {
             if (!isset($mapping[$category->getParentId()])) {
                 continue;
@@ -94,8 +94,28 @@ class AddCatalogToTopmenuItemsObserver implements ObserverInterface
             $parentCategoryNode->addChild($categoryNode);
 
             $mapping[$category->getId()] = $categoryNode; //add node in stack
+        }
+    }
 
-            $block->addIdentity(Category::CACHE_TAG . '_' . $category->getId());
+    /**
+     * Add list of associated identities to the top menu block for caching purposes.
+     *
+     * @param \Magento\Theme\Block\Html\Topmenu $subject
+     * @return void
+     */
+    public function beforeGetIdentities(\Magento\Theme\Block\Html\Topmenu $subject)
+    {
+        $subject->addIdentity(Category::CACHE_TAG);
+        $rootId = $this->storeManager->getStore()->getRootCategoryId();
+        $storeId = $this->storeManager->getStore()->getId();
+        /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
+        $collection = $this->getCategoryTree($storeId, $rootId);
+        $mapping = [$rootId => $subject->getMenu()];  // use nodes stack to avoid recursion
+        foreach ($collection as $category) {
+            if (!isset($mapping[$category->getParentId()])) {
+                continue;
+            }
+            $subject->addIdentity(Category::CACHE_TAG . '_' . $category->getId());
         }
     }
 
diff --git a/app/code/Magento/Catalog/Test/Unit/Observer/AddCatalogToTopmenuItemsObserverTest.php b/app/code/Magento/Catalog/Test/Unit/Plugin/Block/TopmenuTest.php
similarity index 81%
rename from app/code/Magento/Catalog/Test/Unit/Observer/AddCatalogToTopmenuItemsObserverTest.php
rename to app/code/Magento/Catalog/Test/Unit/Plugin/Block/TopmenuTest.php
index 393cf8223d2a2d40ed54802515cdc91fa5fc804a..e0fe2deb232f68b989abb72463268d531e5e81cd 100644
--- a/app/code/Magento/Catalog/Test/Unit/Observer/AddCatalogToTopmenuItemsObserverTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Plugin/Block/TopmenuTest.php
@@ -6,16 +6,16 @@
 
 // @codingStandardsIgnoreFile
 
-namespace Magento\Catalog\Test\Unit\Observer;
+namespace Magento\Catalog\Test\Unit\Plugin\Block;
 
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
-class AddCatalogToTopmenuItemsObserverTest extends \PHPUnit_Framework_TestCase
+class TopmenuTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\Catalog\Observer\AddCatalogToTopmenuItemsObserver
+     * @var \Magento\Catalog\Plugin\Block\Topmenu
      */
-    protected $_observer;
+    protected $block;
 
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Helper\Category
@@ -100,8 +100,8 @@ class AddCatalogToTopmenuItemsObserverTest extends \PHPUnit_Framework_TestCase
         $collection->expects($this->once())->method('getIterator')
             ->willReturn(new \ArrayIterator([]));
 
-        $this->_observer = (new ObjectManager($this))->getObject(
-            'Magento\Catalog\Observer\AddCatalogToTopmenuItemsObserver',
+        $this->block = (new ObjectManager($this))->getObject(
+            \Magento\Catalog\Plugin\Block\Topmenu::class,
             [
                 'catalogCategory' => $this->_catalogCategory,
                 'menuCategoryData' => $this->menuCategoryData,
@@ -142,23 +142,11 @@ class AddCatalogToTopmenuItemsObserverTest extends \PHPUnit_Framework_TestCase
         );
 
         $blockMock = $this->_getCleanMock('\Magento\Theme\Block\Html\Topmenu');
-
-        $eventMock = $this->getMock('\Magento\Framework\Event', ['getBlock'], [], '', false);
-        $eventMock->expects($this->once())
-            ->method('getBlock')
-            ->will($this->returnValue($blockMock));
-
-        $observerMock = $this->getMock('\Magento\Framework\Event\Observer', ['getEvent', 'getMenu'], [], '', false);
-        $observerMock->expects($this->any())
-            ->method('getEvent')
-            ->will($this->returnValue($eventMock));
-
-        return $observerMock;
+        return $blockMock;
     }
 
     public function testAddCatalogToTopMenuItems()
     {
-        $observer = $this->_preparationData();
-        $this->_observer->execute($observer);
+        $this->block->beforeGetHtml($this->_preparationData());
     }
 }
diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml
index 19a4f22a3c8e9425d474873f321f08baf062113c..78e46f0c39205ee7172c890739fa60a23946affa 100644
--- a/app/code/Magento/Catalog/etc/di.xml
+++ b/app/code/Magento/Catalog/etc/di.xml
@@ -46,6 +46,9 @@
     <type name="Magento\Customer\Model\ResourceModel\Visitor">
         <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" />
     </type>
+    <type name="Magento\Theme\Block\Html\Topmenu">
+        <plugin name="catalogTopmenu" type="Magento\Catalog\Plugin\Block\Topmenu" />
+    </type>
     <type name="Magento\Framework\Mview\View\StateInterface">
         <plugin name="setStatusForMview" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\MviewState" />
     </type>
diff --git a/app/code/Magento/Catalog/etc/frontend/events.xml b/app/code/Magento/Catalog/etc/frontend/events.xml
index afb3c70a14ade23fc8caab37ab4f3b564ae1f917..5ef8c724683141c38d735fcd0e321615d562ce6a 100644
--- a/app/code/Magento/Catalog/etc/frontend/events.xml
+++ b/app/code/Magento/Catalog/etc/frontend/events.xml
@@ -12,7 +12,4 @@
     <event name="customer_logout">
         <observer name="catalog" instance="Magento\Catalog\Observer\Compare\BindCustomerLogoutObserver" shared="false" />
     </event>
-    <event name="page_block_html_topmenu_gethtml_before">
-        <observer name="catalog_add_topmenu_items" instance="Magento\Catalog\Observer\AddCatalogToTopmenuItemsObserver" />
-    </event>
 </config>
diff --git a/app/code/Magento/Theme/Block/Html/Topmenu.php b/app/code/Magento/Theme/Block/Html/Topmenu.php
index d0205f51453bc4d003e6347af7b5754ac40d6d4c..52a4714fea8db053b49790a548538644866744cc 100644
--- a/app/code/Magento/Theme/Block/Html/Topmenu.php
+++ b/app/code/Magento/Theme/Block/Html/Topmenu.php
@@ -330,7 +330,9 @@ class Topmenu extends Template implements IdentityInterface
      */
     public function addIdentity($identity)
     {
-        $this->identities[] = $identity;
+        if (!in_array($identity, $this->identities)) {
+            $this->identities[] = $identity;
+        }
     }
 
     /**
@@ -364,4 +366,14 @@ class Topmenu extends Template implements IdentityInterface
     {
         return array_merge(parent::getCacheTags(), $this->getIdentities());
     }
+
+    /**
+     * Get menu object.
+     *
+     * @return Node
+     */
+    public function getMenu()
+    {
+        return $this->_menu;
+    }
 }