diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index e9fb855..24d2b51 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -11,232 +11,72 @@
  */
 
 /**
- * Inform the base system and the Field API about one or more entity types.
- *
- * Inform the system about one or more entity types (i.e., object types that
- * can be loaded via entity_load() and, optionally, to which fields can be
- * attached).
- *
- * @return
- *   An array whose keys are entity type names and whose values identify
- *   properties of those types that the system needs to know about:
- *   - label: The human-readable name of the type.
- *   - entity class: The name of the entity class, defaults to
- *     Drupal\Core\Entity\Entity. The entity class must implement EntityInterface.
- *   - controller class: The name of the class that is used to load the objects.
- *     The class has to implement the
- *     Drupal\Core\Entity\EntityStorageControllerInterface interface. Leave blank
- *     to use the Drupal\Core\Entity\DatabaseStorageController implementation.
- *   - render controller class: The name of the class that is used to render
- *     the entities. Deafaults to Drupal\Core\Entity\EntityRenderController.
- *   - form controller class: An associative array where the keys are the names
- *     of the different form operations (such as creation, editing or deletion)
- *     and the values are the names of the controller classes. To facilitate
- *     supporting the case where an entity form varies only slightly between
- *     different operations, the name of the operation is passed also to the
- *     constructor of the form controller class. This way, one class can be used
- *     for multiple entity forms.
- *   - list controller class: The name of the class that is used to provide
- *     listings of the entity. The class must implement
- *     Drupal\Core\Entity\EntityListControllerInterface. Defaults to
- *     Drupal\Core\Entity\EntityListController.
- *   - base table: (used by Drupal\Core\Entity\DatabaseStorageController) The
- *     name of the entity type's base table.
- *   - static cache: (used by Drupal\Core\Entity\DatabaseStorageController)
- *     FALSE to disable static caching of entities during a page request.
- *     Defaults to TRUE.
- *   - field cache: (used by Field API loading and saving of field data) FALSE
- *     to disable Field API's persistent cache of field data. Only recommended
- *     if a higher level persistent cache is available for the entity type.
- *     Defaults to TRUE.
- *   - uri callback: A function taking an entity as argument and returning the
- *     URI elements of the entity, e.g. 'path' and 'options'. The actual entity
- *     URI can be constructed by passing these elements to url().
- *   - label callback: (optional) A function taking an entity and optional langcode
- *     argument, and returning the label of the entity. If langcode is omitted, the
- *     entity's default language is used.
- *
- *     The entity label is the main string associated with an entity; for
- *     example, the title of a node or the subject of a comment. If there is an
- *     entity object property that defines the label, use the 'label' element
- *     of the 'entity keys' return value component to provide this information
- *     (see below). If more complex logic is needed to determine the label of
- *     an entity, you can instead specify a callback function here, which will
- *     be called to determine the entity label. See also the
- *     Drupal\Core\Entity\Entity::label() method, which implements this logic.
- *   - fieldable: Set to TRUE if you want your entity type to be fieldable.
- *   - translation: An associative array of modules registered as field
- *     translation handlers. Array keys are the module names, array values
- *     can be any data structure the module uses to provide field translation.
- *     Any empty value disallows the module to appear as a translation handler.
- *   - entity keys: An array describing how the Field API can extract the
- *     information it needs from the objects of the type. Elements:
- *     - id: The name of the property that contains the primary id of the
- *       entity. Every entity object passed to the Field API must have this
- *       property and its value must be numeric.
- *     - revision: The name of the property that contains the revision id of
- *       the entity. The Field API assumes that all revision ids are unique
- *       across all entities of a type. This entry can be omitted if the
- *       entities of this type are not versionable.
- *     - bundle: The name of the property that contains the bundle name for the
- *       entity. The bundle name defines which set of fields are attached to
- *       the entity (e.g. what nodes call "content type"). This entry can be
- *       omitted if this entity type exposes a single bundle (all entities have
- *       the same collection of fields). The name of this single bundle will be
- *       the same as the entity type.
- *     - label: The name of the property that contains the entity label. For
- *       example, if the entity's label is located in $entity->subject, then
- *       'subject' should be specified here. If complex logic is required to
- *       build the label, a 'label callback' should be defined instead (see
- *       the 'label callback' section above for details).
- *     - uuid (optional): The name of the property that contains the universally
- *       unique identifier of the entity, which is used to distinctly identify
- *       an entity across different systems.
- *   - bundle keys: An array describing how the Field API can extract the
- *     information it needs from the bundle objects for this type (e.g
- *     $vocabulary objects for terms; not applicable for nodes). This entry can
- *     be omitted if this type's bundles do not exist as standalone objects.
- *     Elements:
- *     - bundle: The name of the property that contains the name of the bundle
- *       object.
- *   - bundles: An array describing all bundles for this object type. Keys are
- *     bundles machine names, as found in the objects' 'bundle' property
- *     (defined in the 'entity keys' entry above). Elements:
- *     - label: The human-readable name of the bundle.
- *     - uri callback: Same as the 'uri callback' key documented above for the
- *       entity type, but for the bundle only. When determining the URI of an
- *       entity, if a 'uri callback' is defined for both the entity type and
- *       the bundle, the one for the bundle is used.
- *     - admin: An array of information that allows Field UI pages to attach
- *       themselves to the existing administration pages for the bundle.
- *       Elements:
- *       - path: the path of the bundle's main administration page, as defined
- *         in hook_menu(). If the path includes a placeholder for the bundle,
- *         the 'bundle argument', 'bundle helper' and 'real path' keys below
- *         are required.
- *       - bundle argument: The position of the placeholder in 'path', if any.
- *       - real path: The actual path (no placeholder) of the bundle's main
- *         administration page. This will be used to generate links.
- *       - access callback: As in hook_menu(). 'user_access' will be assumed if
- *         no value is provided.
- *       - access arguments: As in hook_menu().
- *   - view modes: An array describing the view modes for the entity type. View
- *     modes let entities be displayed differently depending on the context.
- *     For instance, a node can be displayed differently on its own page
- *     ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
- *     in an RSS feed ('rss' mode). Modules taking part in the display of the
- *     entity (notably the Field API) can adjust their behavior depending on
- *     the requested view mode. An additional 'default' view mode is available
- *     for all entity types. This view mode is not intended for actual entity
- *     display, but holds default display settings. For each available view
- *     mode, administrators can configure whether it should use its own set of
- *     field display settings, or just replicate the settings of the 'default'
- *     view mode, thus reducing the amount of display configurations to keep
- *     track of. Keys of the array are view mode names. Each view mode is
- *     described by an array with the following key/value pairs:
- *     - label: The human-readable name of the view mode
- *     - custom settings: A boolean specifying whether the view mode should by
- *       default use its own custom field display settings. If FALSE, entities
- *       displayed in this view mode will reuse the 'default' display settings
- *       by default (e.g. right after the module exposing the view mode is
- *       enabled), but administrators can later use the Field UI to apply custom
- *       display settings specific to the view mode.
- *
- * @see entity_load()
- * @see entity_load_multiple()
- * @see hook_entity_info_alter()
- */
-function hook_entity_info() {
-  $return = array(
-    'node' => array(
-      'label' => t('Node'),
-      'entity class' => 'Drupal\node\Node',
-      'controller class' => 'Drupal\node\NodeStorageController',
-      'form controller class' => array(
-        'default' => 'Drupal\node\NodeFormController',
-      ),
-      'base table' => 'node',
-      'revision table' => 'node_revision',
-      'uri callback' => 'node_uri',
-      'fieldable' => TRUE,
-      'translation' => array(
-        'locale' => TRUE,
-      ),
-      'entity keys' => array(
-        'id' => 'nid',
-        'revision' => 'vid',
-        'bundle' => 'type',
-        'uuid' => 'uuid',
-      ),
-      'bundle keys' => array(
-        'bundle' => 'type',
-      ),
-      'bundles' => array(),
-      'view modes' => array(
-        'full' => array(
-          'label' => t('Full content'),
-          'custom settings' => FALSE,
-        ),
-        'teaser' => array(
-          'label' => t('Teaser'),
-          'custom settings' => TRUE,
-        ),
-        'rss' => array(
-          'label' => t('RSS'),
-          'custom settings' => FALSE,
-        ),
-      ),
-    ),
-  );
-
-  // Search integration is provided by node.module, so search-related
-  // view modes for nodes are defined here and not in search.module.
-  if (module_exists('search')) {
-    $return['node']['view modes'] += array(
-      'search_index' => array(
-        'label' => t('Search index'),
-        'custom settings' => FALSE,
-      ),
-      'search_result' => array(
-        'label' => t('Search result'),
-        'custom settings' => FALSE,
-      ),
-    );
-  }
-
-  // Bundles must provide a human readable name so we can create help and error
-  // messages, and the path to attach Field admin pages to.
-  foreach (node_type_get_names() as $type => $name) {
-    $return['node']['bundles'][$type] = array(
-      'label' => $name,
-      'admin' => array(
-        'path' => 'admin/structure/types/manage/%node_type',
-        'real path' => 'admin/structure/types/manage/' . $type,
-        'bundle argument' => 4,
-        'access arguments' => array('administer content types'),
-      ),
-    );
-  }
-
-  return $return;
-}
-
-/**
- * Alter the entity info.
+ * Alter the entity type definition.
  *
  * Modules may implement this hook to alter the information that defines an
- * entity. All properties that are available in hook_entity_info() can be
- * altered here.
- *
- * @param $entity_info
- *   The entity info array, keyed by entity name.
- *
- * @see hook_entity_info()
+ * entity. All properties that are available in
+ * \Drupal\Core\Entity\EntityManager can be altered here.
+ *
+ * In addition, the following properties should be added here:
+ * - bundles: An array describing all bundles for this object type. Keys are
+ *   bundle machine names, as found in the objects' 'bundle' property
+ *   (defined in the 'entity_keys' entry for the entity type in the
+ *   EntityManager). Elements:
+ *   - label: The human-readable name of the bundle.
+ *   - uri_callback: The same as the 'uri_callback' key defined for the entity
+ *     type in the EntityManager, but for the bundle only. When determining
+ *     the URI of an entity, if a 'uri_callback' is defined for both the
+ *     entity type and the bundle, the one for the bundle is used.
+ *   - admin: An array of information that allows Field UI pages to attach
+ *     themselves to the existing administration pages for the bundle.
+ *     Elements:
+ *     - path: the path of the bundle's main administration page, as defined
+ *       in hook_menu(). If the path includes a placeholder for the bundle,
+ *       the 'bundle argument', 'bundle helper' and 'real path' keys below
+ *       are required.
+ *     - bundle argument: The position of the placeholder in 'path', if any.
+ *     - real path: The actual path (no placeholder) of the bundle's main
+ *       administration page. This will be used to generate links.
+ *     - access callback: As in hook_menu(). 'user_access' will be assumed if
+ *       no value is provided.
+ *     - access arguments: As in hook_menu().
+ * - view_modes: An array describing the view modes for the entity type. View
+ *   modes let entities be displayed differently depending on the context.
+ *   For instance, a node can be displayed differently on its own page
+ *   ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
+ *   in an RSS feed ('rss' mode). Modules taking part in the display of the
+ *   entity (notably the Field API) can adjust their behavior depending on
+ *   the requested view mode. An additional 'default' view mode is available
+ *   for all entity types. This view mode is not intended for actual entity
+ *   display, but holds default display settings. For each available view
+ *   mode, administrators can configure whether it should use its own set of
+ *   field display settings, or just replicate the settings of the 'default'
+ *   view mode, thus reducing the amount of display configurations to keep
+ *   track of. Keys of the array are view mode names. Each view mode is
+ *   described by an array with the following key/value pairs:
+ *   - label: The human-readable name of the view mode.
+ *   - custom settings: A boolean specifying whether the view mode should by
+ *     default use its own custom field display settings. If FALSE, entities
+ *     displayed in this view mode will reuse the 'default' display settings
+ *     by default (e.g. right after the module exposing the view mode is
+ *     enabled), but administrators can later use the Field UI to apply custom
+ *     display settings specific to the view mode.
+ *
+ * @param array $entity_info
+ *   An associative array of all entity type definitions, keyed by the entity
+ *   type name.
+ *
+ * @see \Drupal\Core\Entity\Entity
+ * @see \Drupal\Core\Entity\EntityManager
+ * @see entity_get_info()
+ *
+ * @todo Allow a module to add its bundles and view modes before other modules
+ *   alter the definition.
  */
 function hook_entity_info_alter(&$entity_info) {
   // Set the controller class for nodes to an alternate implementation of the
   // Drupal\Core\Entity\EntityStorageControllerInterface interface.
-  $entity_info['node']['controller class'] = 'Drupal\mymodule\MyCustomNodeStorageController';
+  $entity_info['node']['controller_class'] = 'Drupal\mymodule\MyCustomNodeStorageController';
 }
 
 /**
diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 48ee728..d221d4a 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -13,18 +13,21 @@
 use Drupal\Core\Entity\EntityInterface;
 
 /**
- * Gets the entity info array of an entity type.
+ * Gets the entity definition for an entity type.
  *
- * @param $entity_type
- *   The entity type, e.g. node, for which the info shall be returned, or NULL
- *   to return an array with info about all types.
+ * @param string|null $entity_type
+ *   (optional) The entity type (e.g. 'node'). Leave NULL to retrieve
+ *   information for all entity types.
+ *
+ * @return array
+ *   An array containing the entity type's definition, as retrieved with
+ *   \Drupal\Core\Entity\EntityManager. If $entity_type is NULL, an associative
+ *   array of all entity type definitions keyed by entity type is returned.
  *
- * @see hook_entity_info()
+ * @see \Drupal\Core\Entity\EntityManager
  * @see hook_entity_info_alter()
  */
 function entity_get_info($entity_type = NULL) {
-  $language_interface = language(LANGUAGE_TYPE_INTERFACE);
-
   // Use the advanced drupal_static() pattern, since this is called very often.
   static $drupal_static_fast;
   if (!isset($drupal_static_fast)) {
@@ -32,61 +35,8 @@ function entity_get_info($entity_type = NULL) {
   }
   $entity_info = &$drupal_static_fast['entity_info'];
 
-  // hook_entity_info() includes translated strings, so each language is cached
-  // separately.
-  $langcode = $language_interface->langcode;
-
   if (empty($entity_info)) {
-    if ($cache = cache()->get("entity_info:$langcode")) {
-      $entity_info = $cache->data;
-    }
-    else {
-      $entity_info = module_invoke_all('entity_info');
-      // Merge in default values.
-      foreach ($entity_info as $name => $data) {
-        $entity_info[$name] += array(
-          'fieldable' => FALSE,
-          'entity class' => 'Drupal\Core\Entity\Entity',
-          'controller class' => 'Drupal\Core\Entity\DatabaseStorageController',
-          'list controller class' => 'Drupal\Core\Entity\EntityListController',
-          'render controller class' => 'Drupal\Core\Entity\EntityRenderController',
-          'form controller class' => array(
-            'default' => 'Drupal\Core\Entity\EntityFormController',
-          ),
-          'static cache' => TRUE,
-          'field cache' => TRUE,
-          'bundles' => array(),
-          'view modes' => array(),
-          'entity keys' => array(),
-          'translation' => array(),
-        );
-        $entity_info[$name]['entity keys'] += array(
-          'revision' => '',
-          'bundle' => '',
-        );
-        foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) {
-          $entity_info[$name]['view modes'][$view_mode] += array(
-            'custom settings' => FALSE,
-          );
-        }
-        // If no bundle key is provided, assume a single bundle, named after
-        // the entity type.
-        if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
-          $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label']));
-        }
-        // Prepare entity schema fields SQL info for
-        // Drupal\Core\Entity\DatabaseStorageControllerInterface::buildQuery().
-        if (isset($entity_info[$name]['base table'])) {
-          $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
-          if (isset($entity_info[$name]['revision table'])) {
-            $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);
-          }
-        }
-      }
-      // Let other modules alter the entity info.
-      drupal_alter('entity_info', $entity_info);
-      cache()->set("entity_info:$langcode", $entity_info, CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
-    }
+    $entity_info = drupal_container()->get('plugin.manager.entity')->getDefinitions();
   }
 
   if (empty($entity_type)) {
@@ -119,7 +69,7 @@ function entity_info_cache_clear() {
  * @return Drupal\Core\Entity\EntityInterface
  *   The entity object, or FALSE if there is no entity with the given id.
  *
- * @see hook_entity_info()
+ * @see \Drupal\Core\Entity\EntityManager
  * @see entity_load_multiple()
  * @see Drupal\Core\Entity\EntityStorageControllerInterface
  * @see Drupal\Core\Entity\DatabaseStorageController
@@ -142,7 +92,7 @@ function entity_load($entity_type, $id, $reset = FALSE) {
  *   The entity object, or FALSE if there is no entity with the given revision
  *   id.
  *
- * @see hook_entity_info()
+ * @see \Drupal\Core\Entity\EntityManager
  * @see Drupal\Core\Entity\EntityStorageControllerInterface
  * @see Drupal\Core\Entity\DatabaseStorageController
  */
@@ -180,14 +130,14 @@ function entity_revision_delete($entity_type, $revision_id) {
  * @throws Drupal\Core\Entity\EntityStorageException
  *   Thrown in case the requested entity type does not support UUIDs.
  *
- * @see hook_entity_info()
+ * @see \Drupal\Core\Entity\EntityManager
  */
 function entity_load_by_uuid($entity_type, $uuid, $reset = FALSE) {
   $entity_info = entity_get_info($entity_type);
-  if (empty($entity_info['entity keys']['uuid'])) {
+  if (empty($entity_info['entity_keys']['uuid'])) {
     throw new EntityStorageException("Entity type $entity_type does not support UUIDs.");
   }
-  $uuid_key = $entity_info['entity keys']['uuid'];
+  $uuid_key = $entity_info['entity_keys']['uuid'];
 
   $controller = entity_get_controller($entity_type);
   if ($reset) {
@@ -208,11 +158,12 @@ function entity_load_by_uuid($entity_type, $uuid, $reset = FALSE) {
  * Drupal\Core\Entity\EntityStorageControllerInterface interface. By default,
  * Drupal\Core\Entity\DatabaseStorageController is used. Entity types can
  * specify that a different class should be used by setting the
- * 'controller class' key in hook_entity_info(). These classes can either
- * implement the Drupal\Core\Entity\EntityStorageControllerInterface interface, or,
- * most commonly, extend the Drupal\Core\Entity\DatabaseStorageController
- * class. See node_entity_info() and the NodeStorageController in node.module as
- * an example.
+ * 'controller_class' key in the entity plugin annotation. These classes can
+ * either implement the Drupal\Core\Entity\EntityStorageControllerInterface
+ * interface, or, most commonly, extend the
+ * Drupal\Core\Entity\DatabaseStorageController class.
+ * See Drupal\node\Plugin\Core\Entity\Node and Drupal\node\NodeStorageController
+ * for an example.
  *
  * @param string $entity_type
  *   The entity type to load, e.g. node or user.
@@ -224,7 +175,7 @@ function entity_load_by_uuid($entity_type, $uuid, $reset = FALSE) {
  * @return array
  *   An array of entity objects indexed by their ids.
  *
- * @see hook_entity_info()
+ * @see \Drupal\Core\Entity\EntityManager
  * @see Drupal\Core\Entity\EntityStorageControllerInterface
  * @see Drupal\Core\Entity\DatabaseStorageController
  * @see Drupal\Core\Entity\EntityFieldQuery
@@ -311,7 +262,7 @@ function entity_get_controller($entity_type) {
   $controllers = &drupal_static(__FUNCTION__, array());
   if (!isset($controllers[$entity_type])) {
     $type_info = entity_get_info($entity_type);
-    $class = $type_info['controller class'];
+    $class = $type_info['controller_class'];
     $controllers[$entity_type] = new $class($entity_type);
   }
   return $controllers[$entity_type];
@@ -348,7 +299,7 @@ function entity_page_label(EntityInterface $entity, $langcode = NULL) {
  * used. If a non-existing non-default operation is specified an exception will
  * be thrown.
  *
- * @see hook_entity_info()
+ * @see \Drupal\Core\Entity\EntityManager
  *
  * @param $entity_type
  *   The type of the entity.
@@ -364,11 +315,11 @@ function entity_form_controller($entity_type, $operation = 'default') {
   $info = entity_get_info($entity_type);
 
   // Check whether there is a form controller class for the specified operation.
-  if (!empty($info['form controller class'][$operation])) {
-    $class = $info['form controller class'][$operation];
+  if (!empty($info['form_controller_class'][$operation])) {
+    $class = $info['form_controller_class'][$operation];
   }
   // If no controller is specified default to the base implementation.
-  elseif (empty($info['form controller class']) && $operation == 'default') {
+  elseif (empty($info['form_controller_class']) && $operation == 'default') {
     $class = 'Drupal\Core\Entity\EntityFormController';
   }
   // If a non-existing operation has been specified stop.
@@ -514,12 +465,12 @@ function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_st
  * @return Drupal\Core\Entity\EntityListControllerInterface
  *   An entity list controller.
  *
- * @see hook_entity_info()
+ * @see \Drupal\Core\Entity\EntityManager
  */
 function entity_list_controller($entity_type) {
   $storage = entity_get_controller($entity_type);
   $entity_info = entity_get_info($entity_type);
-  $class = $entity_info['list controller class'];
+  $class = $entity_info['list_controller_class'];
   return new $class($entity_type, $storage);
 }
 
@@ -532,11 +483,11 @@ function entity_list_controller($entity_type) {
  * @return Drupal\Core\Entity\EntityRenderControllerInterface
  *   An entity render controller.
  *
- * @see hook_entity_info()
+ * @see \Drupal\Core\Entity\EntityManager
  */
 function entity_render_controller($entity_type) {
   $info = entity_get_info($entity_type);
-  $class = $info['render controller class'];
+  $class = $info['render_controller_class'];
   return new $class($entity_type);
 }
 
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index 07849c6..87bb20a 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -64,7 +64,7 @@ public function __construct($entityType) {
     $this->entityType = $entityType;
     $this->entityInfo = entity_get_info($entityType);
     $this->hookLoadArguments = array();
-    $this->idKey = $this->entityInfo['entity keys']['id'];
+    $this->idKey = $this->entityInfo['entity_keys']['id'];
   }
 
   /**
@@ -157,8 +157,8 @@ public function loadByProperties(array $values = array()) {
    *   A SelectQuery object for loading the entity.
    */
   protected function buildQuery($ids, $revision_id = FALSE) {
-    $config_class = $this->entityInfo['entity class'];
-    $prefix = $this->entityInfo['config prefix'] . '.';
+    $config_class = $this->entityInfo['class'];
+    $prefix = $this->entityInfo['config_prefix'] . '.';
 
     // Load all of the configuration entities.
     if ($ids === NULL) {
@@ -219,7 +219,7 @@ protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
    * Implements Drupal\Core\Entity\EntityStorageControllerInterface::create().
    */
   public function create(array $values) {
-    $class = isset($this->entityInfo['entity class']) ? $this->entityInfo['entity class'] : 'Drupal\Core\Entity\Entity';
+    $class = $this->entityInfo['class'];
 
     $entity = new $class($values, $this->entityType);
 
@@ -248,7 +248,7 @@ public function delete($ids) {
     }
 
     foreach ($entities as $id => $entity) {
-      $config = config($this->entityInfo['config prefix'] . '.' . $entity->id());
+      $config = config($this->entityInfo['config_prefix'] . '.' . $entity->id());
       $config->delete();
     }
 
@@ -262,7 +262,7 @@ public function delete($ids) {
    * Implements Drupal\Core\Entity\EntityStorageControllerInterface::save().
    */
   public function save(EntityInterface $entity) {
-    $prefix = $this->entityInfo['config prefix'] . '.';
+    $prefix = $this->entityInfo['config_prefix'] . '.';
 
     // Load the stored entity, if any.
     if ($entity->getOriginalID()) {
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 017aa01..3c5ec62 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -70,6 +70,8 @@ public function build(ContainerBuilder $container) {
       ->addArgument(new Reference('router.dumper'))
       ->addArgument(new Reference('lock'));
 
+    $container->register('plugin.manager.entity', 'Drupal\Core\Entity\EntityManager');
+
     $container->register('matcher', 'Drupal\Core\Routing\ChainMatcher');
     $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher')
       ->addTag('chained_matcher');
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index 8b206ee..3f7bb42 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -116,27 +116,27 @@ public function __construct($entityType) {
     $this->entityInfo = entity_get_info($entityType);
     $this->entityCache = array();
     $this->hookLoadArguments = array();
-    $this->idKey = $this->entityInfo['entity keys']['id'];
+    $this->idKey = $this->entityInfo['entity_keys']['id'];
 
     // Check if the entity type supports UUIDs.
-    if (!empty($this->entityInfo['entity keys']['uuid'])) {
-      $this->uuidKey = $this->entityInfo['entity keys']['uuid'];
+    if (!empty($this->entityInfo['entity_keys']['uuid'])) {
+      $this->uuidKey = $this->entityInfo['entity_keys']['uuid'];
     }
     else {
       $this->uuidKey = FALSE;
     }
 
     // Check if the entity type supports revisions.
-    if (!empty($this->entityInfo['entity keys']['revision'])) {
-      $this->revisionKey = $this->entityInfo['entity keys']['revision'];
-      $this->revisionTable = $this->entityInfo['revision table'];
+    if (!empty($this->entityInfo['entity_keys']['revision'])) {
+      $this->revisionKey = $this->entityInfo['entity_keys']['revision'];
+      $this->revisionTable = $this->entityInfo['revision_table'];
     }
     else {
       $this->revisionKey = FALSE;
     }
 
     // Check if the entity type supports static caching of loaded entities.
-    $this->cache = !empty($this->entityInfo['static cache']);
+    $this->cache = !empty($this->entityInfo['static_cache']);
   }
 
   /**
@@ -182,11 +182,11 @@ public function load(array $ids = NULL) {
       // Build and execute the query.
       $query_result = $this->buildQuery($ids)->execute();
 
-      if (!empty($this->entityInfo['entity class'])) {
+      if (!empty($this->entityInfo['class'])) {
         // We provide the necessary arguments for PDO to create objects of the
         // specified entity class.
         // @see Drupal\Core\Entity\EntityInterface::__construct()
-        $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['entity class'], array(array(), $this->entityType));
+        $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
       }
       $queried_entities = $query_result->fetchAllAssoc($this->idKey);
     }
@@ -227,11 +227,11 @@ public function loadRevision($revision_id) {
     // Build and execute the query.
     $query_result = $this->buildQuery(array(), $revision_id)->execute();
 
-    if (!empty($this->entityInfo['entity class'])) {
+    if (!empty($this->entityInfo['class'])) {
       // We provide the necessary arguments for PDO to create objects of the
       // specified entity class.
       // @see Drupal\Core\Entity\EntityInterface::__construct()
-      $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['entity class'], array(array(), $this->entityType));
+      $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
     }
     $queried_entities = $query_result->fetchAllAssoc($this->idKey);
 
@@ -315,7 +315,7 @@ protected function buildPropertyQuery(EntityFieldQuery $entity_query, array $val
    *   A SelectQuery object for loading the entity.
    */
   protected function buildQuery($ids, $revision_id = FALSE) {
-    $query = db_select($this->entityInfo['base table'], 'base');
+    $query = db_select($this->entityInfo['base_table'], 'base');
 
     $query->addTag($this->entityType . '_load_multiple');
 
@@ -327,11 +327,11 @@ protected function buildQuery($ids, $revision_id = FALSE) {
     }
 
     // Add fields from the {entity} table.
-    $entity_fields = $this->entityInfo['schema_fields_sql']['base table'];
+    $entity_fields = $this->entityInfo['schema_fields_sql']['base_table'];
 
     if ($this->revisionKey) {
       // Add all fields from the {entity_revision} table.
-      $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision table']);
+      $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision_table']);
       // The id field is provided by entity, so remove it.
       unset($entity_revision_fields[$this->idKey]);
 
@@ -431,7 +431,7 @@ protected function cacheSet($entities) {
    * Implements Drupal\Core\Entity\EntityStorageControllerInterface::create().
    */
   public function create(array $values) {
-    $class = isset($this->entityInfo['entity class']) ? $this->entityInfo['entity class'] : 'Drupal\Core\Entity\Entity';
+    $class = $this->entityInfo['class'];
 
     $entity = new $class($values, $this->entityType);
 
@@ -462,7 +462,7 @@ public function delete($ids) {
       }
       $ids = array_keys($entities);
 
-      db_delete($this->entityInfo['base table'])
+      db_delete($this->entityInfo['base_table'])
         ->condition($this->idKey, $ids, 'IN')
         ->execute();
 
@@ -505,7 +505,7 @@ public function save(EntityInterface $entity) {
 
       if (!$entity->isNew()) {
         if ($entity->isDefaultRevision()) {
-          $return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey);
+          $return = drupal_write_record($this->entityInfo['base_table'], $entity, $this->idKey);
         }
         else {
           // @todo, should a different value be returned when saving an entity
@@ -520,7 +520,7 @@ public function save(EntityInterface $entity) {
         $this->invokeHook('update', $entity);
       }
       else {
-        $return = drupal_write_record($this->entityInfo['base table'], $entity);
+        $return = drupal_write_record($this->entityInfo['base_table'], $entity);
         if ($this->revisionKey) {
           $this->saveRevision($entity);
         }
@@ -568,7 +568,7 @@ protected function saveRevision(EntityInterface $entity) {
     if ($entity->isNewRevision()) {
       drupal_write_record($this->revisionTable, $record);
       if ($entity->isDefaultRevision()) {
-        db_update($this->entityInfo['base table'])
+        db_update($this->entityInfo['base_table'])
           ->fields(array($this->revisionKey => $record[$this->revisionKey]))
           ->condition($this->idKey, $entity->id())
           ->execute();
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
index 007d6d3..6f22d83 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
@@ -41,14 +41,14 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
    */
   public function __construct($entityType) {
     parent::__construct($entityType);
-    $this->bundleKey = !empty($this->entityInfo['entity keys']['bundle']) ? $this->entityInfo['entity keys']['bundle'] : FALSE;
-    $this->entityClass = $this->entityInfo['entity class'];
+    $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
+    $this->entityClass = $this->entityInfo['class'];
 
     // Work-a-round to let load() get stdClass storage records without having to
     // override it. We map storage records to entities in
     // DatabaseStorageControllerNG:: mapFromStorageRecords().
     // @todo: Remove this once this is moved in the main controller.
-    unset($this->entityInfo['entity class']);
+    unset($this->entityInfo['class']);
   }
 
   /**
@@ -181,13 +181,13 @@ public function save(EntityInterface $entity) {
       $entity->updateOriginalValues();
 
       if (!$entity->isNew()) {
-        $return = drupal_write_record($this->entityInfo['base table'], $record, $this->idKey);
+        $return = drupal_write_record($this->entityInfo['base_table'], $record, $this->idKey);
         $this->resetCache(array($entity->id()));
         $this->postSave($entity, TRUE);
         $this->invokeHook('update', $entity);
       }
       else {
-        $return = drupal_write_record($this->entityInfo['base table'], $record);
+        $return = drupal_write_record($this->entityInfo['base_table'], $record);
         // Reset general caches, but keep caches specific to certain entities.
         $this->resetCache(array());
 
@@ -234,7 +234,7 @@ protected function invokeHook($hook, EntityInterface $entity) {
    */
   protected function mapToStorageRecord(EntityInterface $entity) {
     $record = new \stdClass();
-    foreach ($this->entityInfo['schema_fields_sql']['base table'] as $name) {
+    foreach ($this->entityInfo['schema_fields_sql']['base_table'] as $name) {
       $record->$name = $entity->$name->value;
     }
     return $record;
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 8f462de..1d56590 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -99,7 +99,7 @@ public function isNew() {
    */
   public function isNewRevision() {
     $info = $this->entityInfo();
-    return $this->newRevision || (!empty($info['entity keys']['revision']) && !$this->getRevisionId());
+    return $this->newRevision || (!empty($info['entity_keys']['revision']) && !$this->getRevisionId());
   }
 
   /**
@@ -136,11 +136,11 @@ public function bundle() {
   public function label($langcode = NULL) {
     $label = NULL;
     $entity_info = $this->entityInfo();
-    if (isset($entity_info['label callback']) && function_exists($entity_info['label callback'])) {
-      $label = $entity_info['label callback']($this->entityType, $this, $langcode);
+    if (isset($entity_info['label_callback']) && function_exists($entity_info['label_callback'])) {
+      $label = $entity_info['label_callback']($this->entityType, $this, $langcode);
     }
-    elseif (!empty($entity_info['entity keys']['label']) && isset($this->{$entity_info['entity keys']['label']})) {
-      $label = $this->{$entity_info['entity keys']['label']};
+    elseif (!empty($entity_info['entity_keys']['label']) && isset($this->{$entity_info['entity_keys']['label']})) {
+      $label = $this->{$entity_info['entity_keys']['label']};
     }
     return $label;
   }
@@ -153,11 +153,11 @@ public function uri() {
     // A bundle-specific callback takes precedence over the generic one for the
     // entity type.
     $entity_info = $this->entityInfo();
-    if (isset($entity_info['bundles'][$bundle]['uri callback'])) {
-      $uri_callback = $entity_info['bundles'][$bundle]['uri callback'];
+    if (isset($entity_info['bundles'][$bundle]['uri_callback'])) {
+      $uri_callback = $entity_info['bundles'][$bundle]['uri_callback'];
     }
-    elseif (isset($entity_info['uri callback'])) {
-      $uri_callback = $entity_info['uri callback'];
+    elseif (isset($entity_info['uri_callback'])) {
+      $uri_callback = $entity_info['uri_callback'];
     }
     else {
       return NULL;
@@ -330,12 +330,12 @@ public function delete() {
   public function createDuplicate() {
     $duplicate = clone $this;
     $entity_info = $this->entityInfo();
-    $duplicate->{$entity_info['entity keys']['id']} = NULL;
+    $duplicate->{$entity_info['entity_keys']['id']} = NULL;
 
     // Check if the entity type supports UUIDs and generate a new one if so.
-    if (!empty($entity_info['entity keys']['uuid'])) {
+    if (!empty($entity_info['entity_keys']['uuid'])) {
       $uuid = new Uuid();
-      $duplicate->{$entity_info['entity keys']['uuid']} = $uuid->generate();
+      $duplicate->{$entity_info['entity_keys']['uuid']} = $uuid->generate();
     }
     return $duplicate;
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityFieldQuery.php b/core/lib/Drupal/Core/Entity/EntityFieldQuery.php
index 0c054ce..2bf73bd 100644
--- a/core/lib/Drupal/Core/Entity/EntityFieldQuery.php
+++ b/core/lib/Drupal/Core/Entity/EntityFieldQuery.php
@@ -888,14 +888,14 @@ protected function propertyQuery() {
 
     $entity_type = $this->entityConditions['entity_type']['value'];
     $entity_info = entity_get_info($entity_type);
-    if (empty($entity_info['base table'])) {
+    if (empty($entity_info['base_table'])) {
       throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type)));
     }
 
-    $base_table = $entity_info['base table'];
+    $base_table = $entity_info['base_table'];
     $select_query = db_select($base_table);
     $select_query->addExpression(':entity_type', 'entity_type', array(':entity_type' => $entity_type));
-    $sql_field = $entity_info['entity keys']['id'];
+    $sql_field = $entity_info['entity_keys']['id'];
 
     // If a data table is defined we need to join it and make sure that only one
     // record per entity is returned.
@@ -913,8 +913,8 @@ protected function propertyQuery() {
     }
 
     // If there is a revision key defined, use it.
-    if (!empty($entity_info['entity keys']['revision'])) {
-      $sql_field = $entity_info['entity keys']['revision'];
+    if (!empty($entity_info['entity_keys']['revision'])) {
+      $sql_field = $entity_info['entity_keys']['revision'];
       $select_query->addField($base_table, $sql_field, 'revision_id');
       if (isset($this->entityConditions['revision_id'])) {
         $this->addCondition($select_query, "$base_table.$sql_field", $this->entityConditions['revision_id']);
@@ -927,9 +927,9 @@ protected function propertyQuery() {
     $id_map['revision_id'] = $sql_field;
 
     // Handle bundles.
-    if (!empty($entity_info['entity keys']['bundle'])) {
+    if (!empty($entity_info['entity_keys']['bundle'])) {
       $base_table_schema = drupal_get_schema($base_table);
-      $sql_field = $entity_info['entity keys']['bundle'];
+      $sql_field = $entity_info['entity_keys']['bundle'];
       $having = FALSE;
       if (!empty($base_table_schema['fields'][$sql_field])) {
         $select_query->addField($base_table, $sql_field, 'bundle');
@@ -944,7 +944,7 @@ protected function propertyQuery() {
     $id_map['bundle'] = $sql_field;
 
     if (isset($this->entityConditions['bundle'])) {
-      if (!empty($entity_info['entity keys']['bundle'])) {
+      if (!empty($entity_info['entity_keys']['bundle'])) {
         $this->addCondition($select_query, "$base_table.$sql_field", $this->entityConditions['bundle'], $having);
       }
       else {
@@ -955,7 +955,7 @@ protected function propertyQuery() {
 
     foreach (array('uuid', 'langcode') as $key) {
       if (isset($this->entityConditions[$key])) {
-        $sql_field = !empty($entity_info['entity keys'][$key]) ? $entity_info['entity keys'][$key] : $key;
+        $sql_field = !empty($entity_info['entity_keys'][$key]) ? $entity_info['entity_keys'][$key] : $key;
         if (isset($base_table_schema[$sql_field])) {
           $this->addCondition($select_query, "$base_table.$sql_field", $this->entityConditions[$key]);
         }
@@ -1075,7 +1075,7 @@ public function addCondition(Select $select_query, $sql_field, $condition, $havi
    */
   public function addPropertyConditions(Select $select_query, $entity_type) {
     $entity_info = entity_get_info($entity_type);
-    $entity_base_table = $entity_info['base table'];
+    $entity_base_table = $entity_info['base_table'];
     list($data_table, $data_table_schema) = $this->getPropertyDataSchema($entity_type);
 
     foreach ($this->propertyConditions as $property_condition) {
@@ -1104,7 +1104,7 @@ public function addPropertyOrderBy(Select $select_query, $entity_type, array $or
     $entity_info = entity_get_info($entity_type);
     list($data_table, $data_table_schema) = $this->getPropertyDataSchema($entity_type);
     $specifier = $order['specifier'];
-    $table = !empty($data_table_schema['fields'][$specifier]) ? $data_table  . '_' . $order['langcode_group'] : $entity_info['base table'];
+    $table = !empty($data_table_schema['fields'][$specifier]) ? $data_table  . '_' . $order['langcode_group'] : $entity_info['base_table'];
     $select_query->orderBy("$table.$specifier", $order['direction']);
   }
 
@@ -1126,7 +1126,7 @@ public function joinPropertyData(Select $select_query, $entity_type, $base_table
     // If we have no data table there are no property meta conditions to handle.
     if (!empty($data_table)) {
       $entity_info = entity_get_info($entity_type);
-      $id_key = $entity_info['entity keys']['id'];
+      $id_key = $entity_info['entity_keys']['id'];
       $base_id_key = !empty($base_id_key) ? $base_id_key : $id_key;
 
       foreach ($this->properties as $key => $property) {
@@ -1154,8 +1154,8 @@ public function joinPropertyData(Select $select_query, $entity_type, $base_table
   protected function getPropertyDataSchema($entity_type) {
     $entity_info = entity_get_info($entity_type);
 
-    if (!empty($entity_info['data table'])) {
-      $data_table = $entity_info['data table'];
+    if (!empty($entity_info['data_table'])) {
+      $data_table = $entity_info['data_table'];
       $data_table_schema = drupal_get_schema($data_table);
     }
     else {
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
new file mode 100644
index 0000000..57a9e0f
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -0,0 +1,246 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\EntityManager.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Core\Plugin\Discovery\AlterDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Cache\CacheBackendInterface;
+
+/**
+ * Manages entity type plugin definitions.
+ *
+ * Each entity type definition array is set in the entity type plugin's
+ * annotation and altered by hook_entity_info_alter(). The definition includes
+ * the following keys:
+ * - module: The name of the module providing the type.
+ * - class: The name of the entity type class. Defaults to
+ *   Drupal\Core\Entity\Entity.
+ * - base_table: The name of the entity type's base table. Used by
+ *   Drupal\Core\Entity\DatabaseStorageController.
+ * - controller_class: The name of the class that is used to load the objects.
+ *   The class must implement
+ *   Drupal\Core\Entity\EntityStorageControllerInterface. Defaults to
+ *   Drupal\Core\Entity\DatabaseStorageController.
+ * - fieldable: (optional) Boolean indicating whether fields can be attached
+ *   to entities of this type. Defaults to FALSE.
+ * - field_cache: (optional) Boolean indicating whether the Field API's
+ *   Field API's persistent cache of field data should be used. The persistent
+ *   cache should usually only be disabled if a higher level persistent cache
+ *   is available for the entity type. Defaults to TRUE.
+ * - form_controller_class: (optional) An associative array where the keys
+ *   are the names of the different form operations (such as 'create',
+ *   'edit', or 'delete') and the values are the names of the controller
+ *   classes for those operations. The name of the operation is passed also
+ *   to the form controller's constructor, so that one class can be used for
+ *   multiple entity forms when the forms are similar. Defaults to
+ *   Drupal\Core\Entity\EntityFormController.
+ * - label: The human-readable name of the type.
+ * - label_callback: (optional) A function taking an entity and optional
+ *   langcode argument, and returning the label of the entity. If langcode is
+ *   omitted, the entity's default language is used.
+ *   The entity label is the main string associated with an entity; for
+ *   example, the title of a node or the subject of a comment. If there is an
+ *   entity object property that defines the label, use the 'label' element
+ *   of the 'entity_keys' return value component to provide this information
+ *   (see below). If more complex logic is needed to determine the label of
+ *   an entity, you can instead specify a callback function here, which will
+ *   be called to determine the entity label. See also the
+ *   Drupal\Core\Entity\Entity::label() method, which implements this logic.
+ * - list_controller_class: (optional) The name of the class that provides
+ *   listings of the The class must implement
+ *   Drupal\Core\Entity\EntityListControllerInterface. Defaults to
+ *   Drupal\Core\Entity\EntityListController.
+ * - render_controller_class: The name of the class that is used to render the
+ *   entities. Defaults to Drupal\Core\Entity\EntityRenderController.
+ * - static_cache: (optional) Boolean indicating whether entities should be
+ *   statically cached during a page request. Used by
+ *   Drupal\Core\Entity\DatabaseStorageController. Defaults to TRUE.
+ * - translation: (optional) An associative array of modules registered as
+ *   field translation handlers. Array keys are the module names, and array
+ *   values can be any data structure the module uses to provide field
+ *   translation. If the value is empty, the module will not be used as a
+ *   translation handler.
+ * - entity_keys: An array describing how the Field API can extract certain
+ *   information from objects of this entity type. Elements:
+ *   - id: The name of the property that contains the primary ID of the
+ *     entity. Every entity object passed to the Field API must have this
+ *     property and its value must be numeric.
+ *   - revision: (optional) The name of the property that contains the
+ *     revision ID of the entity. The Field API assumes that all revision IDs
+ *     are unique across all entities of a type. This entry can be omitted if
+ *     the entities of this type are not versionable.
+ *   - bundle: (optional) The name of the property that contains the bundle
+ *     name for the entity. The bundle name defines which set of fields are
+ *     attached to the entity (e.g. what nodes call "content type"). This
+ *     entry can be omitted if this entity type exposes a single bundle (such
+ *     that all entities have the same collection of fields). The name of
+ *     this single bundle will be the same as the entity type.
+ *   - label: The name of the property that contains the entity label. For
+ *     example, if the entity's label is located in $entity->subject, then
+ *     'subject' should be specified here. If complex logic is required to
+ *     build the label, a 'label_callback' should be defined instead (see
+ *     the 'label_callback' section above for details).
+ *   - uuid (optional): The name of the property that contains the universally
+ *     unique identifier of the entity, which is used to distinctly identify
+ *     an entity across different systems.
+ * - bundle_keys: An array describing how the Field API can extract the
+ *   information it needs from the bundle objects for this type (e.g
+ *   Vocabulary objects for terms; not applicable for nodes). This entry can
+ *   be omitted if this type's bundles do not exist as standalone objects.
+ *   Elements:
+ *   - bundle: The name of the property that contains the name of the bundle
+ *     object.
+ *
+ * The defaults for the plugin definition are provided in
+ * \Drupal\Core\Entity\EntityManager::defaults.
+ *
+ * Additional keys may be added to the entity definition with
+ * hook_entity_info_alter(). In particular, lists of the entity type's bundles
+ * and view modes are added there. See hook_entity_info_alter() for more
+ * information.
+ *
+ * @see \Drupal\Core\Entity\Entity
+ * @see entity_get_info()
+ * @see hook_entity_info_alter()
+ *
+ * @todo Allow a module to add its bundles and view modes before other modules
+ *   alter the definition.
+ */
+class EntityManager extends PluginManagerBase {
+
+  /**
+   * The cache bin used for entity plugin definitions.
+   *
+   * @var string
+   */
+  protected $cacheBin = 'cache';
+
+  /**
+   * The cache key used for entity plugin definitions.
+   *
+   * @var string
+   */
+  protected $cacheKey = 'entity_info';
+
+  /**
+   * The cache expiration for entity plugin definitions.
+   *
+   * @var int
+   */
+  protected $cacheExpire = CacheBackendInterface::CACHE_PERMANENT;
+
+  /**
+   * The cache tags used for entity plugin definitions.
+   *
+   * @var array
+   */
+  protected $cacheTags = array('entity_info' => TRUE);
+
+  /**
+   * The default values for optional keys of the entity plugin definition.
+   *
+   * @var array
+   */
+  protected $defaults = array(
+    'class' => 'Drupal\Core\Entity\Entity',
+    'controller_class' => 'Drupal\Core\Entity\DatabaseStorageController',
+    'entity_keys' => array(
+      'revision' => '',
+      'bundle' => '',
+    ),
+    'fieldable' => FALSE,
+    'field_cache' => TRUE,
+    'form_controller_class' => array(
+      'default' => 'Drupal\Core\Entity\EntityFormController',
+    ),
+    'list_controller_class' => 'Drupal\Core\Entity\EntityListController',
+    'render_controller_class' => 'Drupal\Core\Entity\EntityRenderController',
+    'static_cache' => TRUE,
+    'translation' => array(),
+    // Bundles and view modes are added in hook_entity_info_alter(), but we
+    // provide empty lists of them by default.
+    'bundles' => array(),
+    'view_modes' => array(),
+  );
+
+  /**
+   * Constructs a new Entity plugin manager.
+   */
+  public function __construct() {
+    // Allow the plugin definition to be altered by hook_entity_info_alter().
+    $this->discovery = new AlterDecorator(new AnnotatedClassDiscovery('Core', 'Entity'), 'entity_info');
+    $this->factory = new DefaultFactory($this);
+
+    // Entity type plugins includes translated strings, so each language is
+    // cached separately.
+    $this->cacheKey .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
+  }
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginManagerBase::getDefinition().
+   */
+  public function getDefinition($plugin_id) {
+    $definitions = $this->getDefinitions();
+    return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
+  }
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginManagerBase::getDefinitions().
+   */
+  public function getDefinitions() {
+    // Because \Drupal\Core\Plugin\Discovery\CacheDecorator runs before
+    // definitions are processed and does not support cache tags, we perform our
+    // own caching.
+    if ($cache = cache($this->cacheBin)->get($this->cacheKey)) {
+      return $cache->data;
+    }
+    else {
+      // @todo Remove array_filter() once http://drupal.org/node/1780396 is
+      //   resolved.
+      $definitions = array_filter(parent::getDefinitions());
+      cache($this->cacheBin)->set($this->cacheKey, $definitions, $this->cacheExpire, $this->cacheTags);
+      return $definitions;
+    }
+  }
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginManagerBase::processDefinition().
+   */
+  protected function processDefinition(&$definition, $plugin_id) {
+    parent::processDefinition($definition, $plugin_id);
+
+    // @todo Remove this check once http://drupal.org/node/1780396 is resolved.
+    if (!module_exists($definition['module'])) {
+      $definition = NULL;
+      return;
+    }
+
+    foreach ($definition['view_modes'] as $view_mode => $view_mode_info) {
+      $definition['view_modes'][$view_mode] += array(
+        'custom settings' => FALSE,
+      );
+    }
+
+    // If no bundle key is provided, assume a single bundle, named after
+    // the entity type.
+    if (empty($definition['entity_keys']['bundle']) && empty($definition['bundles'])) {
+      $definition['bundles'] = array($plugin_id => array('label' => $definition['label']));
+    }
+    // Prepare entity schema fields SQL info for
+    // Drupal\Core\Entity\DatabaseStorageControllerInterface::buildQuery().
+    if (isset($definition['base_table'])) {
+      $definition['schema_fields_sql']['base_table'] = drupal_schema_fields_sql($definition['base_table']);
+      if (isset($definition['revision_table'])) {
+        $definition['schema_fields_sql']['revision_table'] = drupal_schema_fields_sql($definition['revision_table']);
+      }
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php
index 7520466..2fc7764 100644
--- a/core/lib/Drupal/Core/Entity/EntityNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityNG.php
@@ -398,12 +398,12 @@ public function __unset($name) {
   public function createDuplicate() {
     $duplicate = clone $this;
     $entity_info = $this->entityInfo();
-    $duplicate->{$entity_info['entity keys']['id']}->value = NULL;
+    $duplicate->{$entity_info['entity_keys']['id']}->value = NULL;
 
     // Check if the entity type supports UUIDs and generate a new one if so.
-    if (!empty($entity_info['entity keys']['uuid'])) {
+    if (!empty($entity_info['entity_keys']['uuid'])) {
       $uuid = new Uuid();
-      $duplicate->{$entity_info['entity keys']['uuid']}->value = $uuid->generate();
+      $duplicate->{$entity_info['entity_keys']['uuid']}->value = $uuid->generate();
     }
     return $duplicate;
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
index 2dcf682..353ed1d 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
@@ -10,9 +10,9 @@
 /**
  * Defines a common interface for entity controller classes.
  *
- * All entity controller classes specified via the 'controller class' key
- * returned by hook_entity_info() or hook_entity_info_alter() have to implement
- * this interface.
+ * All entity controller classes specified via the 'controller_class' key
+ * returned by \Drupal\Core\Entity\EntityManager or hook_entity_info_alter()
+ * have to implement this interface.
  *
  * Most simple, SQL-based entity controllers will do better by extending
  * Drupal\Core\Entity\DatabaseStorageController instead of implementing this
diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index fbd8dda..27f5cae 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -5,7 +5,7 @@
  * Admin page callbacks for the book module.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Page callback: Returns an administrative overview of all books.
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 7c43e28..82e5d03 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -5,7 +5,7 @@
  * Allows users to create and organize related content in an outline.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 use Drupal\Core\Template\Attribute;
 
 /**
@@ -253,11 +253,9 @@ function book_admin_paths() {
  */
 function book_entity_info_alter(&$info) {
   // Add the 'Print' view mode for nodes.
-  $info['node']['view modes'] += array(
-    'print' => array(
-      'label' => t('Print'),
-      'custom settings' => FALSE,
-    ),
+  $info['node']['view_modes']['print'] = array(
+    'label' => t('Print'),
+    'custom settings' => FALSE,
   );
 }
 
diff --git a/core/modules/book/book.pages.inc b/core/modules/book/book.pages.inc
index 90b2192..ae79318 100644
--- a/core/modules/book/book.pages.inc
+++ b/core/modules/book/book.pages.inc
@@ -5,7 +5,7 @@
  * User page callbacks for the book module.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
diff --git a/core/modules/book/lib/Drupal/book/Tests/BookTest.php b/core/modules/book/lib/Drupal/book/Tests/BookTest.php
index a1a88c4..5f50fe8 100644
--- a/core/modules/book/lib/Drupal/book/Tests/BookTest.php
+++ b/core/modules/book/lib/Drupal/book/Tests/BookTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\book\Tests;
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 use Drupal\simpletest\WebTestBase;
 
 class BookTest extends WebTestBase {
diff --git a/core/modules/breakpoint/breakpoint.module b/core/modules/breakpoint/breakpoint.module
index 455c1fd..07cbc42 100644
--- a/core/modules/breakpoint/breakpoint.module
+++ b/core/modules/breakpoint/breakpoint.module
@@ -5,8 +5,8 @@
  * Manage breakpoints and breakpoint groups for responsive designs.
  */
 
-use Drupal\breakpoint\Breakpoint;
-use Drupal\breakpoint\BreakpointGroup;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
+use Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup;
 
 /**
  * Implements hook_help().
@@ -193,7 +193,7 @@ function _breakpoint_delete_breakpoints($list, $source_type) {
 
   // Remove the breakpoint.breakpoint part of the breakpoint identifier.
   foreach ($ids as &$id) {
-    $id = drupal_substr($id, drupal_strlen($entity_info['config prefix']) + 1);
+    $id = drupal_substr($id, drupal_strlen($entity_info['config_prefix']) + 1);
   }
   $breakpoint_groups = entity_load_multiple('breakpoint_group', $ids);
 
@@ -208,7 +208,7 @@ function _breakpoint_delete_breakpoints($list, $source_type) {
 
       // Remove the breakpoint.breakpoint part of the breakpoint identifier.
       foreach ($breakpoint_ids as &$breakpoint_id) {
-        $breakpoint_id = drupal_substr($breakpoint_id, drupal_strlen($entity_info['config prefix']) + 1);
+        $breakpoint_id = drupal_substr($breakpoint_id, drupal_strlen($entity_info['config_prefix']) + 1);
       }
       $breakpoints = entity_load_multiple('breakpoint', $breakpoint_ids);
 
@@ -290,45 +290,12 @@ function breakpoint_get_module_media_queries($module) {
 }
 
 /**
- * Implements hook_entity_info().
- */
-function breakpoint_entity_info() {
-  // Breakpoint.
-  $types['breakpoint'] = array(
-    'label' => 'Breakpoint',
-    'entity class' => 'Drupal\breakpoint\Breakpoint',
-    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
-    'config prefix' => 'breakpoint.breakpoint',
-    'entity keys' => array(
-      'id' => 'id',
-      'label' => 'label',
-      'uuid' => 'uuid',
-    ),
-  );
-
-  // Breakpoint group.
-  $types['breakpoint_group'] = array(
-    'label' => 'Breakpoint group',
-    'entity class' => 'Drupal\breakpoint\BreakpointGroup',
-    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
-    'config prefix' => 'breakpoint.breakpoint_group',
-    'entity keys' => array(
-      'id' => 'id',
-      'label' => 'label',
-      'uuid' => 'uuid',
-    ),
-  );
-
-  return $types;
-}
-
-/**
  * Load one breakpoint group by its identifier.
  *
  * @param string $id
  *   The id of the breakpoint group to load.
  *
- * @return Drupal\breakpoint\BreakpointGroup|false
+ * @return Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup|false
  *   The breakpoint group, or FALSE if there is no entity with the given id.
  *
  * @todo Remove this in a follow-up issue.
@@ -344,7 +311,7 @@ function breakpoint_group_load($id) {
  * @param int $id
  *   The id of the breakpoint to load.
  *
- * @return Drupal\breakpoint\Breakpoint
+ * @return Drupal\breakpoint\Plugin\Core\Entity\Breakpoint
  *   The entity object, or FALSE if there is no entity with the given id.
  *
  * @todo Remove this in a follow-up issue.
@@ -398,7 +365,7 @@ function breakpoint_select_options() {
  * @param string $source_type
  *   Either Breakpoint::SOURCE_TYPE_THEME or Breakpoint::SOURCE_TYPE_MODULE.
  *
- * @return Drupal\breakpoint\BreakpointGroup
+ * @return Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup
  *
  * @see _breakpoint_import_media_queries()
  * @see _breakpoint_import_breakpoint_groups()
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Breakpoint.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php
similarity index 95%
rename from core/modules/breakpoint/lib/Drupal/breakpoint/Breakpoint.php
rename to core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php
index b695e47..7c7301d 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Breakpoint.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php
@@ -2,10 +2,10 @@
 
 /**
  * @file
- * Definition of Drupal\breakpoint\Breakpoint.
+ * Definition of Drupal\breakpoint\Plugin\Core\Entity\Breakpoint.
  */
 
-namespace Drupal\breakpoint;
+namespace Drupal\breakpoint\Plugin\Core\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\breakpoint\InvalidBreakpointException;
@@ -13,9 +13,24 @@
 use Drupal\breakpoint\InvalidBreakpointSourceException;
 use Drupal\breakpoint\InvalidBreakpointSourceTypeException;
 use Drupal\breakpoint\InvalidBreakpointMediaQueryException;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the Breakpoint entity.
+ *
+ * @Plugin(
+ *   id = "breakpoint",
+ *   label = @Translation("Breakpoint"),
+ *   module = "breakpoint",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   config_prefix = "breakpoint.breakpoint",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class Breakpoint extends ConfigEntityBase {
 
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroup.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php
similarity index 89%
rename from core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroup.php
rename to core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php
index fc47e91..2fe4227 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroup.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php
@@ -2,17 +2,32 @@
 
 /**
  * @file
- * Definition of Drupal\breakpoint\BreakpointGroup.
+ * Definition of Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup.
  */
 
-namespace Drupal\breakpoint;
+namespace Drupal\breakpoint\Plugin\Core\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\breakpoint\InvalidBreakpointSourceException;
 use Drupal\breakpoint\InvalidBreakpointSourceTypeException;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the BreakpointGroup entity.
+ *
+ * @Plugin(
+ *   id = "breakpoint_group",
+ *   label = @Translation("Breakpoint group"),
+ *   module = "breakpoint",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   config_prefix = "breakpoint.breakpoint_group",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class BreakpointGroup extends ConfigEntityBase {
 
@@ -50,7 +65,7 @@ class BreakpointGroup extends ConfigEntityBase {
    * @var array
    *   Array containing all breakpoints of this group.
    *
-   * @see Drupal\breakpoints\Breakpoint
+   * @see Drupal\breakpoint\Plugin\Core\Entity\Breakpoint
    */
   public $breakpoints = array();
 
@@ -71,7 +86,7 @@ class BreakpointGroup extends ConfigEntityBase {
    *     Breakpoint::SOURCE_TYPE_MODULE
    *     Breakpoint::SOURCE_TYPE_USER_DEFINED
    *
-   * @see Drupal\breakpoint\Breakpoint
+   * @see Drupal\breakpoint\Plugin\Core\Entity\Breakpoint
    */
   public $sourceType = Breakpoint::SOURCE_TYPE_USER_DEFINED;
 
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
index 24c2ae9..f65b559 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
@@ -7,7 +7,7 @@
 namespace Drupal\breakpoint\Tests;
 
 use Drupal\breakpoint\Tests\BreakpointsTestBase;
-use Drupal\breakpoint\Breakpoint;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
 use Drupal\breakpoint\InvalidBreakpointNameException;
 use Drupal\breakpoint\InvalidBreakpointSourceException;
 use Drupal\breakpoint\InvalidBreakpointSourceTypeException;
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
index 1d5d6e1..37caff7 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
@@ -7,7 +7,7 @@
 namespace Drupal\breakpoint\Tests;
 
 use Drupal\breakpoint\Tests\BreakpointTestBase;
-use Drupal\breakpoint\Breakpoint;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
 
 /**
  * Tests for breakpoint CRUD operations.
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
index 9fd2ee3..d2bc016 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
@@ -7,8 +7,8 @@
 namespace Drupal\breakpoint\Tests;
 
 use Drupal\breakpoint\Tests\BreakpointsTestBase;
-use Drupal\breakpoint\BreakpointGroup;
-use Drupal\breakpoint\Breakpoint;
+use Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
 use Drupal\breakpoint\InvalidBreakpointNameException;
 use Drupal\breakpoint\InvalidBreakpointSourceException;
 use Drupal\breakpoint\InvalidBreakpointSourceTypeException;
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCRUDTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCRUDTest.php
index 72d6c1e..05eaeaf 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCRUDTest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupCRUDTest.php
@@ -7,8 +7,8 @@
 namespace Drupal\breakpoint\Tests;
 
 use Drupal\breakpoint\Tests\BreakpointGroupTestBase;
-use Drupal\breakpoint\BreakpointGroup;
-use Drupal\breakpoint\Breakpoint;
+use Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
 
 /**
  * Tests for breakpoint group CRUD operations.
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupTestBase.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupTestBase.php
index febf71d..ee1e57d 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupTestBase.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupTestBase.php
@@ -7,7 +7,7 @@
 namespace Drupal\breakpoint\Tests;
 
 use Drupal\simpletest\WebTestBase;
-use Drupal\breakpoint\BreakpointGroup;
+use Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup;
 
 /**
  * Base class for Breakpoint group tests.
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php
index 1696c2b..d0ee5fb 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php
@@ -7,7 +7,7 @@
 namespace Drupal\breakpoint\Tests;
 
 use Drupal\simpletest\UnitTestBase;
-use Drupal\breakpoint\Breakpoint;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
 use Drupal\breakpoint\InvalidBreakpointMediaQueryException;
 
 /**
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php
index f8f2b35..cca4d62 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php
@@ -7,7 +7,7 @@
 namespace Drupal\breakpoint\Tests;
 
 use Drupal\simpletest\WebTestBase;
-use Drupal\breakpoint\Breakpoint;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
 
 /**
  * Base class for Breakpoint tests.
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php
index 0737611..0091300 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointThemeTest.php
@@ -7,8 +7,8 @@
 namespace Drupal\breakpoint\Tests;
 
 use Drupal\breakpoint\Tests\BreakpointGroupTestBase;
-use Drupal\breakpoint\BreakpointGroup;
-use Drupal\breakpoint\Breakpoint;
+use Drupal\breakpoint\Plugin\Core\Entity\BreakpointGroup;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
 
 /**
  * Test breakpoints provided by themes.
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index dcbd1bc..26e9c8b 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -5,7 +5,7 @@
  * Admin page callbacks for the Comment module.
  */
 
-use Drupal\comment\Comment;
+use Drupal\comment\Plugin\Core\Entity\Comment;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index df0a33a..220e957 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -9,8 +9,8 @@
  * book page, etc.
  */
 
-use Drupal\node\Node;
-use Drupal\file\File;
+use Drupal\node\Plugin\Core\Entity\Node;
+use Drupal\file\Plugin\Core\Entity\File;
 use Drupal\Core\Entity\EntityInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -76,7 +76,7 @@
  */
 const COMMENT_NODE_OPEN = 2;
 
-use Drupal\comment\Comment;
+use Drupal\comment\Plugin\Core\Entity\Comment;
 
 /**
  * Implements hook_help().
@@ -98,40 +98,16 @@ function comment_help($path, $arg) {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_info_alter().
  */
-function comment_entity_info() {
-  $return = array(
-    'comment' => array(
-      'label' => t('Comment'),
-      'base table' => 'comment',
-      'uri callback' => 'comment_uri',
-      'fieldable' => TRUE,
-      'controller class' => 'Drupal\comment\CommentStorageController',
-      'form controller class' => array(
-        'default' => 'Drupal\comment\CommentFormController',
-      ),
-      'entity class' => 'Drupal\comment\Comment',
-      'entity keys' => array(
-        'id' => 'cid',
-        'bundle' => 'node_type',
-        'label' => 'subject',
-        'uuid' => 'uuid',
-      ),
-      'bundles' => array(),
-      'render controller class' => 'Drupal\comment\CommentRenderController',
-      'view modes' => array(
-        'full' => array(
-          'label' => t('Full comment'),
-          'custom settings' => FALSE,
-        ),
-      ),
-      'static cache' => FALSE,
-    ),
+function comment_entity_info_alter(&$info) {
+  $info['comment']['view_modes']['full'] = array(
+    'label' => t('Full comment'),
+    'custom settings' => FALSE,
   );
 
   foreach (node_type_get_names() as $type => $name) {
-    $return['comment']['bundles']['comment_node_' . $type] = array(
+    $info['comment']['bundles']['comment_node_' . $type] = array(
       'label' => t('@node_type comment', array('@node_type' => $name)),
       // Provide the node type/bundle name for other modules, so it does not
       // have to be extracted manually from the bundle name.
@@ -150,8 +126,6 @@ function comment_entity_info() {
       ),
     );
   }
-
-  return $return;
 }
 
 /**
diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc
index 3d1ecb9..6263660 100644
--- a/core/modules/comment/comment.pages.inc
+++ b/core/modules/comment/comment.pages.inc
@@ -5,7 +5,7 @@
  * User page callbacks for the Comment module.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
diff --git a/core/modules/comment/lib/Drupal/comment/Comment.php b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
similarity index 67%
rename from core/modules/comment/lib/Drupal/comment/Comment.php
rename to core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
index ef6c16a..c2dd9f2 100644
--- a/core/modules/comment/lib/Drupal/comment/Comment.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
@@ -2,16 +2,39 @@
 
 /**
  * @file
- * Definition of Drupal\comment\Comment.
+ * Definition of Drupal\comment\Plugin\Core\Entity\Comment.
  */
 
-namespace Drupal\comment;
+namespace Drupal\comment\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the comment entity class.
+ *
+ * @Plugin(
+ *   id = "comment",
+ *   label = @Translation("Comment"),
+ *   module = "comment",
+ *   controller_class = "Drupal\comment\CommentStorageController",
+ *   render_controller_class = "Drupal\comment\CommentRenderController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\comment\CommentFormController"
+ *   },
+ *   base_table = "comment",
+ *   uri_callback = "comment_uri",
+ *   fieldable = TRUE,
+ *   static_cache = FALSE,
+ *   entity_keys = {
+ *     "id" = "cid",
+ *     "bundle" = "node_type",
+ *     "label" = "subject",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class Comment extends Entity implements ContentEntityInterface {
 
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/views/row/Rss.php b/core/modules/comment/lib/Drupal/comment/Plugin/views/row/Rss.php
index d140963..cb39cd2 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/views/row/Rss.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/views/row/Rss.php
@@ -81,8 +81,8 @@ function pre_render($result) {
   function options_form_summary_options() {
     $entity_info = entity_get_info('node');
     $options = array();
-    if (!empty($entity_info['view modes'])) {
-      foreach ($entity_info['view modes'] as $mode => $settings) {
+    if (!empty($entity_info['view_modes'])) {
+      foreach ($entity_info['view_modes'] as $mode => $settings) {
         $options[$mode] = $settings['label'];
       }
     }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php
index 37a8b47..e8bab5d 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\comment\Tests;
 
-use Drupal\comment\Comment;
+use Drupal\comment\Plugin\Core\Entity\Comment;
 use Drupal\simpletest\WebTestBase;
 
 /**
diff --git a/core/modules/config/config.api.php b/core/modules/config/config.api.php
index 147412d..d845667 100644
--- a/core/modules/config/config.api.php
+++ b/core/modules/config/config.api.php
@@ -69,7 +69,7 @@ function hook_config_import_change($name, $new_config, $old_config) {
   // @todo Make this less ugly.
   list($entity_type) = explode('.', $name);
   $entity_info = entity_get_info($entity_type);
-  $id = substr($name, strlen($entity_info['config prefix']) + 1);
+  $id = substr($name, strlen($entity_info['config_prefix']) + 1);
   $config_test = entity_load('config_test', $id);
 
   // Store the original config, and iterate through each property to store it.
@@ -119,7 +119,7 @@ function hook_config_import_delete($name, $new_config, $old_config) {
   //   config objects as a standard?
   list($entity_type) = explode('.', $name);
   $entity_info = entity_get_info($entity_type);
-  $id = substr($name, strlen($entity_info['config prefix']) + 1);
+  $id = substr($name, strlen($entity_info['config_prefix']) + 1);
   config_test_delete($id);
   return TRUE;
 }
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
index cd48f71..80698c1 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
@@ -8,7 +8,7 @@
 namespace Drupal\config\Tests;
 
 use Drupal\simpletest\WebTestBase;
-use Drupal\config_test\ConfigTest;
+use Drupal\config_test\Plugin\Core\Entity\ConfigTest;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 
 /**
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
index 3e1cbe9..6286b1c 100644
--- a/core/modules/config/tests/config_test/config_test.module
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -5,7 +5,7 @@
  * Provides Config module hook implementations for testing purposes.
  */
 
-use Drupal\config_test\ConfigTest;
+use Drupal\config_test\Plugin\Core\Entity\ConfigTest;
 
 require_once dirname(__FILE__) . '/config_test.hooks.inc';
 
@@ -37,7 +37,7 @@ function config_test_config_import_change($name, $new_config, $old_config) {
   // @todo Make this less ugly.
   list($entity_type) = explode('.', $name);
   $entity_info = entity_get_info($entity_type);
-  $id = substr($name, strlen($entity_info['config prefix']) + 1);
+  $id = substr($name, strlen($entity_info['config_prefix']) + 1);
   $config_test = entity_load('config_test', $id);
 
   // Store the original config, and iterate through each property to store it.
@@ -69,35 +69,15 @@ function config_test_config_import_delete($name, $new_config, $old_config) {
   // @todo Make this less ugly.
   list($entity_type) = explode('.', $name);
   $entity_info = entity_get_info($entity_type);
-  $id = substr($name, strlen($entity_info['config prefix']) + 1);
+  $id = substr($name, strlen($entity_info['config_prefix']) + 1);
   config_test_delete($id);
   return TRUE;
 }
 
 /**
- * Implements hook_entity_info().
- */
-function config_test_entity_info() {
-  $types['config_test'] = array(
-    'label' => 'Test configuration',
-    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
-    'entity class' => 'Drupal\config_test\ConfigTest',
-    'list controller class' => 'Drupal\Core\Config\Entity\ConfigEntityListController',
-    'uri callback' => 'config_test_uri',
-    'config prefix' => 'config_test.dynamic',
-    'entity keys' => array(
-      'id' => 'id',
-      'label' => 'label',
-      'uuid' => 'uuid',
-    ),
-  );
-  return $types;
-}
-
-/**
  * Entity URI callback.
  *
- * @param Drupal\config_test\ConfigTest $config_test
+ * @param Drupal\config_test\Plugin\Core\Entity\ConfigTest $config_test
  *   A ConfigTest entity.
  */
 function config_test_uri(ConfigTest $config_test) {
@@ -156,7 +136,7 @@ function config_test_load($id) {
 /**
  * Saves a ConfigTest object.
  *
- * @param Drupal\config_test\ConfigTest $config_test
+ * @param Drupal\config_test\Plugin\Core\Entity\ConfigTest $config_test
  *   The ConfigTest object to save.
  */
 function config_test_save(ConfigTest $config_test) {
@@ -184,7 +164,7 @@ function config_test_list_page() {
 /**
  * Form constructor to add or edit a ConfigTest object.
  *
- * @param Drupal\config_test\ConfigTest $config_test
+ * @param Drupal\config_test\Plugin\Core\Entity\ConfigTest $config_test
  *   (optional) An existing ConfigTest object to edit. If omitted, the form
  *   creates a new ConfigTest.
  */
@@ -258,7 +238,7 @@ function config_test_form_submit($form, &$form_state) {
 /**
  * Form constructor to delete a ConfigTest object.
  *
- * @param Drupal\config_test\ConfigTest $config_test
+ * @param Drupal\config_test\Plugin\Core\Entity\ConfigTest $config_test
  *   The ConfigTest object to delete.
  */
 function config_test_delete_form($form, &$form_state, ConfigTest $config_test) {
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php
deleted file mode 100644
index e1df5c4..0000000
--- a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\config_test\ConfigTest.
- */
-
-namespace Drupal\config_test;
-
-use Drupal\Core\Config\Entity\ConfigEntityBase;
-
-/**
- * Defines the ConfigTest configuration entity.
- */
-class ConfigTest extends ConfigEntityBase {
-
-  /**
-   * The machine name for the configuration entity.
-   *
-   * @var string
-   */
-  public $id;
-
-  /**
-   * The UUID for the configuration entity.
-   *
-   * @var string
-   */
-  public $uuid;
-
-  /**
-   * The human-readable name of the configuration entity.
-   *
-   * @var string
-   */
-  public $label;
-
-  /**
-   * The image style to use.
-   *
-   * @var string
-   */
-  public $style;
-
-}
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
new file mode 100644
index 0000000..d7947d3
--- /dev/null
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config_test\Plugin\Core\Entity\ConfigTest.
+ */
+
+namespace Drupal\config_test\Plugin\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the ConfigTest configuration entity.
+ *
+ * @Plugin(
+ *   id = "config_test",
+ *   label = @Translation("Test configuration"),
+ *   module = "config_test",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   list_controller_class = "Drupal\Core\Config\Entity\ConfigEntityListController",
+ *   uri_callback = "config_test_uri",
+ *   config_prefix = "config_test.dynamic",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label",
+ *     "uuid" = "uuid"
+ *   }
+ * )
+ */
+class ConfigTest extends ConfigEntityBase {
+
+  /**
+   * The machine name for the configuration entity.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The UUID for the configuration entity.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The human-readable name of the configuration entity.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * The image style to use.
+   *
+   * @var string
+   */
+  public $style;
+
+}
diff --git a/core/modules/contact/contact.admin.inc b/core/modules/contact/contact.admin.inc
index 6f9adaf..989c55c 100644
--- a/core/modules/contact/contact.admin.inc
+++ b/core/modules/contact/contact.admin.inc
@@ -5,7 +5,7 @@
  * Admin page callbacks for the Contact module.
  */
 
-use Drupal\contact\Category;
+use Drupal\contact\Plugin\Core\Entity\Category;
 
 /**
  * Page callback: Lists contact categories.
@@ -35,7 +35,7 @@ function contact_category_add() {
 /**
  * Page callback: Form constructor for the contact category deletion form.
  *
- * @param Drupal\contact\Category $category
+ * @param Drupal\contact\Plugin\Core\Entity\Category $category
  *   The contact category to be deleted.
  *
  * @see contact_menu()
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 1087308..5063464 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -5,7 +5,7 @@
  * Enables the use of personal and site-wide contact forms.
  */
 
-use Drupal\contact\Category;
+use Drupal\contact\Plugin\Core\Entity\Category;
 
 /**
  * Implements hook_help().
@@ -202,35 +202,12 @@ function contact_config_import_delete($name, $new_config, $old_config) {
 }
 
 /**
- * Implements hook_entity_info().
- */
-function contact_entity_info() {
-  $types['contact_category'] = array(
-    'label' => 'Category',
-    'entity class' => 'Drupal\contact\Category',
-    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
-    'list controller class' => 'Drupal\contact\CategoryListController',
-    'form controller class' => array(
-      'default' => 'Drupal\contact\CategoryFormController',
-    ),
-    'uri callback' => 'contact_category_uri',
-    'config prefix' => 'contact.category',
-    'entity keys' => array(
-      'id' => 'id',
-      'label' => 'label',
-      'uuid' => 'uuid',
-    ),
-  );
-  return $types;
-}
-
-/**
  * Loads a contact category.
  *
  * @param $id
  *   The ID of the contact category to load.
  *
- * @return Drupal\contact\Category|false
+ * @return Drupal\contact\Plugin\Core\Entity\Category|false
  *   A Category object or FALSE if the requested $id does not exist.
  */
 function contact_category_load($id) {
diff --git a/core/modules/contact/lib/Drupal/contact/Category.php b/core/modules/contact/lib/Drupal/contact/Category.php
deleted file mode 100644
index 1a34479..0000000
--- a/core/modules/contact/lib/Drupal/contact/Category.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\contact\Category.
- */
-
-namespace Drupal\contact;
-
-use Drupal\Core\Config\Entity\ConfigEntityBase;
-
-/**
- * Defines the contact category entity.
- */
-class Category extends ConfigEntityBase {
-
-  /**
-   * The category ID.
-   *
-   * @var string
-   */
-  public $id;
-
-  /**
-   * The category UUID.
-   *
-   * @var string
-   */
-  public $uuid;
-
-  /**
-   * The category label.
-   *
-   * @var string
-   */
-  public $label;
-
-  /**
-   * List of recipient e-mail addresses.
-   *
-   * @var array
-   */
-  public $recipients = array();
-
-  /**
-   * An auto-reply message to send to the message author.
-   *
-   * @var string
-   */
-  public $reply = '';
-
-  /**
-   * Weight of this category (used for sorting).
-   *
-   * @var int
-   */
-  public $weight = 0;
-
-}
diff --git a/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php
new file mode 100644
index 0000000..c2d456b
--- /dev/null
+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\contact\Plugin\Core\Entity\Category.
+ */
+
+namespace Drupal\contact\Plugin\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the contact category entity.
+ *
+ * @Plugin(
+ *   id = "contact_category",
+ *   label = @Translation("Category"),
+ *   module = "contact",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   list_controller_class = "Drupal\contact\CategoryListController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\contact\CategoryFormController"
+ *   },
+ *   uri_callback = "contact_category_uri",
+ *   config_prefix = "contact.category",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label",
+ *     "uuid" = "uuid"
+ *   }
+ * )
+ */
+class Category extends ConfigEntityBase {
+
+  /**
+   * The category ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The category UUID.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The category label.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * List of recipient e-mail addresses.
+   *
+   * @var array
+   */
+  public $recipients = array();
+
+  /**
+   * An auto-reply message to send to the message author.
+   *
+   * @var string
+   */
+  public $reply = '';
+
+  /**
+   * Weight of this category (used for sorting).
+   *
+   * @var int
+   */
+  public $weight = 0;
+
+}
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index d267546..c972299 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -60,19 +60,23 @@
  * of the fieldable entity, such as 'node' or 'user', and $entity is the entity
  * itself.
  *
- * hook_entity_info() is the central place for entity types to define if and how
+ * An entity plugin's annotation is how entity types define if and how
  * Field API should operate on their entity objects. Notably, the 'fieldable'
  * property needs to be set to TRUE.
  *
  * The Field Attach API uses the concept of bundles: the set of fields for a
  * given entity is defined on a per-bundle basis. The collection of bundles for
- * an entity type is defined its hook_entity_info() implementation. For
- * instance, node_entity_info() exposes each node type as its own bundle. This
- * means that the set of fields of a node is determined by the node type. The
- * Field API reads the bundle name for a given entity from a particular property
- * of the entity object, and hook_entity_info() defines which property to use.
- * For instance, node_entity_info() specifies:
- * @code $info['entity keys']['bundle'] = 'type'@endcode
+ * an entity type is added to the entity definition with
+ * hook_entity_info_alter(). For instance, node_entity_info_alter() exposes
+ * each node type as its own bundle. This means that the set of fields of a
+ * node is determined by the node type.
+ *
+ * The Field API reads the bundle name for a given entity from a particular
+ * property of the entity object, and hook_entity_info_alter() defines which
+ * property to use. For instance, node_entity_info_alter() specifies:
+ * @code
+ *   $info['entity_keys']['bundle'] = 'type'
+ * @endcode
  * This indicates that for a particular node object, the bundle name can be
  * found in $node->type. This property can be omitted if the entity type only
  * exposes a single bundle (all entities of this type have the same collection
@@ -900,7 +904,7 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $
   $info = entity_get_info($entity_type);
   // Only the most current revision of non-deleted fields for cacheable entity
   // types can be cached.
-  $cache_read = $load_current && $info['field cache'] && empty($options['deleted']);
+  $cache_read = $load_current && $info['field_cache'] && empty($options['deleted']);
   // In addition, do not write to the cache when loading a single field.
   $cache_write = $cache_read && !isset($options['field_id']);
 
@@ -1258,7 +1262,7 @@ function field_attach_update($entity_type, EntityInterface $entity) {
   module_invoke_all('field_attach_update', $entity_type, $entity);
 
   $entity_info = entity_get_info($entity_type);
-  if ($entity_info['field cache']) {
+  if ($entity_info['field_cache']) {
     cache('field')->delete("field:$entity_type:" . $entity->id());
   }
 }
@@ -1293,7 +1297,7 @@ function field_attach_delete($entity_type, $entity) {
   module_invoke_all('field_attach_delete', $entity_type, $entity);
 
   $entity_info = entity_get_info($entity_type);
-  if ($entity_info['field cache']) {
+  if ($entity_info['field_cache']) {
     cache('field')->delete("field:$entity_type:" . $entity->id());
   }
 }
diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc
index ba2f09e..429bc6d 100644
--- a/core/modules/field/field.crud.inc
+++ b/core/modules/field/field.crud.inc
@@ -91,7 +91,7 @@ function field_create_field($field) {
   // collisions with existing entity properties, but some is better
   // than none.
   foreach (entity_get_info() as $type => $info) {
-    if (in_array($field['field_name'], $info['entity keys'])) {
+    if (in_array($field['field_name'], $info['entity_keys'])) {
       throw new FieldException(t('Attempt to create field name %name which is reserved by entity type %type.', array('%name' => $field['field_name'], '%type' => $type)));
     }
   }
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 7fac3d2..43ae3f9 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -740,10 +740,10 @@ function field_view_mode_settings($entity_type, $bundle) {
     $bundle_settings = field_bundle_settings($entity_type, $bundle);
     $settings = $bundle_settings['view_modes'];
     // Include view modes for which nothing has been stored yet, but whose
-    // definition in hook_entity_info() specify they should use custom settings
-    // by default.
+    // definition in hook_entity_info_alter() specify they should use custom
+    // settings by default.
     $entity_info = entity_get_info($entity_type);
-    foreach ($entity_info['view modes'] as $view_mode => $view_mode_info) {
+    foreach ($entity_info['view_modes'] as $view_mode => $view_mode_info) {
       if (!isset($settings[$view_mode]['custom_settings']) && $view_mode_info['custom settings']) {
         $settings[$view_mode]['custom_settings'] = TRUE;
       }
@@ -1112,8 +1112,8 @@ function field_extract_bundle($entity_type, $bundle) {
   }
 
   $info = entity_get_info($entity_type);
-  if (is_object($bundle) && isset($info['bundle keys']['bundle']) && isset($bundle->{$info['bundle keys']['bundle']})) {
-    return $bundle->{$info['bundle keys']['bundle']};
+  if (is_object($bundle) && isset($info['bundle_keys']['bundle']) && isset($bundle->{$info['bundle_keys']['bundle']})) {
+    return $bundle->{$info['bundle_keys']['bundle']};
   }
 }
 
@@ -1300,12 +1300,12 @@ function theme_field($variables) {
 function _field_create_entity_from_ids($ids) {
   $id_properties = array();
   $info = entity_get_info($ids->entity_type);
-  $id_properties[$info['entity keys']['id']] = $ids->entity_id;
-  if (!empty($info['entity keys']['revision']) && isset($ids->revision_id)) {
-    $id_properties[$info['entity keys']['revision']] = $ids->revision_id;
+  $id_properties[$info['entity_keys']['id']] = $ids->entity_id;
+  if (!empty($info['entity_keys']['revision']) && isset($ids->revision_id)) {
+    $id_properties[$info['entity_keys']['revision']] = $ids->revision_id;
   }
-  if (!empty($info['entity keys']['bundle']) && isset($ids->bundle)) {
-    $id_properties[$info['entity keys']['bundle']] = $ids->bundle;
+  if (!empty($info['entity_keys']['bundle']) && isset($ids->bundle)) {
+    $id_properties[$info['entity_keys']['bundle']] = $ids->bundle;
   }
   return entity_create($ids->entity_type, $id_properties);
  }
diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc
index b2e4363..73c47cb 100644
--- a/core/modules/field/field.multilingual.inc
+++ b/core/modules/field/field.multilingual.inc
@@ -29,7 +29,7 @@
  * calling field_is_translatable(), which checks the $field['translatable']
  * property returned by field_info_field(), and whether there is at least one
  * translation handler available for the field. A translation handler is a
- * module registering itself via hook_entity_info() to handle field
+ * module registering itself via hook_entity_info_alter() to handle field
  * translations.
  *
 
diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc
index 1629add..07806a1 100644
--- a/core/modules/field/field.views.inc
+++ b/core/modules/field/field.views.inc
@@ -125,15 +125,15 @@ function field_views_field_default_views_data($field) {
       $group_name = $groups[$entity];
     }
 
-    $entity_tables[$entity_info['base table']] = $entity;
-    $current_tables[$entity] = $entity_info['base table'];
-    if (isset($entity_info['revision table'])) {
-      $entity_tables[$entity_info['revision table']] = $entity;
-      $revision_tables[$entity] = $entity_info['revision table'];
+    $entity_tables[$entity_info['base_table']] = $entity;
+    $current_tables[$entity] = $entity_info['base_table'];
+    if (isset($entity_info['revision_table'])) {
+      $entity_tables[$entity_info['revision_table']] = $entity;
+      $revision_tables[$entity] = $entity_info['revision_table'];
     }
 
-    $data[$current_table]['table']['join'][$entity_info['base table']] = array(
-      'left_field' => $entity_info['entity keys']['id'],
+    $data[$current_table]['table']['join'][$entity_info['base_table']] = array(
+      'left_field' => $entity_info['entity_keys']['id'],
       'field' => 'entity_id',
       'extra' => array(
         array('field' => 'entity_type', 'value' => $entity),
@@ -141,9 +141,9 @@ function field_views_field_default_views_data($field) {
       ),
     );
 
-    if (!empty($entity_info['entity keys']['revision']) && !empty($entity_info['revision table'])) {
-      $data[$revision_table]['table']['join'][$entity_info['revision table']] = array(
-        'left_field' => $entity_info['entity keys']['revision'],
+    if (!empty($entity_info['entity_keys']['revision']) && !empty($entity_info['revision_table'])) {
+      $data[$revision_table]['table']['join'][$entity_info['revision_table']] = array(
+        'left_field' => $entity_info['entity_keys']['revision'],
         'field' => 'revision_id',
         'extra' => array(
           array('field' => 'entity_type', 'value' => $entity),
diff --git a/core/modules/field/lib/Drupal/field/FieldInfo.php b/core/modules/field/lib/Drupal/field/FieldInfo.php
index accbe17..b1692f7 100644
--- a/core/modules/field/lib/Drupal/field/FieldInfo.php
+++ b/core/modules/field/lib/Drupal/field/FieldInfo.php
@@ -549,7 +549,7 @@ public function prepareExtraFields($extra_fields, $entity_type, $bundle) {
     $data = $extra_fields['display'];
     foreach ($extra_fields['display'] as $name => $field_data) {
       $settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array();
-      $view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes']));
+      $view_modes = array_merge(array('default'), array_keys($entity_type_info['view_modes']));
       foreach ($view_modes as $view_mode) {
         if (isset($settings[$view_mode])) {
           $field_data['display'][$view_mode] = $settings[$view_mode];
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
index 690768e..a4be944 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
@@ -423,8 +423,7 @@ function testFieldAttachDelete() {
    * Test field_attach_create_bundle() and field_attach_rename_bundle().
    */
   function testFieldAttachCreateRenameBundle() {
-    // Create a new bundle. This has to be initiated by the module so that its
-    // hook_entity_info() is consistent.
+    // Create a new bundle.
     $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
     field_test_create_bundle($new_bundle);
 
@@ -445,8 +444,7 @@ function testFieldAttachCreateRenameBundle() {
     field_attach_load($entity_type, array(0 => $entity));
     $this->assertEqual(count($entity->{$this->field_name}[$langcode]), $this->field['cardinality'], "Data is retrieved for the new bundle");
 
-    // Rename the bundle. This has to be initiated by the module so that its
-    // hook_entity_info() is consistent.
+    // Rename the bundle.
     $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
     field_test_rename_bundle($this->instance['bundle'], $new_bundle);
 
@@ -464,8 +462,7 @@ function testFieldAttachCreateRenameBundle() {
    * Test field_attach_delete_bundle().
    */
   function testFieldAttachDeleteBundle() {
-    // Create a new bundle. This has to be initiated by the module so that its
-    // hook_entity_info() is consistent.
+    // Create a new bundle.
     $new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
     field_test_create_bundle($new_bundle);
 
@@ -505,8 +502,7 @@ function testFieldAttachDeleteBundle() {
     $this->assertEqual(count($entity->{$this->field_name}[$langcode]), 4, 'First field got loaded');
     $this->assertEqual(count($entity->{$field_name}[$langcode]), 1, 'Second field got loaded');
 
-    // Delete the bundle. This has to be initiated by the module so that its
-    // hook_entity_info() is consistent.
+    // Delete the bundle.
     field_test_delete_bundle($this->instance['bundle']);
 
     // Verify no data gets loaded
diff --git a/core/modules/field/modules/field_sql_storage/field_sql_storage.module b/core/modules/field/modules/field_sql_storage/field_sql_storage.module
index dcd6ba1..265c117 100644
--- a/core/modules/field/modules/field_sql_storage/field_sql_storage.module
+++ b/core/modules/field/modules/field_sql_storage/field_sql_storage.module
@@ -615,7 +615,7 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
         $entity_schema = drupal_get_schema($entity_base_table);
         $entity_info = entity_get_info($entity_type);
       }
-      $key = !empty($entity_info['entity keys'][$key]) ? $entity_info['entity keys'][$key] : $key;
+      $key = !empty($entity_info['entity_keys'][$key]) ? $entity_info['entity_keys'][$key] : $key;
       if (!empty($entity_schema['fields'][$key])) {
         $table = $entity_base_table;
       }
@@ -665,8 +665,8 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
  */
 function _field_sql_storage_query_join_entity(Select $select_query, $entity_type, $field_base_table) {
   $entity_info = entity_get_info($entity_type);
-  $entity_base_table = $entity_info['base table'];
-  $entity_field = $entity_info['entity keys']['id'];
+  $entity_base_table = $entity_info['base_table'];
+  $entity_field = $entity_info['entity_keys']['id'];
   $select_query->join($entity_base_table, $entity_base_table, "$entity_base_table.$entity_field = $field_base_table.entity_id");
   return $entity_base_table;
 }
diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc
index fab06fa..7f5eb16 100644
--- a/core/modules/field/tests/modules/field_test/field_test.entity.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc
@@ -6,141 +6,7 @@
  */
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\field_test\TestEntity;
-
-/**
- * Implements hook_entity_info().
- */
-function field_test_entity_info() {
-  $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
-  $test_entity_modes = array(
-    'full' => array(
-      'label' => t('Full object'),
-      'custom settings' => TRUE,
-    ),
-    'teaser' => array(
-      'label' => t('Teaser'),
-      'custom settings' => TRUE,
-    ),
-  );
-
-  return array(
-    'test_entity' => array(
-      'label' => t('Test Entity'),
-      'entity class' => 'Drupal\field_test\TestEntity',
-      'controller class' => 'Drupal\field_test\TestEntityController',
-      'form controller class' => array(
-        'default' => 'Drupal\field_test\TestEntityFormController',
-      ),
-      'fieldable' => TRUE,
-      'field cache' => FALSE,
-      'base table' => 'test_entity',
-      'revision table' => 'test_entity_revision',
-      'entity keys' => array(
-        'id' => 'ftid',
-        'revision' => 'ftvid',
-        'bundle' => 'fttype',
-      ),
-      'bundles' => $bundles,
-      'view modes' => $test_entity_modes,
-    ),
-    // This entity type doesn't get form handling for now...
-    'test_cacheable_entity' => array(
-      'label' => t('Test Entity, cacheable'),
-      'entity class' => 'Drupal\field_test\TestEntity',
-      'controller class' => 'Drupal\field_test\TestEntityController',
-      'fieldable' => TRUE,
-      'field cache' => TRUE,
-      'entity keys' => array(
-        'id' => 'ftid',
-        'revision' => 'ftvid',
-        'bundle' => 'fttype',
-      ),
-      'bundles' => $bundles,
-      'view modes' => $test_entity_modes,
-    ),
-    'test_entity_bundle_key' => array(
-      'entity class' => 'Drupal\field_test\TestEntity',
-      'controller class' => 'Drupal\field_test\TestEntityController',
-      'label' => t('Test Entity with a bundle key.'),
-      'base table' => 'test_entity_bundle_key',
-      'fieldable' => TRUE,
-      'field cache' => FALSE,
-      'entity keys' => array(
-        'id' => 'ftid',
-        'bundle' => 'fttype',
-      ),
-      'bundles' => array('bundle1' => array('label' => 'Bundle1'), 'bundle2' => array('label' => 'Bundle2')) + $bundles,
-      'view modes' => $test_entity_modes,
-    ),
-    // In this case, the bundle key is not stored in the database.
-    'test_entity_bundle' => array(
-      'label' => t('Test Entity with a specified bundle.'),
-      'entity class' => 'Drupal\field_test\TestEntity',
-      'controller class' => 'Drupal\field_test\TestEntityController',
-      'base table' => 'test_entity_bundle',
-      'fieldable' => TRUE,
-      'field cache' => FALSE,
-      'entity keys' => array(
-        'id' => 'ftid',
-        'bundle' => 'fttype',
-      ),
-      'bundles' => array('test_entity_2' => array('label' => 'Test entity 2')) + $bundles,
-      'view modes' => $test_entity_modes,
-    ),
-    // @see EntityPropertiesTestCase::testEntityLabel()
-    'test_entity_no_label' => array(
-      'label' => t('Test entity without label'),
-      'entity class' => 'Drupal\field_test\TestEntity',
-      'controller class' => 'Drupal\field_test\TestEntityController',
-      'fieldable' => TRUE,
-      'field cache' => FALSE,
-      'base table' => 'test_entity',
-      'revision table' => 'test_entity_revision',
-      'entity keys' => array(
-        'id' => 'ftid',
-        'revision' => 'ftvid',
-        'bundle' => 'fttype',
-      ),
-      'bundles' => $bundles,
-      'view modes' => $test_entity_modes,
-    ),
-    'test_entity_label' => array(
-      'label' => t('Test entity label'),
-      'entity class' => 'Drupal\field_test\TestEntity',
-      'controller class' => 'Drupal\field_test\TestEntityController',
-      'fieldable' => TRUE,
-      'field cache' => FALSE,
-      'base table' => 'test_entity',
-      'revision table' => 'test_entity_revision',
-      'entity keys' => array(
-        'id' => 'ftid',
-        'revision' => 'ftvid',
-        'bundle' => 'fttype',
-        'label' => 'ftlabel',
-      ),
-      'bundles' => $bundles,
-      'view modes' => $test_entity_modes,
-    ),
-    'test_entity_label_callback' => array(
-      'label' => t('Test entity label callback'),
-      'entity class' => 'Drupal\field_test\TestEntity',
-      'controller class' => 'Drupal\field_test\TestEntityController',
-      'fieldable' => TRUE,
-      'field cache' => FALSE,
-      'base table' => 'test_entity',
-      'revision table' => 'test_entity_revision',
-      'label callback' => 'field_test_entity_label_callback',
-      'entity keys' => array(
-        'id' => 'ftid',
-        'revision' => 'ftvid',
-        'bundle' => 'fttype',
-      ),
-      'bundles' => $bundles,
-      'view modes' => $test_entity_modes,
-    ),
-  );
-}
+use Drupal\field_test\Plugin\Core\Entity\TestEntity;
 
 /**
  * Implements hook_entity_info_alter().
@@ -153,6 +19,25 @@ function field_test_entity_info_alter(&$entity_info) {
   // Disable the entity type translation handler.
   foreach ($entity_info as $entity_type => $info) {
     $entity_info[$entity_type]['translation'][$entity_type] = FALSE;
+    if ($info['module'] == 'field_test') {
+      $entity_info[$entity_type]['view_modes'] = array(
+        'full' => array(
+          'label' => t('Full object'),
+          'custom settings' => TRUE,
+        ),
+        'teaser' => array(
+          'label' => t('Teaser'),
+          'custom settings' => TRUE,
+        ),
+      );
+      $entity_info[$entity_type]['bundles'] = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
+      if ($entity_type == 'test_entity_bundle') {
+        $entity_info[$entity_type]['bundles'] += array('test_entity_2' => array('label' => 'Test entity 2'));
+      }
+      if ($entity_type == 'test_entity_bundle_key') {
+        $entity_info[$entity_type]['bundles'] += array('bundle1' => array('label' => 'Bundle1'), 'bundle2' => array('label' => 'Bundle2'));
+      }
+    }
   }
 }
 
@@ -183,9 +68,11 @@ function field_test_create_bundle($bundle, $text = NULL) {
   $bundles += array($bundle => array('label' => $text ? $text : $bundle));
   variable_set('field_test_bundles', $bundles);
 
-  $info = field_test_entity_info();
+  $info = entity_get_info();
   foreach ($info as $type => $type_info) {
-    field_attach_create_bundle($type, $bundle);
+    if ($type_info['module'] == 'field_test') {
+      field_attach_create_bundle($type, $bundle);
+    }
   }
 }
 
@@ -203,9 +90,11 @@ function field_test_rename_bundle($bundle_old, $bundle_new) {
   unset($bundles[$bundle_old]);
   variable_set('field_test_bundles', $bundles);
 
-  $info = field_test_entity_info();
+  $info = entity_get_info();
   foreach ($info as $type => $type_info) {
-    field_attach_rename_bundle($type, $bundle_old, $bundle_new);
+    if ($type_info['module'] == 'field_test') {
+      field_attach_rename_bundle($type, $bundle_old, $bundle_new);
+    }
   }
 }
 
@@ -220,9 +109,11 @@ function field_test_delete_bundle($bundle) {
   unset($bundles[$bundle]);
   variable_set('field_test_bundles', $bundles);
 
-  $info = field_test_entity_info();
+  $info = entity_get_info();
   foreach ($info as $type => $type_info) {
-    field_attach_delete_bundle($type, $bundle);
+    if ($type_info['module'] == 'field_test') {
+      field_attach_delete_bundle($type, $bundle);
+    }
   }
 }
 
diff --git a/core/modules/field/tests/modules/field_test/field_test.storage.inc b/core/modules/field/tests/modules/field_test/field_test.storage.inc
index ffc919e..c6b06b6 100644
--- a/core/modules/field/tests/modules/field_test/field_test.storage.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.storage.inc
@@ -319,7 +319,7 @@ function field_test_field_storage_query($field_id, $conditions, $count, &$cursor
           // If querying all revisions and the entity type has revisions, we need
           // to key the results by revision_ids.
           $entity_type = entity_get_info($row->type);
-          $id = ($load_current || empty($entity_type['entity keys']['revision'])) ? $row->entity_id : $row->revision_id;
+          $id = ($load_current || empty($entity_type['entity_keys']['revision'])) ? $row->entity_id : $row->revision_id;
 
           if (!isset($return[$row->type][$id])) {
             $return[$row->type][$id] = (object) array('entity_id' => $row->entity_id, 'revision_id' => $row->revision_id, 'bundle' => $row->bundle);
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleKeyTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleKeyTestEntity.php
new file mode 100644
index 0000000..683bc48
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleKeyTestEntity.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\field_test\Plugin\Core\Entity\BundleKeyTestEntity.
+ */
+
+namespace Drupal\field_test\Plugin\Core\Entity;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Test entity class.
+ *
+ * @Plugin(
+ *   id = "test_entity_bundle_key",
+ *   label = @Translation("Test Entity with a bundle key"),
+ *   module = "field_test",
+ *   controller_class = "Drupal\field_test\TestEntityController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\field_test\TestEntityFormController"
+ *   },
+ *   field_cache = FALSE,
+ *   base_table = "test_entity_bundle_key",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "ftid",
+ *     "bundle" = "fttype"
+ *   }
+ * )
+ */
+class BundleKeyTestEntity extends TestEntity {
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleTestEntity.php
new file mode 100644
index 0000000..b31258e
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleTestEntity.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\field_test\Plugin\Core\Entity\BundleTestEntity.
+ */
+
+namespace Drupal\field_test\Plugin\Core\Entity;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Test entity class.
+ *
+ * @Plugin(
+ *   id = "test_entity_bundle",
+ *   label = @Translation("Test Entity with a specified bundle"),
+ *   module = "field_test",
+ *   controller_class = "Drupal\field_test\TestEntityController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\field_test\TestEntityFormController"
+ *   },
+ *   field_cache = FALSE,
+ *   base_table = "test_entity_bundle",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "ftid",
+ *     "bundle" = "fttype"
+ *   }
+ * )
+ */
+class BundleTestEntity extends TestEntity {
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/CacheableTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/CacheableTestEntity.php
new file mode 100644
index 0000000..12de950
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/CacheableTestEntity.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\field_test\Plugin\Core\Entity\CacheableTestEntity.
+ */
+
+namespace Drupal\field_test\Plugin\Core\Entity;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Test entity class.
+ *
+ * @Plugin(
+ *   id = "test_cacheable_entity",
+ *   label = @Translation("Test Entity, cacheable"),
+ *   module = "field_test",
+ *   controller_class = "Drupal\field_test\TestEntityController",
+ *   field_cache = TRUE,
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "ftid",
+ *     "revision" = "ftvid",
+ *     "bundle" = "fttype"
+ *   }
+ * )
+ */
+class CacheableTestEntity extends TestEntity {
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/LabelCallbackTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/LabelCallbackTestEntity.php
new file mode 100644
index 0000000..cddde9e
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/LabelCallbackTestEntity.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\field_test\Plugin\Core\Entity\LabelCallbackTestEntity.
+ */
+
+namespace Drupal\field_test\Plugin\Core\Entity;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Test entity class.
+ *
+ * @Plugin(
+ *   id = "test_entity_label_callback",
+ *   label = @Translation("Test entity label callback"),
+ *   module = "field_test",
+ *   controller_class = "Drupal\field_test\TestEntityController",
+ *   field_cache = FALSE,
+ *   base_table = "test_entity",
+ *   revision_table = "test_entity_revision",
+ *   label_callback = "field_test_entity_label_callback",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "ftid",
+ *     "revision" = "ftvid",
+ *     "bundle" = "fttype"
+ *   }
+ * )
+ */
+class LabelCallbackTestEntity extends TestEntity {
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/LabelTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/LabelTestEntity.php
new file mode 100644
index 0000000..7e2bdb2
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/LabelTestEntity.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\field_test\Plugin\Core\Entity\LabelTestEntity.
+ */
+
+namespace Drupal\field_test\Plugin\Core\Entity;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Test entity class.
+ *
+ * @Plugin(
+ *   id = "test_entity_label",
+ *   label = @Translation("Test Entity label"),
+ *   module = "field_test",
+ *   controller_class = "Drupal\field_test\TestEntityController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\field_test\TestEntityFormController"
+ *   },
+ *   field_cache = FALSE,
+ *   base_table = "test_entity",
+ *   revision_table = "test_entity_revision",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "ftid",
+ *     "revision" = "ftvid",
+ *     "bundle" = "fttype",
+ *     "label" = "ftlabel"
+ *   }
+ * )
+ */
+class LabelTestEntity extends TestEntity {
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/NoLabelTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/NoLabelTestEntity.php
new file mode 100644
index 0000000..4802e5e
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/NoLabelTestEntity.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\field_test\Plugin\Core\Entity\NoLabelTestEntity.
+ */
+
+namespace Drupal\field_test\Plugin\Core\Entity;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Test entity class.
+ *
+ * @Plugin(
+ *   id = "test_entity_no_label",
+ *   label = @Translation("Test Entity without label"),
+ *   module = "field_test",
+ *   controller_class = "Drupal\field_test\TestEntityController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\field_test\TestEntityFormController"
+ *   },
+ *   field_cache = FALSE,
+ *   base_table = "test_entity",
+ *   revision_table = "test_entity_revision",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "ftid",
+ *     "revision" = "ftvid",
+ *     "bundle" = "fttype"
+ *   }
+ * )
+ */
+class NoLabelTestEntity extends TestEntity {
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/TestEntity.php
similarity index 53%
rename from core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php
rename to core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/TestEntity.php
index 25898c8..a5911e9 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/TestEntity.php
@@ -1,16 +1,37 @@
 <?php
 
-/*
+/**
  * @file
- * Definition of Drupal\field_test\TestEntity.
+ * Contains Drupal\field_test\Plugin\Core\Entity\TestEntity.
  */
 
-namespace Drupal\field_test;
+namespace Drupal\field_test\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Test entity class.
+ *
+ * @Plugin(
+ *   id = "test_entity",
+ *   label = @Translation("Test Entity"),
+ *   module = "field_test",
+ *   controller_class = "Drupal\field_test\TestEntityController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\field_test\TestEntityFormController"
+ *   },
+ *   field_cache = FALSE,
+ *   base_table = "test_entity",
+ *   revision_table = "test_entity_revision",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "ftid",
+ *     "revision" = "ftvid",
+ *     "bundle" = "fttype"
+ *   }
+ * )
  */
 class TestEntity extends Entity {
 
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index 0e92ef0..fd1bdc3 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -169,7 +169,7 @@ function field_ui_menu() {
           // view modes, and use an access callback to determine which ones are
           // actually visible for a given bundle.
           $weight = 0;
-          $view_modes = array('default' => array('label' => t('Default'))) + $entity_info['view modes'];
+          $view_modes = array('default' => array('label' => t('Default'))) + $entity_info['view_modes'];
           foreach ($view_modes as $view_mode => $view_mode_info) {
             $items["$path/display/$view_mode"] = array(
               'title' => $view_mode_info['label'],
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
index 086f6ee..a5e30eb 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
@@ -333,7 +333,7 @@ public function form(array $form, array &$form_state) {
     // Custom display settings.
     if ($this->view_mode == 'default') {
       $entity_info = entity_get_info($this->entity_type);
-      $view_modes = $entity_info['view modes'];
+      $view_modes = $entity_info['view_modes'];
       // Only show the settings if there is more than one view mode.
       if (count($view_modes) > 1) {
         $form['modes'] = array(
@@ -460,7 +460,7 @@ public function submit(array $form, array &$form_state) {
         // settings.
         $view_mode_settings = field_view_mode_settings($this->entity_type, $this->bundle);
         if (!empty($value) && empty($view_mode_settings[$view_mode_name]['custom_settings'])) {
-          $view_mode_label = $entity_info['view modes'][$view_mode_name]['label'];
+          $view_mode_label = $entity_info['view_modes'][$view_mode_name]['label'];
           $path = _field_ui_bundle_admin_path($this->entity_type, $this->bundle) . "/display/$view_mode_name";
           drupal_set_message(t('The %view_mode mode now uses custom display settings. You might want to <a href="@url">configure them</a>.', array('%view_mode' => $view_mode_label, '@url' => url($path))));
           // Initialize the newly customized view mode with the display settings
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageDisplayTest.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageDisplayTest.php
index 96f59a2..a1c854f 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageDisplayTest.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageDisplayTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\field_ui\Tests;
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Tests the functionality of the 'Manage display' screens.
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 4e6c8eb..4588d4d 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -6,7 +6,7 @@
  */
 
 use Drupal\Core\Entity\EntityFieldQuery;
-use Drupal\file\File;
+use Drupal\file\Plugin\Core\Entity\File;
 use Drupal\Core\Template\Attribute;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Drupal\file\FileUsage\DatabaseFileUsageBackend;
@@ -88,30 +88,15 @@ function file_element_info() {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_info_alter().
  */
-function file_entity_info() {
-  return array(
-    'file' => array(
-      'label' => t('File'),
-      'base table' => 'file_managed',
-      'controller class' => 'Drupal\file\FileStorageController',
-      'entity class' => 'Drupal\file\File',
-      'entity keys' => array(
-        'id' => 'fid',
-        'label' => 'filename',
-        'uuid' => 'uuid',
-      ),
-      'view modes' => array(
-        'full' => array(
-          'label' => t('File default'),
-          'custom settings' => FALSE,
-        ),
-      ),
-      'static cache' => FALSE,
-    ),
+function file_entity_info_alter(&$info) {
+  $info['file']['view_modes']['full'] = array(
+    'label' => t('File default'),
+    'custom settings' => FALSE,
   );
 }
+
 /**
  * Loads file entities from the database.
  *
diff --git a/core/modules/file/file.views.inc b/core/modules/file/file.views.inc
index 387b344..f080aa1 100644
--- a/core/modules/file/file.views.inc
+++ b/core/modules/file/file.views.inc
@@ -500,8 +500,8 @@ function file_field_views_data_views_data_alter(&$data, $field) {
       'field_name' => $field['field_name'],
       'field table' => _field_sql_storage_tablename($field),
       'field field' => $field['field_name'] . '_fid',
-      'base' => $entity_info['base table'],
-      'base field' => $entity_info['entity keys']['id'],
+      'base' => $entity_info['base_table'],
+      'base field' => $entity_info['entity_keys']['id'],
       'label' => t('!field_name', array('!field_name' => $field['field_name'])),
       'join_extra' => array(
         0 => array(
diff --git a/core/modules/file/lib/Drupal/file/FileUsage/DatabaseFileUsageBackend.php b/core/modules/file/lib/Drupal/file/FileUsage/DatabaseFileUsageBackend.php
index ff3b0ae..eaca4bf 100644
--- a/core/modules/file/lib/Drupal/file/FileUsage/DatabaseFileUsageBackend.php
+++ b/core/modules/file/lib/Drupal/file/FileUsage/DatabaseFileUsageBackend.php
@@ -9,7 +9,7 @@
 
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Database\DatabaseExceptionWrapper;
-use Drupal\file\File;
+use Drupal\file\Plugin\Core\Entity\File;
 
 /**
  * Defines the database file usage backend. This is the default Drupal backend.
diff --git a/core/modules/file/lib/Drupal/file/FileUsage/FileUsageBase.php b/core/modules/file/lib/Drupal/file/FileUsage/FileUsageBase.php
index ae2c670..9ba2ac7 100644
--- a/core/modules/file/lib/Drupal/file/FileUsage/FileUsageBase.php
+++ b/core/modules/file/lib/Drupal/file/FileUsage/FileUsageBase.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\file\FileUsage;
 
-use Drupal\file\File;
+use Drupal\file\Plugin\Core\Entity\File;
 
 /**
  * Defines the base class for database file usage backend.
diff --git a/core/modules/file/lib/Drupal/file/FileUsage/FileUsageInterface.php b/core/modules/file/lib/Drupal/file/FileUsage/FileUsageInterface.php
index 4a4b231..d9a248d 100644
--- a/core/modules/file/lib/Drupal/file/FileUsage/FileUsageInterface.php
+++ b/core/modules/file/lib/Drupal/file/FileUsage/FileUsageInterface.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\file\FileUsage;
 
-use Drupal\file\File;
+use Drupal\file\Plugin\Core\Entity\File;
 
 /**
  * File usage backend interface.
diff --git a/core/modules/file/lib/Drupal/file/File.php b/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
similarity index 76%
rename from core/modules/file/lib/Drupal/file/File.php
rename to core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
index 6ecf1c1..d7f2a6b 100644
--- a/core/modules/file/lib/Drupal/file/File.php
+++ b/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
@@ -2,16 +2,32 @@
 
 /**
  * @file
- * Definition of Drupal\file\File.
+ * Definition of Drupal\file\Plugin\Core\Entity\File.
  */
 
-namespace Drupal\file;
+namespace Drupal\file\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the file entity class.
+ *
+ * @Plugin(
+ *   id = "file",
+ *   label = @Translation("File"),
+ *   module = "file",
+ *   controller_class = "Drupal\file\FileStorageController",
+ *   base_table = "file_managed",
+ *   static_cache = FALSE,
+ *   entity_keys = {
+ *     "id" = "fid",
+ *     "label" = "filename",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class File extends Entity implements ContentEntityInterface {
 
diff --git a/core/modules/file/tests/file_module_test.module b/core/modules/file/tests/file_module_test.module
index eae577e..b962e2a 100644
--- a/core/modules/file/tests/file_module_test.module
+++ b/core/modules/file/tests/file_module_test.module
@@ -6,7 +6,7 @@
  */
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\file\File;
+use Drupal\file\Plugin\Core\Entity\File;
 
 /**
  * Implements hook_menu().
diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module
index 46b8604..d4a4359 100644
--- a/core/modules/file/tests/file_test/file_test.module
+++ b/core/modules/file/tests/file_test/file_test.module
@@ -8,7 +8,7 @@
  * calling file_test_get_calls() or file_test_set_return().
  */
 
-use Drupal\file\File;
+use Drupal\file\Plugin\Core\Entity\File;
 
 const FILE_URL_TEST_CDN_1 = 'http://cdn1.example.com';
 const FILE_URL_TEST_CDN_2 = 'http://cdn2.example.com';
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 3ec8a41..459cbeb 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -5,8 +5,8 @@
  * Provides discussion forums.
  */
 
-use Drupal\node\Node;
-use Drupal\taxonomy\Term;
+use Drupal\node\Plugin\Core\Entity\Node;
+use Drupal\taxonomy\Plugin\Core\Entity\Term;
 
 /**
  * Implements hook_help().
@@ -220,8 +220,8 @@ function forum_menu_local_tasks_alter(&$data, $router_item, $root_path) {
 function forum_entity_info_alter(&$info) {
   // Take over URI construction for taxonomy terms that are forums.
   if ($vid = config('forum.settings')->get('vocabulary')) {
-    // Within hook_entity_info(), we can't invoke entity_load() as that would
-    // cause infinite recursion, so we call taxonomy_vocabulary_get_names()
+    // Within hook_entity_info_alter(), we can't invoke entity_load() as that
+    // would cause infinite recursion, so we call taxonomy_vocabulary_get_names()
     // instead of taxonomy_vocabulary_load(). All we need is the machine name
     // of $vid, so retrieving and iterating all the vocabulary names is somewhat
     // inefficient, but entity info is cached across page requests, and an
@@ -229,7 +229,7 @@ function forum_entity_info_alter(&$info) {
     // and is done as part of taxonomy_entity_info() anyway.
     foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
       if ($vid == $vocabulary->vid) {
-        $info['taxonomy_term']['bundles'][$machine_name]['uri callback'] = 'forum_uri';
+        $info['taxonomy_term']['bundles'][$machine_name]['uri_callback'] = 'forum_uri';
       }
     }
   }
diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
index 67e58a2..f56cfbb 100644
--- a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
+++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\forum\Tests;
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 use Drupal\simpletest\WebTestBase;
 
 /**
diff --git a/core/modules/image/image.api.php b/core/modules/image/image.api.php
index 1879a07..c11347f 100644
--- a/core/modules/image/image.api.php
+++ b/core/modules/image/image.api.php
@@ -74,7 +74,7 @@ function hook_image_effect_info_alter(&$effects) {
  * be cleared using this hook. This hook is called whenever a style is updated,
  * deleted, or any effect associated with the style is update or deleted.
  *
- * @param Drupal\image\ImageStyle $style
+ * @param Drupal\image\Plugin\Core\Entity\ImageStyle $style
  *   The image style array that is being flushed.
  */
 function hook_image_style_flush($style) {
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 5dab988..c763dc6 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -9,8 +9,8 @@
 use Symfony\Component\HttpFoundation\StreamedResponse;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Drupal\Component\Uuid\Uuid;
-use Drupal\file\File;
-use Drupal\image\ImageStyle;
+use Drupal\file\Plugin\Core\Entity\File;
+use Drupal\image\Plugin\Core\Entity\ImageStyle;
 
 /**
  * Image style constant for user presets in the database.
@@ -597,7 +597,7 @@ function image_image_style_update($style) {
 /**
  * Delete an image style.
  *
- * @param Drupal\image\ImageStyle $style
+ * @param Drupal\image\Plugin\Core\Entity\ImageStyle $style
  *   An image style array.
  * @param $replacement_style_name
  *   (optional) When deleting a style, specify a replacement style name so
@@ -1164,26 +1164,6 @@ function _image_effect_definitions_sort($a, $b) {
 }
 
 /**
- * Implements hook_entity_info().
- */
-function image_entity_info() {
-  return array(
-    'image_style' => array(
-      'label' => t('Image style'),
-      'entity class' => 'Drupal\image\ImageStyle',
-      'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
-      'uri callback' => 'image_style_uri',
-      'config prefix' => 'image.style',
-      'entity keys' => array(
-        'id' => 'name',
-        'label' => 'label',
-        'uuid' => 'uuid',
-      ),
-    ),
-  );
-}
-
-/**
  * URI callbacks for image styles.
  */
 function image_style_uri(ImageStyle $image_style) {
diff --git a/core/modules/image/image.views.inc b/core/modules/image/image.views.inc
index 43c04d6..77c4ea3 100644
--- a/core/modules/image/image.views.inc
+++ b/core/modules/image/image.views.inc
@@ -53,8 +53,8 @@ function image_field_views_data_views_data_alter(&$data, $field) {
       'field_name' => $field['field_name'],
       'field table' => _field_sql_storage_tablename($field),
       'field field' => $field['field_name'] . '_fid',
-      'base' => $entity_info['base table'],
-      'base field' => $entity_info['entity keys']['id'],
+      'base' => $entity_info['base_table'],
+      'base field' => $entity_info['entity_keys']['id'],
       'label' => t('!field_name', array('!field_name' => $field['field_name'])),
       'join_extra' => array(
         0 => array(
diff --git a/core/modules/image/lib/Drupal/image/ImageStyle.php b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
similarity index 51%
rename from core/modules/image/lib/Drupal/image/ImageStyle.php
rename to core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
index fae896f..363fbd8 100644
--- a/core/modules/image/lib/Drupal/image/ImageStyle.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
@@ -2,15 +2,31 @@
 
 /**
  * @file
- * Definition of Drupal\image\ImageStyle.
+ * Definition of Drupal\image\Plugin\Core\Entity\ImageStyle.
  */
 
-namespace Drupal\image;
+namespace Drupal\image\Plugin\Core\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines an image style configuration entity.
+ *
+ * @Plugin(
+ *   id = "image_style",
+ *   label = @Translation("Image style"),
+ *   module = "image",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   uri_callback = "image_style_uri",
+ *   config_prefix = "image.style",
+ *   entity_keys = {
+ *     "id" = "name",
+ *     "label" = "label",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class ImageStyle extends ConfigEntityBase {
 
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index a55660b..22f5dd7 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -11,7 +11,7 @@
  * URLs to be added to the main site navigation menu.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 use Symfony\Component\HttpFoundation\JsonResponse;
 
diff --git a/core/modules/node/lib/Drupal/node/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
similarity index 81%
rename from core/modules/node/lib/Drupal/node/Node.php
rename to core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
index 45d93f0..b828c71 100644
--- a/core/modules/node/lib/Drupal/node/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
@@ -2,16 +2,40 @@
 
 /**
  * @file
- * Definition of Drupal\node\Node.
+ * Definition of Drupal\node\Plugin\Core\Entity\Node.
  */
 
-namespace Drupal\node;
+namespace Drupal\node\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the node entity class.
+ *
+ * @Plugin(
+ *   id = "node",
+ *   label = @Translation("Node"),
+ *   module = "node",
+ *   controller_class = "Drupal\node\NodeStorageController",
+ *   render_controller_class = "Drupal\node\NodeRenderController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\node\NodeFormController"
+ *   },
+ *   base_table = "node",
+ *   revision_table = "node_revision",
+ *   uri_callback = "node_uri",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "nid",
+ *     "revision" = "vid",
+ *     "bundle" = "type",
+ *     "label" = "title",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class Node extends Entity implements ContentEntityInterface {
 
diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php b/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
index 8dddd32..31dab0b 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/views/row/Rss.php
@@ -66,8 +66,8 @@ public function buildOptionsForm(&$form, &$form_state) {
   public function buildOptionsForm_summary_options() {
     $entity_info = entity_get_info('node');
     $options = array();
-    if (!empty($entity_info['view modes'])) {
-      foreach ($entity_info['view modes'] as $mode => $settings) {
+    if (!empty($entity_info['view_modes'])) {
+      foreach ($entity_info['view_modes'] as $mode => $settings) {
         $options[$mode] = $settings['label'];
       }
     }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index a09aee1..7f90db6 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -15,8 +15,8 @@
 use Drupal\Core\Database\Query\SelectExtender;
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Template\Attribute;
-use Drupal\node\Node;
-use Drupal\file\File;
+use Drupal\node\Plugin\Core\Entity\Node;
+use Drupal\file\Plugin\Core\Entity\File;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
@@ -191,67 +191,40 @@ function node_cron() {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_info_alter().
  */
-function node_entity_info() {
-  $return = array(
-    'node' => array(
-      'label' => t('Node'),
-      'entity class' => 'Drupal\node\Node',
-      'controller class' => 'Drupal\node\NodeStorageController',
-      'form controller class' => array(
-        'default' => 'Drupal\node\NodeFormController',
-      ),
-      'base table' => 'node',
-      'revision table' => 'node_revision',
-      'uri callback' => 'node_uri',
-      'fieldable' => TRUE,
-      'entity keys' => array(
-        'id' => 'nid',
-        'revision' => 'vid',
-        'bundle' => 'type',
-        'label' => 'title',
-        'uuid' => 'uuid',
-      ),
-      'bundle keys' => array(
-        'bundle' => 'type',
-      ),
-      'bundles' => array(),
-      'render controller class' => 'Drupal\node\NodeRenderController',
-      'view modes' => array(
-        'full' => array(
-          'label' => t('Full content'),
-          'custom settings' => FALSE,
-        ),
-        'teaser' => array(
-          'label' => t('Teaser'),
-          'custom settings' => TRUE,
-        ),
-        'rss' => array(
-          'label' => t('RSS'),
-          'custom settings' => FALSE,
-        ),
-      ),
-    ),
+function node_entity_info_alter(&$info) {
+  $info['node']['bundle_keys'] = array(
+    'bundle' => 'type',
+  );
+  $info['node']['view_modes']['full'] = array(
+    'label' => t('Full content'),
+    'custom settings' => FALSE,
+  );
+  $info['node']['view_modes']['teaser'] = array(
+    'label' => t('Teaser'),
+    'custom settings' => TRUE,
+  );
+  $info['node']['view_modes']['rss'] = array(
+    'label' => t('RSS'),
+    'custom settings' => FALSE,
   );
 
   // Add a translation handler for fields if the language module is enabled.
   if (module_exists('language')) {
-    $return['node']['translation']['node'] = TRUE;
+    $info['node']['translation']['node'] = TRUE;
   }
 
   // Search integration is provided by node.module, so search-related
   // view modes for nodes are defined here and not in search.module.
   if (module_exists('search')) {
-    $return['node']['view modes'] += array(
-      'search_index' => array(
-        'label' => t('Search index'),
-        'custom settings' => FALSE,
-      ),
-      'search_result' => array(
-        'label' => t('Search result'),
-        'custom settings' => FALSE,
-      ),
+    $info['node']['view_modes']['search_index'] = array(
+      'label' => t('Search index'),
+      'custom settings' => FALSE,
+    );
+    $info['node']['view_modes']['search_result'] = array(
+      'label' => t('Search result'),
+      'custom settings' => FALSE,
     );
   }
 
@@ -259,7 +232,7 @@ function node_entity_info() {
   // messages, and the path to attach Field admin pages to.
   node_type_cache_reset();
   foreach (node_type_get_names() as $type => $name) {
-    $return['node']['bundles'][$type] = array(
+    $info['node']['bundles'][$type] = array(
       'label' => $name,
       'admin' => array(
         'path' => 'admin/structure/types/manage/%node_type',
@@ -269,8 +242,6 @@ function node_entity_info() {
       ),
     );
   }
-
-  return $return;
 }
 
 /**
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index a3afa7f..3a86060 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -9,7 +9,7 @@
  * @see node_menu()
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Page callback: Presents the node editing form.
diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module
index a503217..bcd5a71 100644
--- a/core/modules/node/tests/modules/node_access_test/node_access_test.module
+++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module
@@ -9,7 +9,7 @@
 
 use Drupal\Core\Entity\EntityFieldQuery;
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Implements hook_node_grants().
diff --git a/core/modules/node/tests/modules/node_test/node_test.module b/core/modules/node/tests/modules/node_test/node_test.module
index 8af5dfe..49da0a0 100644
--- a/core/modules/node/tests/modules/node_test/node_test.module
+++ b/core/modules/node/tests/modules/node_test/node_test.module
@@ -6,7 +6,7 @@
  * the Node module.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Implements hook_node_load().
diff --git a/core/modules/node/tests/modules/node_test_exception/node_test_exception.module b/core/modules/node/tests/modules/node_test_exception/node_test_exception.module
index 570236b..c5d1129 100644
--- a/core/modules/node/tests/modules/node_test_exception/node_test_exception.module
+++ b/core/modules/node/tests/modules/node_test_exception/node_test_exception.module
@@ -6,7 +6,7 @@
  * the Node module.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Implements hook_node_insert().
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index 6a69c13..942fbb6 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -5,9 +5,9 @@
  * Enables users to rename URLs.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
-use Drupal\taxonomy\Term;
+use Drupal\taxonomy\Plugin\Core\Entity\Term;
 
 /**
  * Implements hook_help().
diff --git a/core/modules/poll/poll.module b/core/modules/poll/poll.module
index c85f121..d3f8d3a 100644
--- a/core/modules/poll/poll.module
+++ b/core/modules/poll/poll.module
@@ -5,7 +5,7 @@
  * Collects votes on different topics in the form of multiple choice questions.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Implements hook_help().
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php
index 2a8fba3..b7771c6 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\rdf\Tests;
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 use Drupal\simpletest\WebTestBase;
 
 /**
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 315ae7a..a32b5c3 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -5,7 +5,7 @@
  * Enables site-wide keyword searching.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Matches all 'N' Unicode character classes (numbers)
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index 8469e52..f11f3ed 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -5,7 +5,7 @@
  * Logs and displays access statistics for a site.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Implements hook_help().
diff --git a/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php b/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
index d00edd1..62b725c 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
@@ -58,8 +58,8 @@ public function init(ViewExecutable $view, &$display, $options = NULL) {
     $this->entityType = $this->definition['entity_type'];
 
     $this->entityInfo = entity_get_info($this->entityType);
-    $this->base_table = $this->entityInfo['base table'];
-    $this->base_field = $this->entityInfo['entity keys']['id'];
+    $this->base_table = $this->entityInfo['base_table'];
+    $this->base_field = $this->entityInfo['entity_keys']['id'];
   }
 
   /**
@@ -93,8 +93,8 @@ public function buildOptionsForm(&$form, &$form_state) {
    */
   protected function buildViewModeOptions() {
     $options = array();
-    if (!empty($this->entityInfo['view modes'])) {
-      foreach ($this->entityInfo['view modes'] as $mode => $settings) {
+    if (!empty($this->entityInfo['view_modes'])) {
+      foreach ($this->entityInfo['view_modes'] as $mode => $settings) {
         $options[$mode] = $settings['label'];
       }
     }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
index 2af085b..fbebe94 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
@@ -33,6 +33,8 @@ function testEntityInfoChanges() {
     // Change the label of the test entity type and make sure changes appear
     // after flushing caches.
     variable_set('entity_cache_test_label', 'New label.');
+    $info = entity_get_info('entity_cache_test');
+    $this->assertEqual($info['label'], 'Entity Cache Test', 'Original label appears in cached entity info.');
     $this->resetAll();
     $info = entity_get_info('entity_cache_test');
     $this->assertEqual($info['label'], 'New label.', 'New label appears in entity info.');
@@ -52,6 +54,6 @@ function testEntityInfoCacheWatchdog() {
     module_enable(array('entity_cache_test'));
     $info = variable_get('entity_cache_test');
     $this->assertEqual($info['label'], 'Entity Cache Test', 'Entity info label is correct.');
-    $this->assertEqual($info['controller class'], 'Drupal\Core\Entity\DatabaseStorageController', 'Entity controller class info is correct.');
+    $this->assertEqual($info['controller_class'], 'Drupal\Core\Entity\DatabaseStorageController', 'Entity controller class info is correct.');
   }
 }
diff --git a/core/modules/system/tests/modules/bundle_test/lib/Drupal/bundle_test/TestFileUsage.php b/core/modules/system/tests/modules/bundle_test/lib/Drupal/bundle_test/TestFileUsage.php
index 12cc137..0ee5f2d 100644
--- a/core/modules/system/tests/modules/bundle_test/lib/Drupal/bundle_test/TestFileUsage.php
+++ b/core/modules/system/tests/modules/bundle_test/lib/Drupal/bundle_test/TestFileUsage.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\bundle_test;
 
-use Drupal\file\File;
+use Drupal\file\Plugin\Core\Entity\File;
 
 class TestFileUsage extends FileUsageBase {
 
diff --git a/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.module b/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.module
index 2d4b3be..6db46d7 100644
--- a/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.module
+++ b/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.module
@@ -6,12 +6,8 @@
  */
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_info_alter().
  */
-function entity_cache_test_dependency_entity_info() {
-  return array(
-    'entity_cache_test' => array(
-      'label' => variable_get('entity_cache_test_label', 'Entity Cache Test'),
-    ),
-  );
+function entity_cache_test_dependency_entity_info_alter(&$info) {
+  $info['entity_cache_test']['label'] = variable_get('entity_cache_test_label', 'Entity Cache Test');
 }
diff --git a/core/modules/system/tests/modules/entity_cache_test_dependency/lib/Drupal/entity_cache_test_dependency/Plugin/Core/Entity/EntityCacheTest.php b/core/modules/system/tests/modules/entity_cache_test_dependency/lib/Drupal/entity_cache_test_dependency/Plugin/Core/Entity/EntityCacheTest.php
new file mode 100644
index 0000000..c0fbea4
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_cache_test_dependency/lib/Drupal/entity_cache_test_dependency/Plugin/Core/Entity/EntityCacheTest.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\entity_cache_test_dependency\Plugin\Core\Entity\EntityCacheTest.
+ */
+
+namespace Drupal\entity_cache_test_dependency\Plugin\Core\Entity;
+
+use Drupal\Core\Entity\Entity;
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Defines the EntityCacheTest class.
+ *
+ * @Plugin(
+ *   id = "entity_cache_test",
+ *   module = "entity_cache_test_dependency"
+ * )
+ */
+class EntityCacheTest extends Entity {
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 46def82..43daa4a 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -6,29 +6,13 @@
  */
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_info_alter().
  */
-function entity_test_entity_info() {
-  $items['entity_test'] = array(
-    'label' => t('Test entity'),
-    'entity class' => 'Drupal\entity_test\EntityTest',
-    'controller class' => 'Drupal\entity_test\EntityTestStorageController',
-    'form controller class' => array(
-      'default' => 'Drupal\entity_test\EntityTestFormController',
-    ),
-    'base table' => 'entity_test',
-    'data table' => 'entity_test_property_data',
-    'fieldable' => TRUE,
-    'entity keys' => array(
-      'id' => 'id',
-      'uuid' => 'uuid',
-    ),
-  );
+function entity_test_entity_info_alter(&$info) {
   // Optionally specify a translation handler for testing translations.
   if (variable_get('entity_test_translation')) {
-    $items['entity_test']['translation']['entity_test'] = TRUE;
+    $info['entity_test']['translation']['entity_test'] = TRUE;
   }
-  return $items;
 }
 
 /**
@@ -98,7 +82,7 @@ function entity_test_edit($entity) {
  * @param bool $reset
  *   A boolean indicating that the internal cache should be reset.
  *
- * @return Drupal\entity_test\EntityTest
+ * @return Drupal\entity_test\Plugin\Core\Entity\EntityTest
  *   The loaded entity object, or FALSE if the entity cannot be loaded.
  */
 function entity_test_load($id, $reset = FALSE) {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
similarity index 58%
rename from core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTest.php
rename to core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
index 17e8470..240c5c7 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
@@ -2,15 +2,34 @@
 
 /**
  * @file
- * Definition of Drupal\entity_test\EntityTest.
+ * Definition of Drupal\entity_test\Plugin\Core\Entity\EntityTest.
  */
 
-namespace Drupal\entity_test;
+namespace Drupal\entity_test\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\EntityNG;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the test entity class.
+ *
+ * @Plugin(
+ *   id = "entity_test",
+ *   label = @Translation("Test entity"),
+ *   module = "entity_test",
+ *   controller_class = "Drupal\entity_test\EntityTestStorageController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\entity_test\EntityTestFormController"
+ *   },
+ *   base_table = "entity_test",
+ *   data_table = "entity_test_property_data",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class EntityTest extends EntityNG {
 
diff --git a/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module b/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module
index 63f6406..b2e3adb 100644
--- a/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module
+++ b/core/modules/system/tests/modules/taxonomy_test/taxonomy_test.module
@@ -7,7 +7,7 @@
  * @see Drupal\taxonomy\Tests\TaxonomyHooksTestCase::testTaxonomyTermHooks()
  */
 
-use Drupal\taxonomy\Term;
+use Drupal\taxonomy\Plugin\Core\Entity\Term;
 
 /**
  * Implements hook_taxonomy_term_load().
diff --git a/core/modules/system/tests/modules/theme_page_test/theme_page_test.module b/core/modules/system/tests/modules/theme_page_test/theme_page_test.module
index fe86da9..63f8cb4 100644
--- a/core/modules/system/tests/modules/theme_page_test/theme_page_test.module
+++ b/core/modules/system/tests/modules/theme_page_test/theme_page_test.module
@@ -18,4 +18,4 @@ function theme_page_test_system_theme_info() {
   $themes['test_invalid_basetheme'] = drupal_get_path('module', 'system') . '/tests/themes/test_invalid_basetheme/test_invalid_basetheme.info';
   $themes['test_invalid_engine'] = drupal_get_path('module', 'system') . '/tests/themes/test_invalid_engine/test_invalid_engine.info';
   return $themes;
-}
\ No newline at end of file
+}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
similarity index 71%
rename from core/modules/taxonomy/lib/Drupal/taxonomy/Term.php
rename to core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
index 42ad4c5..3b4b121 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
@@ -2,16 +2,38 @@
 
 /**
  * @file
- * Definition of Drupal\taxonomy\Term.
+ * Definition of Drupal\taxonomy\Plugin\Core\Entity\Term.
  */
 
-namespace Drupal\taxonomy;
+namespace Drupal\taxonomy\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the taxonomy term entity.
+ *
+ * @Plugin(
+ *   id = "taxonomy_term",
+ *   label = @Translation("Taxonomy term"),
+ *   module = "taxonomy",
+ *   controller_class = "Drupal\taxonomy\TermStorageController",
+ *   render_controller_class = "Drupal\taxonomy\TermRenderController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\taxonomy\TermFormController"
+ *   },
+ *   base_table = "taxonomy_term_data",
+ *   uri_callback = "taxonomy_term_uri",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "tid",
+ *     "bundle" = "vocabulary_machine_name",
+ *     "label" = "name",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class Term extends Entity implements ContentEntityInterface {
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Vocabulary.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
similarity index 64%
rename from core/modules/taxonomy/lib/Drupal/taxonomy/Vocabulary.php
rename to core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
index e32f285..beee8d1 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Vocabulary.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
@@ -2,15 +2,32 @@
 
 /**
  * @file
- * Definition of Drupal\taxonomy\Vocabulary.
+ * Definition of Drupal\taxonomy\Plugin\Core\Entity\Vocabulary.
  */
 
-namespace Drupal\taxonomy;
+namespace Drupal\taxonomy\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the taxonomy vocabulary entity.
+ *
+ * @Plugin(
+ *   id = "taxonomy_vocabulary",
+ *   label = @Translation("Taxonomy vocabulary"),
+ *   module = "taxonomy",
+ *   controller_class = "Drupal\taxonomy\VocabularyStorageController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\taxonomy\VocabularyFormController"
+ *   },
+ *   base_table = "taxonomy_vocabulary",
+ *   entity_keys = {
+ *     "id" = "vid",
+ *     "label" = "name"
+ *   }
+ * )
  */
 class Vocabulary extends Entity {
 
diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc
index 24c2039..3847ebf 100644
--- a/core/modules/taxonomy/taxonomy.admin.inc
+++ b/core/modules/taxonomy/taxonomy.admin.inc
@@ -5,8 +5,8 @@
  * Administrative page callbacks for the taxonomy module.
  */
 
-use Drupal\taxonomy\Term;
-use Drupal\taxonomy\Vocabulary;
+use Drupal\taxonomy\Plugin\Core\Entity\Term;
+use Drupal\taxonomy\Plugin\Core\Entity\Vocabulary;
 
 /**
  * Form builder to list and manage vocabularies.
@@ -131,7 +131,7 @@ function taxonomy_vocabulary_add() {
  * Display a tree of all the terms in a vocabulary, with options to edit
  * each one. The form is made drag and drop by the theme function.
  *
- * @param Drupal\taxonomy\Vocabulary $vocabulary
+ * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
  *   The taxonomy vocabulary entity to list terms for.
  *
  * @ingroup forms
diff --git a/core/modules/taxonomy/taxonomy.api.php b/core/modules/taxonomy/taxonomy.api.php
index 6657624..1eb6bf2 100644
--- a/core/modules/taxonomy/taxonomy.api.php
+++ b/core/modules/taxonomy/taxonomy.api.php
@@ -34,10 +34,10 @@ function hook_taxonomy_vocabulary_load(array $vocabularies) {
  * Modules implementing this hook can act on the vocabulary object before it is
  * inserted or updated.
  *
- * @param Drupal\taxonomy\Vocabulary $vocabulary
+ * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
  *   A taxonomy vocabulary entity.
  */
-function hook_taxonomy_vocabulary_presave(Drupal\taxonomy\Vocabulary $vocabulary) {
+function hook_taxonomy_vocabulary_presave(Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary) {
   $vocabulary->foo = 'bar';
 }
 
@@ -47,10 +47,10 @@ function hook_taxonomy_vocabulary_presave(Drupal\taxonomy\Vocabulary $vocabulary
  * Modules implementing this hook can act on the vocabulary object when saved
  * to the database.
  *
- * @param Drupal\taxonomy\Vocabulary $vocabulary
+ * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
  *   A taxonomy vocabulary entity.
  */
-function hook_taxonomy_vocabulary_insert(Drupal\taxonomy\Vocabulary $vocabulary) {
+function hook_taxonomy_vocabulary_insert(Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary) {
   if ($vocabulary->synonyms) {
     variable_set('taxonomy_' . $vocabulary->vid . '_synonyms', TRUE);
   }
@@ -61,10 +61,10 @@ function hook_taxonomy_vocabulary_insert(Drupal\taxonomy\Vocabulary $vocabulary)
  *
  * Modules implementing this hook can act on the vocabulary object when updated.
  *
- * @param Drupal\taxonomy\Vocabulary $vocabulary
+ * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
  *   A taxonomy vocabulary entity.
  */
-function hook_taxonomy_vocabulary_update(Drupal\taxonomy\Vocabulary $vocabulary) {
+function hook_taxonomy_vocabulary_update(Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary) {
   $status = $vocabulary->synonyms ? TRUE : FALSE;
   if ($vocabulary->synonyms) {
     variable_set('taxonomy_' . $vocabulary->vid . '_synonyms', $status);
@@ -78,13 +78,13 @@ function hook_taxonomy_vocabulary_update(Drupal\taxonomy\Vocabulary $vocabulary)
  * field_attach_delete_bundle() is called and before the vocabulary is actually
  * removed from the database.
  *
- * @param Drupal\taxonomy\Vocabulary $vocabulary
+ * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
  *   The taxonomy vocabulary entity that is about to be deleted.
  *
  * @see hook_taxonomy_vocabulary_delete()
  * @see taxonomy_vocabulary_delete()
  */
-function hook_taxonomy_vocabulary_predelete(Drupal\taxonomy\Vocabulary $vocabulary) {
+function hook_taxonomy_vocabulary_predelete(Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary) {
   if (variable_get('taxonomy_' . $vocabulary->vid . '_synonyms', FALSE)) {
     variable_del('taxonomy_' . $vocabulary->vid . '_synonyms');
   }
@@ -97,13 +97,13 @@ function hook_taxonomy_vocabulary_predelete(Drupal\taxonomy\Vocabulary $vocabula
  * field_attach_delete_bundle() has been called and after the vocabulary has
  * been removed from the database.
  *
- * @param Drupal\taxonomy\Vocabulary $vocabulary
+ * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
  *   The taxonomy vocabulary entity that has been deleted.
  *
  * @see hook_taxonomy_vocabulary_predelete()
  * @see taxonomy_vocabulary_delete()
  */
-function hook_taxonomy_vocabulary_delete(Drupal\taxonomy\Vocabulary $vocabulary) {
+function hook_taxonomy_vocabulary_delete(Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary) {
   if (variable_get('taxonomy_' . $vocabulary->vid . '_synonyms', FALSE)) {
     variable_del('taxonomy_' . $vocabulary->vid . '_synonyms');
   }
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 874c78a..ceba797 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -5,9 +5,9 @@
  * Enables the organization of content into categories.
  */
 
-use Drupal\node\Node;
-use Drupal\taxonomy\Term;
-use Drupal\taxonomy\Vocabulary;
+use Drupal\node\Plugin\Core\Entity\Node;
+use Drupal\taxonomy\Plugin\Core\Entity\Term;
+use Drupal\taxonomy\Plugin\Core\Entity\Vocabulary;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
@@ -106,42 +106,18 @@ function taxonomy_permission() {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_info_alter().
  */
-function taxonomy_entity_info() {
-  $return = array(
-    'taxonomy_term' => array(
-      'label' => t('Taxonomy term'),
-      'entity class' => 'Drupal\taxonomy\Term',
-      'controller class' => 'Drupal\taxonomy\TermStorageController',
-      'form controller class' => array(
-        'default' => 'Drupal\taxonomy\TermFormController',
-      ),
-      'base table' => 'taxonomy_term_data',
-      'uri callback' => 'taxonomy_term_uri',
-      'fieldable' => TRUE,
-      'entity keys' => array(
-        'id' => 'tid',
-        'bundle' => 'vocabulary_machine_name',
-        'label' => 'name',
-        'uuid' => 'uuid',
-      ),
-      'bundle keys' => array(
-        'bundle' => 'machine_name',
-      ),
-      'bundles' => array(),
-      'render controller class' => 'Drupal\taxonomy\TermRenderController',
-      'view modes' => array(
-        // @todo View mode for display as a field (when attached to nodes etc).
-        'full' => array(
-          'label' => t('Taxonomy term page'),
-          'custom settings' => FALSE,
-        ),
-      ),
-    ),
+function taxonomy_entity_info_alter(&$info) {
+  $info['taxonomy_term']['bundle_keys'] = array(
+    'bundle' => 'machine_name',
+  );
+  $info['taxonomy_term']['view_modes']['full'] = array(
+    'label' => t('Taxonomy term page'),
+    'custom settings' => FALSE,
   );
   foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) {
-    $return['taxonomy_term']['bundles'][$machine_name] = array(
+    $info['taxonomy_term']['bundles'][$machine_name] = array(
       'label' => $vocabulary->name,
       'admin' => array(
         'path' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name',
@@ -151,28 +127,10 @@ function taxonomy_entity_info() {
       ),
     );
   }
-  $return['taxonomy_vocabulary'] = array(
-    'label' => t('Taxonomy vocabulary'),
-    'entity class' => 'Drupal\taxonomy\Vocabulary',
-    'controller class' => 'Drupal\taxonomy\VocabularyStorageController',
-    'form controller class' => array(
-      'default' => 'Drupal\taxonomy\VocabularyFormController',
-    ),
-    'base table' => 'taxonomy_vocabulary',
-    'entity keys' => array(
-      'id' => 'vid',
-      'label' => 'name',
-    ),
-    'fieldable' => FALSE,
-    'view modes' => array(
-      'full' => array(
-        'label' => t('Taxonomy vocabulary default'),
-        'custom settings' => FALSE,
-      ),
-    ),
+  $info['taxonomy_vocabulary']['view_modes']['full'] = array(
+    'label' => t('Taxonomy vocabulary default'),
+    'custom settings' => FALSE,
   );
-
-  return $return;
 }
 
 /**
@@ -443,7 +401,7 @@ function taxonomy_term_access($op, $term) {
 /**
  * Saves a vocabulary.
  *
- * @param Drupal\taxonomy\Vocabulary $vocabulary
+ * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
  *   The taxonomy vocabulary entity to be saved.
  */
 function taxonomy_vocabulary_save(Vocabulary $vocabulary) {
@@ -507,7 +465,7 @@ function taxonomy_taxonomy_vocabulary_update(Vocabulary $vocabulary) {
  * term has multiple parents then the vocabulary will be given a hierarchy of
  * TAXONOMY_HIERARCHY_MULTIPLE.
  *
- * @param Drupal\taxonomy\Vocabulary $vocabulary
+ * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
  *   A taxonomy vocabulary entity.
  * @param $changed_term
  *   An array of the term structure that was updated.
@@ -972,7 +930,7 @@ function taxonomy_vocabulary_load_multiple(array $vids = NULL) {
  * @param int $vid
  *   The vocabulary's ID.
  *
- * @return Drupal\taxonomy\Vocabulary|false
+ * @return Drupal\taxonomy\Plugin\Core\Entity\Vocabulary|false
  *   The taxonomy vocabulary entity, if exists, FALSE otherwise. Results are
  *   statically cached.
  *
@@ -988,7 +946,7 @@ function taxonomy_vocabulary_load($vid) {
  * @param $name
  *   The vocabulary's machine name.
  *
- * @return Drupal\taxonomy\Vocabulary|false
+ * @return Drupal\taxonomy\Plugin\Core\Entity\Vocabulary|false
  *   The taxonomy vocabulary entity, if exists, FALSE otherwise. Results are
  *   statically cached.
  *
diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc
index fe29aaf..f24dba9 100644
--- a/core/modules/taxonomy/taxonomy.pages.inc
+++ b/core/modules/taxonomy/taxonomy.pages.inc
@@ -5,8 +5,8 @@
  * Page callbacks for the taxonomy module.
  */
 
-use Drupal\taxonomy\Term;
-use Drupal\taxonomy\Vocabulary;
+use Drupal\taxonomy\Plugin\Core\Entity\Term;
+use Drupal\taxonomy\Plugin\Core\Entity\Vocabulary;
 use Symfony\Component\HttpFoundation\JsonResponse;
 
 /**
diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc
index e3f6e18..57ef50c 100644
--- a/core/modules/taxonomy/taxonomy.views.inc
+++ b/core/modules/taxonomy/taxonomy.views.inc
@@ -455,8 +455,8 @@ function taxonomy_field_views_data_views_data_alter(&$data, $field) {
       'field_name' => $field['field_name'],
       'field table' => _field_sql_storage_tablename($field),
       'field field' => $field['field_name'] . '_tid',
-      'base' => $entity_info['base table'],
-      'base field' => $entity_info['entity keys']['id'],
+      'base' => $entity_info['base_table'],
+      'base field' => $entity_info['entity_keys']['id'],
       'label' => t('!field_name', array('!field_name' => $field['field_name'])),
       'join_extra' => array(
         0 => array(
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index 301ea8f..92f5701 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -5,7 +5,7 @@
  * Tracks recent content posted by a user or users.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Implements hook_help().
diff --git a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
index ca65380..bc9094d 100644
--- a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
+++ b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\translation\Tests;
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 use Drupal\simpletest\WebTestBase;
 
 /**
diff --git a/core/modules/translation/tests/translation_test.module b/core/modules/translation/tests/translation_test.module
index 1bd0659..8c9fdf2 100644
--- a/core/modules/translation/tests/translation_test.module
+++ b/core/modules/translation/tests/translation_test.module
@@ -5,7 +5,7 @@
  * Mock module for content translation tests.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Implements hook_node_insert().
diff --git a/core/modules/translation/translation.module b/core/modules/translation/translation.module
index e7f22fd..5a67213 100644
--- a/core/modules/translation/translation.module
+++ b/core/modules/translation/translation.module
@@ -19,7 +19,7 @@
  *   date (0) or needs to be updated (1).
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Implements hook_help().
diff --git a/core/modules/translation/translation.pages.inc b/core/modules/translation/translation.pages.inc
index e33f53e..cc94a1e 100644
--- a/core/modules/translation/translation.pages.inc
+++ b/core/modules/translation/translation.pages.inc
@@ -5,7 +5,7 @@
  * User page callbacks for the Translation module.
  */
 
-use Drupal\node\Node;
+use Drupal\node\Plugin\Core\Entity\Node;
 
 /**
  * Page callback: Displays a list of a node's translations.
diff --git a/core/modules/user/lib/Drupal/user/User.php b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
similarity index 75%
rename from core/modules/user/lib/Drupal/user/User.php
rename to core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
index 2217588..4fa5b12 100644
--- a/core/modules/user/lib/Drupal/user/User.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
@@ -2,15 +2,37 @@
 
 /**
  * @file
- * Definition of Drupal\user\User.
+ * Definition of Drupal\user\Plugin\Core\Entity\User.
  */
 
-namespace Drupal\user;
+namespace Drupal\user\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
  * Defines the user entity class.
+ *
+ * @Plugin(
+ *   id = "user",
+ *   label = @Translation("User"),
+ *   module = "user",
+ *   controller_class = "Drupal\user\UserStorageController",
+ *   render_controller_class = "Drupal\user\UserRenderController",
+ *   form_controller_class = {
+ *     "profile" = "Drupal\user\ProfileFormController",
+ *     "register" = "Drupal\user\RegisterFormController"
+ *   },
+ *   base_table = "users",
+ *   uri_callback = "user_uri",
+ *   label_callback = "user_label",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "uid",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
 class User extends Entity {
 
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index b7ac23d..8b05731 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -2,7 +2,7 @@
 
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\file\File;
+use Drupal\file\Plugin\Core\Entity\File;
 use Drupal\Core\Template\Attribute;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
@@ -139,44 +139,20 @@ function user_theme() {
 }
 
 /**
- * Implements hook_entity_info().
+ * Implements hook_entity_info_alter().
  */
-function user_entity_info() {
-  return array(
-    'user' => array(
-      'label' => t('User'),
-      'controller class' => 'Drupal\user\UserStorageController',
-      'form controller class' => array(
-        'profile' => 'Drupal\user\ProfileFormController',
-        'register' => 'Drupal\user\RegisterFormController',
-      ),
-      'base table' => 'users',
-      'uri callback' => 'user_uri',
-      'label callback' => 'user_label',
-      'fieldable' => TRUE,
-      'entity class' => 'Drupal\user\User',
-      'entity keys' => array(
-        'id' => 'uid',
-        'uuid' => 'uuid',
-      ),
-      'bundles' => array(
-        'user' => array(
-          'label' => t('User'),
-          'admin' => array(
-            'path' => 'admin/config/people/accounts',
-            'access arguments' => array('administer users'),
-          ),
-        ),
-      ),
-      'render controller class' => 'Drupal\user\UserRenderController',
-      'view modes' => array(
-        'full' => array(
-          'label' => t('User account'),
-          'custom settings' => FALSE,
-        ),
-      ),
+function user_entity_info_alter(&$info) {
+  $info['user']['bundles']['user'] = array(
+    'label' => t('User'),
+    'admin' => array(
+      'path' => 'admin/config/people/accounts',
+      'access arguments' => array('administer users'),
     ),
   );
+  $info['user']['view_modes']['full'] = array(
+    'label' => t('User account'),
+    'custom settings' => FALSE,
+  );
 }
 
 /**
diff --git a/core/modules/views/lib/Drupal/views/ViewStorage.php b/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
similarity index 91%
rename from core/modules/views/lib/Drupal/views/ViewStorage.php
rename to core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
index 5b06a41..409128e 100644
--- a/core/modules/views/lib/Drupal/views/ViewStorage.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
@@ -2,18 +2,37 @@
 
 /**
  * @file
- * Definition of Drupal\views\ViewStorage.
+ * Definition of Drupal\views\Plugin\Core\Entity\View.
  */
 
-namespace Drupal\views;
+namespace Drupal\views\Plugin\Core\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\views_ui\ViewUI;
+use Drupal\views\ViewStorageInterface;
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
 
 /**
- * Defines a ViewStorage configuration entity class.
+ * Defines a View configuration entity class.
+ *
+ * @Plugin(
+ *   id = "view",
+ *   label = @Translation("View"),
+ *   module = "views",
+ *   controller_class = "Drupal\views\ViewStorageController",
+ *   list_controller_class = "Drupal\views_ui\ViewListController",
+ *   config_prefix = "views.view",
+ *   fieldable = FALSE,
+ *   entity_keys = {
+ *     "id" = "name",
+ *     "label" = "human_name",
+ *     "uuid" = "uuid"
+ *   }
+ * )
  */
-class ViewStorage extends ConfigEntityBase implements ViewStorageInterface {
+class View extends ConfigEntityBase implements ViewStorageInterface {
 
   /**
    * The name of the base table this view will use.
@@ -171,9 +190,8 @@ public function getModule() {
    * Overrides Drupal\Core\Entity\EntityInterface::uri().
    */
   public function uri() {
-    $info = $this->entityInfo();
     return array(
-      'path' => $info['list path'],
+      'path' => 'admin/structure/views/view/' . $this->id(),
     );
   }
 
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 8cda2a9..eabea9d 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -1519,7 +1519,7 @@ public function buildOptionsForm(&$form, &$form_state) {
         $entity_tables = array();
         $has_translation_handlers = FALSE;
         foreach ($entities as $type => $entity_info) {
-          $entity_tables[] = $entity_info['base table'];
+          $entity_tables[] = $entity_info['base_table'];
 
           if (!empty($entity_info['translation'])) {
             $has_translation_handlers = TRUE;
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
index ab74e1e..991d38e 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
@@ -1327,7 +1327,7 @@ public function query($get_count = FALSE) {
 
       foreach ($entity_tables as $table_alias => $table) {
         $info = entity_get_info($table['entity_type']);
-        $base_field = empty($table['revision']) ? $info['entity keys']['id'] : $info['entity keys']['revision'];
+        $base_field = empty($table['revision']) ? $info['entity_keys']['id'] : $info['entity_keys']['revision'];
         $this->add_field($table_alias, $base_field, '', $params);
       }
     }
@@ -1598,7 +1598,7 @@ function load_entities(&$results) {
     foreach ($entity_tables as $table_alias => $table) {
       $entity_type = $table['entity_type'];
       $info = entity_get_info($entity_type);
-      $id_key = empty($table['revision']) ? $info['entity keys']['id'] : $info['entity keys']['revision'];
+      $id_key = empty($table['revision']) ? $info['entity_keys']['id'] : $info['entity_keys']['revision'];
       $id_alias = $this->get_field_alias($table_alias, $id_key);
 
       foreach ($results as $index => $result) {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
index 0219788..acc1285 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\views\Plugin\views\wizard;
 
-use Drupal\views\ViewStorage;
+use Drupal\views\Plugin\Core\Entity\View;
 use Drupal\views_ui\ViewUI;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\PluginBase;
@@ -119,7 +119,7 @@ public function __construct(array $configuration, $plugin_id, DiscoveryInterface
 
     $entities = entity_get_info();
     foreach ($entities as $entity_type => $entity_info) {
-      if (isset($entity_info['base table']) && $this->base_table == $entity_info['base table']) {
+      if (isset($entity_info['base_table']) && $this->base_table == $entity_info['base_table']) {
         $this->entity_info = $entity_info;
         $this->entity_type = $entity_type;
       }
@@ -482,7 +482,7 @@ protected function build_filters(&$form, &$form_state) {
 
     $entity_info = $this->entity_info;
     // If the current base table support bundles and has more than one (like user).
-    if (isset($entity_info['bundle keys']) && isset($entity_info['bundles'])) {
+    if (isset($entity_info['bundle_keys']) && isset($entity_info['bundles'])) {
       // Get all bundles and their human readable names.
       $options = array('all' => t('All'));
       foreach ($entity_info['bundles'] as $type => $bundle) {
@@ -619,7 +619,7 @@ protected function alter_display_options(&$display_options, $form, $form_state)
   /**
    * Adds the array of display options to the view, with appropriate overrides.
    */
-  protected function addDisplays(ViewStorage $view, $display_options, $form, $form_state) {
+  protected function addDisplays(View $view, $display_options, $form, $form_state) {
     // Display: Master
     $default_display = $view->newDisplay('default', 'Master', 'default');
     foreach ($display_options['default'] as $option => $value) {
@@ -742,7 +742,7 @@ protected function default_display_filters_user(array $form, array &$form_state)
     $filters = array();
 
     if (!empty($form_state['values']['show']['type']) && $form_state['values']['show']['type'] != 'all') {
-      $bundle_key = $this->entity_info['bundle keys']['bundle'];
+      $bundle_key = $this->entity_info['bundle_keys']['bundle'];
       // Figure out the table where $bundle_key lives. It may not be the same as
       // the base table for the view; the taxonomy vocabulary machine_name, for
       // example, is stored in taxonomy_vocabulary, not taxonomy_term_data.
diff --git a/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php b/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
index e2365a2..5bf1574 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
@@ -196,8 +196,8 @@ function testStatusFunctions() {
    * Helper to return an expected views option array.
    *
    * @param array $views
-   *   An array of Drupal\views\ViewStorage objects to create an options array
-   *   for.
+   *   An array of Drupal\views\Plugin\Core\Entity\View objects for which to
+   *   create an options array.
    *
    * @return array
    *   A formatted options array that matches the expected output.
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php
index a54bbd5..8db3eab 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php
@@ -8,13 +8,13 @@
 namespace Drupal\views\Tests;
 
 use Drupal\views\ViewStorageController;
-use Drupal\views\ViewStorage;
+use Drupal\views\Plugin\Core\Entity\View;
 use Drupal\views\Plugin\views\display\Page;
 use Drupal\views\Plugin\views\display\DefaultDisplay;
 use Drupal\views\Plugin\views\display\Feed;
 
 /**
- * Tests the functionality of ViewStorage and ViewStorageController.
+ * Tests the functionality of View and ViewStorageController.
  *
  * @see Drupal\views\ViewStorage
  * @see Drupal\views\ViewStorageController
@@ -63,7 +63,7 @@ class ViewStorageTest extends ViewTestBase {
   public static function getInfo() {
     return array(
       'name' => 'Configuration entity CRUD tests',
-      'description' => 'Tests the CRUD functionality for ViewStorage.',
+      'description' => 'Tests the CRUD functionality for View.',
       'group' => 'Views',
     );
   }
@@ -103,7 +103,7 @@ protected function loadTests() {
 
     // Confirm that an actual view object is loaded and that it returns all of
     // expected properties.
-    $this->assertTrue($view instanceof ViewStorage, 'Single View instance loaded.');
+    $this->assertTrue($view instanceof View, 'Single View instance loaded.');
     foreach ($this->config_properties as $property) {
       $this->assertTrue(isset($view->{$property}), format_string('Property: @property loaded onto View.', array('@property' => $property)));
     }
@@ -155,7 +155,7 @@ protected function createTests() {
     // Create a new View instance with empty values.
     $created = $this->controller->create(array());
 
-    $this->assertTrue($created instanceof ViewStorage, 'Created object is a View.');
+    $this->assertTrue($created instanceof View, 'Created object is a View.');
     // Check that the View contains all of the properties.
     foreach ($this->config_properties as $property) {
       $this->assertTrue(property_exists($created, $property), format_string('Property: @property created on View.', array('@property' => $property)));
@@ -165,7 +165,7 @@ protected function createTests() {
     $values = config('views.view.glossary')->get();
     $created = $this->controller->create($values);
 
-    $this->assertTrue($created instanceof ViewStorage, 'Created object is a View.');
+    $this->assertTrue($created instanceof View, 'Created object is a View.');
     // Check that the View contains all of the properties.
     $properties = $this->config_properties;
     // Remove display from list.
@@ -339,7 +339,7 @@ protected function displayMethodTests() {
 
     $this->assertEqual($view->getPaths(), $expected_paths, 'Make sure the paths in the ui are generated as expected.');
 
-    // Tests Drupal\views\ViewStorage::addDisplay()
+    // Tests Drupal\views\Plugin\Core\Entity\View::addDisplay()
     $view = $this->controller->create(array());
     $random_title = $this->randomName();
 
@@ -355,7 +355,7 @@ protected function displayMethodTests() {
     $id = $view->addDisplay('page');
     $this->assertEqual($view->display[$id]['display_title'], 'Page 3');
 
-    // Tests Drupal\views\ViewStorage::generateDisplayId().
+    // Tests Drupal\views\Plugin\Core\Entity\View::generateDisplayId().
     // @todo Sadly this method is not public so it cannot be tested.
     // $view = $this->controller->create(array());
     // $this->assertEqual($view->generateDisplayId('default'), 'default', 'The plugin ID for default is always default.');
@@ -363,7 +363,7 @@ protected function displayMethodTests() {
     // $view->addDisplay('feed', 'feed title');
     // $this->assertEqual($view->generateDisplayId('feed'), 'feed_2', 'The generated ID for the first instance of a plugin type should have an suffix of _2.');
 
-    // Tests Drupal\views\ViewStorage::newDisplay().
+    // Tests Drupal\views\Plugin\Core\Entity\View::newDisplay().
     $view = $this->controller->create(array());
     $view->newDisplay('default');
 
@@ -438,7 +438,7 @@ public function testCreateDuplicate() {
     $view = views_get_view('archive');
     $copy = $view->createDuplicate();
 
-    $this->assertTrue($copy instanceof ViewStorage, 'The copied object is a View.');
+    $this->assertTrue($copy instanceof View, 'The copied object is a View.');
 
     // Check that the original view and the copy have different UUIDs.
     $this->assertNotIdentical($view->storage->uuid(), $copy->uuid(), 'The copied view has a new UUID.');
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php
index 0da4c1f..0354f9b 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php
@@ -418,7 +418,7 @@ protected function createViewFromConfig($view_name) {
   /**
    * Clones the view used in this test and sets the default display.
    *
-   * @param Drupal\views\ViewStorage $original_view
+   * @param Drupal\views\Plugin\Core\Entity\View $original_view
    *   (optional) The view to clone. If not specified, the default view for the
    *   test will be used.
    *
diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php
index b295b9e..0367905 100644
--- a/core/modules/views/lib/Drupal/views/ViewExecutable.php
+++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php
@@ -8,6 +8,7 @@
 namespace Drupal\views;
 
 use Symfony\Component\HttpFoundation\Response;
+use Drupal\views\Plugin\Core\Entity\View;
 
 /**
  * @defgroup views_objects Objects that represent a View or part of a view
@@ -25,7 +26,7 @@ class ViewExecutable {
   /**
    * The config entity in which the view is stored.
    *
-   * @var Drupal\views\ViewStorage
+   * @var Drupal\views\Plugin\Core\Entity\View
    */
   public $storage;
 
@@ -399,10 +400,10 @@ class ViewExecutable {
   /**
    * Constructs a new ViewExecutable object.
    *
-   * @param Drupal\views\ViewStorage $storage
+   * @param Drupal\views\Plugin\Core\Entity\View $storage
    *   The view config entity the actual information is stored on.
    */
-  public function __construct(ViewStorage $storage) {
+  public function __construct(View $storage) {
     // Reference the storage and the executable to each other.
     $this->storage = $storage;
     $this->storage->setExecutable($this);
diff --git a/core/modules/views/lib/Drupal/views/ViewStorageController.php b/core/modules/views/lib/Drupal/views/ViewStorageController.php
index 007b9ff..75b4884 100644
--- a/core/modules/views/lib/Drupal/views/ViewStorageController.php
+++ b/core/modules/views/lib/Drupal/views/ViewStorageController.php
@@ -12,7 +12,7 @@
 use Drupal\Component\Uuid\Uuid;
 
 /**
- * Defines the storage controller class for ViewStorage entities.
+ * Defines the storage controller class for View entities.
  */
 class ViewStorageController extends ConfigStorageController {
 
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 52de65c..f6568a4 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -12,7 +12,7 @@
 use Drupal\Core\Database\Query\AlterableInterface;
 use Drupal\views\ViewExecutable;
 use Drupal\Component\Plugin\Exception\PluginException;
-use Drupal\views\ViewStorage;
+use Drupal\views\Plugin\Core\Entity\View;
 
 /**
  * Views API version string.
@@ -20,33 +20,6 @@
 const VIEWS_API_VERSION = '3.0';
 
 /**
- * Implements hook_entity_info().
- */
-function views_entity_info() {
-  $return = array(
-    'view' => array(
-      'label' => t('View'),
-      'entity class' => 'Drupal\views\ViewStorage',
-      'controller class' => 'Drupal\views\ViewStorageController',
-      'list controller class' => 'Drupal\views_ui\ViewListController',
-      'list path' => 'admin/structure/views',
-      'form controller class' => array(
-        'default' => 'Drupal\node\NodeFormController',
-      ),
-      'config prefix' => 'views.view',
-      'fieldable' => FALSE,
-      'entity keys' => array(
-        'id' => 'name',
-        'label' => 'human_name',
-        'uuid' => 'uuid',
-      ),
-    ),
-  );
-
-  return $return;
-}
-
-/**
  * Implements hook_forms().
  *
  * To provide distinct form IDs for Views forms, the View name and
@@ -1350,7 +1323,7 @@ function views_get_enabled_display_extenders() {
 /**
  * Create an empty view to work with.
  *
- * @return Drupal\views\ViewStorage
+ * @return Drupal\views\Plugin\Core\Entity\View
  *   A fully formed, empty $view object. This object must be populated before
  *   it can be successfully saved.
  */
@@ -1361,7 +1334,7 @@ function views_new_view() {
 /**
  * Creates a view from an array of values.
  *
- * @return Drupal\views\ViewStorage
+ * @return Drupal\views\Plugin\Core\Entity\View
  *   A fully formed $view object with properties from the values passed in.
  */
 function views_create_view(array $values = array()) {
@@ -1526,46 +1499,46 @@ function views_get_views_as_options($views_only = FALSE, $filter = 'all', $exclu
 /**
  * Returns whether the view is enabled.
  *
- * @param Drupal\views\ViewStorage $view
+ * @param Drupal\views\Plugin\Core\Entity\View $view
  *   The view object to check.
  *
  * @return bool
  *   Returns TRUE if a view is enabled, FALSE otherwise.
  */
-function views_view_is_enabled(ViewStorage $view) {
+function views_view_is_enabled(View $view) {
   return $view->isEnabled();
 }
 
 /**
  * Returns whether the view is disabled.
  *
- * @param Drupal\views\ViewStorage $view
+ * @param Drupal\views\Plugin\Core\Entity\View $view
  *   The view object to check.
  *
  * @return bool
  *   Returns TRUE if a view is disabled, FALSE otherwise.
  */
-function views_view_is_disabled(ViewStorage $view) {
+function views_view_is_disabled(View $view) {
   return !$view->isEnabled();
 }
 
 /**
  * Enables a view.
  *
- * @param Drupal\views\ViewStorage $view
- *   The ViewStorage object to disable.
+ * @param Drupal\views\Plugin\Core\Entity\View $view
+ *   The View object to disable.
  */
-function views_enable_view(ViewStorage $view) {
+function views_enable_view(View $view) {
   $view->enable();
 }
 
 /**
  * Disables a view.
  *
- * @param Drupal\views\ViewStorage $view
- *   The ViewStorage object to disable.
+ * @param Drupal\views\Plugin\Core\Entity\View $view
+ *   The View object to disable.
  */
-function views_disable_view(ViewStorage $view) {
+function views_disable_view(View $view) {
   $view->disable();
 }
 
diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewListController.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewListController.php
index 64b2512..78464df 100644
--- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewListController.php
+++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewListController.php
@@ -83,7 +83,7 @@ public function buildHeader() {
    */
   public function getOperations(EntityInterface $view) {
     $uri = $view->uri();
-    $path = $uri['path'] . '/view/' . $view->id();
+    $path = $uri['path'];
 
     $definition['edit'] = array(
       'title' => t('Edit'),
diff --git a/core/modules/views/views_ui/views_ui.module b/core/modules/views/views_ui/views_ui.module
index eba7f08..d78ac25 100644
--- a/core/modules/views/views_ui/views_ui.module
+++ b/core/modules/views/views_ui/views_ui.module
@@ -747,7 +747,7 @@ function views_ui_ajax_callback(ViewExecutable $view, $op) {
   // Otherwise, redirect back to the page.
   else {
     $entity_info = entity_get_info('view');
-    drupal_goto($entity_info['list path']);
+    drupal_goto('admin/structure/views');
   }
 }
 
