diff --git a/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php b/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php
index 027c59a..43f5ebf 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php
@@ -155,7 +155,7 @@ protected function encodePluginId($base_plugin_id, $derivative_id) {
    *
    * @param string $base_plugin_id
    *   The base plugin id of the plugin.
-   * @param array $base_definition
+   * @param array|object $base_definition
    *   The base plugin definition to build derivatives.
    *
    * @return \Drupal\Component\Plugin\Derivative\DerivativeInterface|null
@@ -165,7 +165,7 @@ protected function encodePluginId($base_plugin_id, $derivative_id) {
 +   *   Thrown if the 'derivative' class specified in the plugin definition does
 +   *   not implement \Drupal\Component\Plugin\Derivative\DerivativeInterface.
    */
-  protected function getDerivativeFetcher($base_plugin_id, array $base_definition) {
+  protected function getDerivativeFetcher($base_plugin_id, $base_definition) {
     if (!isset($this->derivativeFetchers[$base_plugin_id])) {
       $this->derivativeFetchers[$base_plugin_id] = FALSE;
       $class = $this->getDerivativeClass($base_definition);
@@ -191,7 +191,9 @@ protected function getDerivativeFetcher($base_plugin_id, array $base_definition)
    */
   protected function getDerivativeClass($base_definition) {
     $class = NULL;
-    if (isset($base_definition['derivative'])) {
+    // @todo: Support derivatives for plugin definitions as classes in
+    //   https://drupal.org/node/2168159.
+    if (is_array($base_definition) && isset($base_definition['derivative'])) {
       $class = $base_definition['derivative'];
       if (!is_subclass_of($class, '\Drupal\Component\Plugin\Derivative\DerivativeInterface')) {
         throw new InvalidDerivativeClassException(sprintf('Plugin (%s) derivative class "%s" has to implement interface \Drupal\Component\Plugin\Derivative\DerivativeInterface', $base_definition['id'], $class));
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index d100546..da1dc51 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -9,7 +9,6 @@
 
 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
-use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Field\FieldDefinition;
@@ -18,9 +17,8 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageManager;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Plugin\Discovery\AlterDecorator;
-use Drupal\Core\Plugin\Discovery\CacheDecorator;
-use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Plugin\Discovery\InfoHookDecorator;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
@@ -40,7 +38,7 @@
  * @see \Drupal\Core\Entity\EntityTypeInterface
  * @see hook_entity_type_alter()
  */
-class EntityManager extends PluginManagerBase implements EntityManagerInterface {
+class EntityManager extends DefaultPluginManager implements EntityManagerInterface {
 
   /**
    * The injection container that should be passed into the controller factory.
@@ -57,27 +55,6 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
   protected $controllers = array();
 
   /**
-   * The module handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
-   * The cache backend to use.
-   *
-   * @var \Drupal\Core\Cache\CacheBackendInterface
-   */
-  protected $cache;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManager
-   */
-  protected $languageManager;
-
-  /**
    * An array of field information per entity type, i.e. containing definitions.
    *
    * @var array
@@ -94,15 +71,6 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
   protected $fieldDefinitions;
 
   /**
-   * The root paths.
-   *
-   * @see self::__construct().
-   *
-   * @var \Traversable
-   */
-  protected $namespaces;
-
-  /**
    * The string translationManager.
    *
    * @var \Drupal\Core\StringTranslation\TranslationInterface
@@ -128,25 +96,18 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
    *   The module handler.
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
    *   The cache backend to use.
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
    *   The string translationManager.
    */
-  public function __construct(\Traversable $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager, TranslationInterface $translation_manager) {
-    // Allow the plugin definition to be altered by hook_entity_type_alter().
+  public function __construct(\Traversable $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager) {
+    parent::__construct('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
+
+    $this->setCacheBackend($cache, $language_manager, 'entity_type:', array('entity_types' => TRUE));
+    $this->alterInfo($module_handler, 'entity_type');
 
-    $this->moduleHandler = $module_handler;
-    $this->cache = $cache;
-    $this->languageManager = $language_manager;
-    $this->namespaces = $namespaces;
     $this->translationManager = $translation_manager;
-
-    $this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
-    $this->discovery = new InfoHookDecorator($this->discovery, 'entity_type_build');
-    $this->discovery = new AlterDecorator($this->discovery, 'entity_type');
-    $this->discovery = new CacheDecorator($this->discovery, 'entity_type:' . $this->languageManager->getCurrentLanguage()->id, 'cache', Cache::PERMANENT, array('entity_types' => TRUE));
-
     $this->container = $container;
   }
 
@@ -162,6 +123,24 @@ public function clearCachedDefinitions() {
   /**
    * {@inheritdoc}
    */
+  protected function findDefinitions() {
+    $definitions = $this->discovery->getDefinitions();
+    foreach ($definitions as $plugin_id => &$definition) {
+      $this->processDefinition($definition, $plugin_id);
+    }
+    foreach ($this->moduleHandler->getImplementations('entity_type_build') as $module) {
+      $function = $module . '_' . 'entity_type_build';
+      $function($definitions);
+    }
+    if ($this->alterHook) {
+      $this->moduleHandler->alter($this->alterHook, $definitions);
+    }
+    return $definitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getDefinition($entity_type_id, $exception_on_invalid = FALSE) {
     if (($entity_type = parent::getDefinition($entity_type_id)) && class_exists($entity_type->getClass())) {
       return $entity_type;
@@ -300,7 +279,7 @@ public function getFieldDefinitions($entity_type_id, $bundle = NULL) {
     if (!isset($this->entityFieldInfo[$entity_type_id])) {
       // First, try to load from cache.
       $cid = 'entity_field_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->id;
-      if ($cache = $this->cache->get($cid)) {
+      if ($cache = $this->cacheBackend->get($cid)) {
         $this->entityFieldInfo[$entity_type_id] = $cache->data;
       }
       else {
@@ -352,7 +331,7 @@ public function getFieldDefinitions($entity_type_id, $bundle = NULL) {
           }
         }
 
-        $this->cache->set($cid, $this->entityFieldInfo[$entity_type_id], Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
+        $this->cacheBackend->set($cid, $this->entityFieldInfo[$entity_type_id], Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE));
       }
     }
 
@@ -402,7 +381,7 @@ public function getBundleInfo($entity_type) {
   public function getAllBundleInfo() {
     if (!isset($this->bundleInfo)) {
       $langcode = $this->languageManager->getCurrentLanguage()->id;
-      if ($cache = $this->cache->get("entity_bundle_info:$langcode")) {
+      if ($cache = $this->cacheBackend->get("entity_bundle_info:$langcode")) {
         $this->bundleInfo = $cache->data;
       }
       else {
@@ -414,7 +393,7 @@ public function getAllBundleInfo() {
           }
         }
         $this->moduleHandler->alter('entity_bundle_info', $this->bundleInfo);
-        $this->cache->set("entity_bundle_info:$langcode", $this->bundleInfo, Cache::PERMANENT, array('entity_types' => TRUE));
+        $this->cacheBackend->set("entity_bundle_info:$langcode", $this->bundleInfo, Cache::PERMANENT, array('entity_types' => TRUE));
       }
     }
 
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/ContainerDerivativeDiscoveryDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/ContainerDerivativeDiscoveryDecorator.php
index eed640c..b01d1ac 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/ContainerDerivativeDiscoveryDecorator.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/ContainerDerivativeDiscoveryDecorator.php
@@ -14,7 +14,7 @@ class ContainerDerivativeDiscoveryDecorator extends DerivativeDiscoveryDecorator
   /**
    * {@inheritdoc}
    */
-  protected function getDerivativeFetcher($base_plugin_id, array $base_definition) {
+  protected function getDerivativeFetcher($base_plugin_id, $base_definition) {
     if (!isset($this->derivativeFetchers[$base_plugin_id])) {
       $this->derivativeFetchers[$base_plugin_id] = FALSE;
       $class = $this->getDerivativeClass($base_definition);
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
index 60622f0..5dc1e07 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
@@ -111,12 +111,13 @@ protected function setUp() {
 
     $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
 
-    $this->languageManager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $this->languageManager->expects($this->any())
       ->method('getCurrentLanguage')
       ->will($this->returnValue((object) array('id' => 'en')));
+    $this->languageManager->expects($this->any())
+      ->method('getLanguages')
+      ->will($this->returnValue(array('en' => (object) array('id' => 'en'))));
 
     $this->translationManager = $this->getStringTranslationStub();
 
@@ -124,7 +125,7 @@ protected function setUp() {
 
     $this->container = $this->getContainerWithCacheBins($this->cache);
 
-    $this->discovery = $this->getMock('Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface');
+    $this->discovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface');
   }
 
   /**
@@ -161,8 +162,9 @@ protected function setUpEntityManager($definitions = array()) {
    */
   public function testClearCachedDefinitions() {
     $this->setUpEntityManager();
-    $this->discovery->expects($this->once())
-      ->method('clearCachedDefinitions');
+    $this->cache->expects($this->once())
+      ->method('deleteTags')
+      ->with(array('entity_types' => TRUE));
 
     $this->entityManager->clearCachedDefinitions();
   }
@@ -474,21 +476,19 @@ public function testGetFieldDefinitionsWithCaching() {
 
     $expected = array('id' => $field_definition);
 
-    // @todo Investigate why this is 0 and 2, not 0/1 or 1/2.
-    $this->cache->expects($this->at(0))
+    $get_method = $this->cache->expects($this->any())
       ->method('get')
-      ->with('entity_field_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(2))
-      ->method('get')
-      ->with('entity_field_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue((object) array('data' => array('definitions' => $expected))));
 
-    $this->cache->expects($this->once())
+    $this->cache->expects($this->exactly(2))
       ->method('set');
 
     $this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type'));
     $this->entityManager->testClearEntityFieldInfo();
+
+    $get_method
+      ->will($this->returnValue((object) array('data' => array('definitions' => $expected))));
+
     $this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type'));
   }
 
@@ -500,12 +500,11 @@ public function testGetFieldDefinitionsWithCaching() {
   public function testGetFieldDefinitionsWithBundleMap() {
     $field_definition = $this->setUpEntityWithFieldDefinition(TRUE);
 
-    $this->moduleHandler->expects($this->at(0))
+    $this->moduleHandler->expects($this->exactly(2))
       ->method('invokeAll')
-      ->will($this->returnValue(array()));
-    $this->moduleHandler->expects($this->at(1))
-      ->method('invokeAll')
-      ->will($this->returnValue(array(
+      ->will($this->returnValueMap(array(
+        array('test_entity_type_field_info', array(), array()),
+        array('entity_field_info', array('test_entity_type'), array(
           'bundle map' => array(
             'test_entity_bundle' => array(
               'custom_field',
@@ -514,7 +513,7 @@ public function testGetFieldDefinitionsWithBundleMap() {
           'optional' => array(
             'custom_field' => $field_definition,
           ),
-        )));
+        )))));
 
     $expected = array('id' => $field_definition);
     $this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type'));
@@ -589,7 +588,7 @@ protected function setUpEntityWithFieldDefinition($custom_invoke_all = FALSE, $f
         $field_definition_id => $field_definition,
       )));
 
-    $this->moduleHandler->expects($this->once())
+    $this->moduleHandler->expects($this->exactly(2))
       ->method('alter');
     if (!$custom_invoke_all) {
       $this->moduleHandler->expects($this->exactly(2))
@@ -681,13 +680,11 @@ public function testGetAllBundleInfo() {
       'apple' => $apple,
       'banana' => $banana,
     ));
-    $this->cache->expects($this->at(1))
+    $get_method = $this->cache->expects($this->any())
       ->method('get')
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(2))
-      ->method('get')
-      ->will($this->returnValue((object) array('data' => 'cached data')));
-    $this->cache->expects($this->once())
+
+    $this->cache->expects($this->exactly(2))
       ->method('set');
 
     $expected = array(
@@ -710,6 +707,12 @@ public function testGetAllBundleInfo() {
 
     $this->entityManager->clearCachedDefinitions();
 
+    $get_method
+      ->will($this->returnValueMap(array(
+        array('entity_types:en', FALSE, FALSE),
+        array('entity_bundle_info:en', FALSE, (object) array('data' => 'cached data')),
+      )));
+
     $bundle_info = $this->entityManager->getAllBundleInfo();
     $this->assertSame('cached data', $bundle_info);
   }
