diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 7822d7a..04a4130 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -102,6 +102,8 @@ function entity_get_bundles($entity_type = NULL) {
  */
 function entity_invoke_bundle_hook($hook, $entity_type, $bundle, $bundle_new = NULL) {
   entity_info_cache_clear();
+  $method = 'handleBundle' . ucfirst($hook);
+  Drupal::entityManager()->getStorageController($entity_type)->$method($bundle, $bundle_new);
   Drupal::moduleHandler()->invokeAll('entity_bundle_' . $hook, array($entity_type, $bundle, $bundle_new));
 }
 
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
index 14d3f18..0d9bd71 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
@@ -75,4 +75,14 @@ public function setStatus($status);
    */
   public function status();
 
+  /**
+   * Retrieves the exportable properties of the entity.
+   *
+   * These are the values that get saved into config.
+   *
+   * @return array
+   *   An array of exportable properties and their values.
+   */
+  public function getExportProperties();
+
 }
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index bbf1ae0..82830c1 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -7,13 +7,15 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Core\Language\Language;
-use PDO;
-use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\Query\QueryInterface;
-use Drupal\Component\Uuid\Uuid;
 use Drupal\Component\Utility\NestedArray;
-use Drupal\Core\Database\Connection;
+use Drupal\Component\Uuid\Uuid;
+use Drupal\field\FieldInfo;
+use Drupal\field\FieldUpdateForbiddenException;
+use Drupal\field\FieldInterface;
+use Drupal\field\FieldInstanceInterface;
+use Drupal\field\Plugin\Core\Entity\Field;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -24,7 +26,7 @@
  * This class can be used as-is by most simple entity types. Entity types
  * requiring special handling can extend the class.
  */
-class DatabaseStorageController extends EntityStorageControllerBase {
+class DatabaseStorageController extends FieldableEntityStorageControllerBase {
 
   /**
    * Name of entity's revision database table field, if it supports revisions.
@@ -59,13 +61,21 @@ class DatabaseStorageController extends EntityStorageControllerBase {
   protected $database;
 
   /**
+   * The field info object.
+   *
+   * @var \Drupal\field\FieldInfo
+   */
+  protected $fieldInfo;
+
+  /**
    * {@inheritdoc}
    */
   public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
     return new static(
       $entity_type,
       $entity_info,
-      $container->get('database')
+      $container->get('database'),
+      $container->get('field.info')
     );
   }
 
@@ -78,11 +88,14 @@ public static function createInstance(ContainerInterface $container, $entity_typ
    *   An array of entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
+   * @param \Drupal\field\FieldInfo $field_info
+   *   The field info service.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database) {
+  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
     parent::__construct($entity_type, $entity_info);
 
     $this->database = $database;
+    $this->fieldInfo = $field_info;
 
     // Check if the entity type supports IDs.
     if (isset($this->entityInfo['entity_keys']['id'])) {
@@ -143,7 +156,7 @@ public function loadMultiple(array $ids = NULL) {
         // 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['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);
     }
@@ -196,7 +209,7 @@ public function loadRevision($revision_id) {
       // 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['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);
 
@@ -335,14 +348,9 @@ protected function buildQuery($ids, $revision_id = FALSE) {
    *   (optional) TRUE if the revision should be loaded, defaults to FALSE.
    */
   protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
-    // Attach fields.
+    // Attach field values.
     if ($this->entityInfo['fieldable']) {
-      if ($load_revision) {
-        field_attach_load_revision($this->entityType, $queried_entities);
-      }
-      else {
-        field_attach_load($this->entityType, $queried_entities);
-      }
+      $this->fieldLoad($queried_entities, $load_revision ? FIELD_LOAD_REVISION : FIELD_LOAD_CURRENT);
     }
 
     // Call hook_entity_load().
@@ -490,7 +498,7 @@ public function save(EntityInterface $entity) {
   /**
    * Saves an entity revision.
    *
-   * @param Drupal\Core\Entity\EntityInterface $entity
+   * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity object.
    */
   protected function saveRevision(EntityInterface $entity) {
@@ -528,42 +536,673 @@ protected function saveRevision(EntityInterface $entity) {
   }
 
   /**
-   * Invokes a hook on behalf of the entity.
+   * {@inheritdoc}
+   */
+  public function baseFieldDefinitions() {
+    // @todo: Define abstract once all entity types have been converted.
+    return array();
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::getQueryServiceName().
+   */
+  public function getQueryServiceName() {
+    return 'entity.query.sql';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doFieldLoad($entities, $age) {
+    $load_current = $age == FIELD_LOAD_CURRENT;
+
+    // Collect entities ids and bundles.
+    $bundles = array();
+    $ids = array();
+    foreach ($entities as $key => $entity) {
+      $bundles[$entity->bundle()] = TRUE;
+      $ids[] = $load_current ? $key : $entity->getRevisionId();
+    }
+
+    // Collect impacted fields.
+    $fields = array();
+    foreach ($bundles as $bundle => $v) {
+      foreach ($this->fieldInfo->getBundleInstances($this->entityType, $bundle) as $field_name => $instance) {
+        $fields[$field_name] = $instance->getField();
+      }
+    }
+
+    // Load field data.
+    foreach ($fields as $field_name => $field) {
+      $table = $load_current ? static::_fieldTableName($field) : static::_fieldRevisionTableName($field);
+
+      $results = $this->database->select($table, 't')
+        ->fields('t')
+        ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
+        ->condition('langcode', field_available_languages($this->entityType, $field), 'IN')
+        ->orderBy('delta')
+        ->condition('deleted', 0)
+        ->execute();
+
+      $delta_count = array();
+      foreach ($results as $row) {
+        if (!isset($delta_count[$row->entity_id][$row->langcode])) {
+          $delta_count[$row->entity_id][$row->langcode] = 0;
+        }
+
+        if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
+          $item = array();
+          // For each column declared by the field, populate the item from the
+          // prefixed database column.
+          foreach ($field['columns'] as $column => $attributes) {
+            $column_name = static::_fieldColumnName($field, $column);
+            // Unserialize the value if specified in the column schema.
+            $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name;
+          }
+
+          // Add the item to the field values for the entity.
+          $entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
+          $delta_count[$row->entity_id][$row->langcode]++;
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doFieldInsert(EntityInterface $entity) {
+    $this->doFieldWrite($entity, FALSE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doFieldUpdate(EntityInterface $entity) {
+    $this->doFieldWrite($entity, TRUE);
+  }
+
+  /**
+   * Performs the actual write on behalf of doFieldInsert / doFieldUpdate.
    *
-   * @param $hook
-   *   One of 'presave', 'insert', 'update', 'predelete', 'delete', or
-   *  'revision_delete'.
-   * @param $entity
-   *   The entity object.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity being written.
+   * @param bool $update
+   *   TRUE when running an update, FALSE when running an insert.
    */
-  protected function invokeHook($hook, EntityInterface $entity) {
-    $function = 'field_attach_' . $hook;
-    // @todo: field_attach_delete_revision() is named the wrong way round,
-    // consider renaming it.
-    if ($function == 'field_attach_revision_delete') {
-      $function = 'field_attach_delete_revision';
+  protected function doFieldWrite(EntityInterface $entity, $update) {
+    $vid = $entity->getRevisionId();
+    $id = $entity->id();
+    $bundle = $entity->bundle();
+    $entity_type = $entity->entityType();
+    if (!isset($vid)) {
+      $vid = $id;
     }
-    if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
-      $function($entity);
+
+    foreach ($this->fieldInfo->getBundleInstances($entity_type, $bundle) as $field_name => $instance) {
+      $field = $instance->getField();
+      $table_name = static::_fieldTableName($field);
+      $revision_name = static::_fieldRevisionTableName($field);
+
+      $all_langcodes = field_available_languages($entity_type, $field);
+      $field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
+
+      // Delete and insert, rather than update, in case a value was added.
+      if ($update) {
+        // Delete language codes present in the incoming $entity->$field_name.
+        // Delete all language codes if $entity->$field_name is empty.
+        $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
+        if ($langcodes) {
+          // Only overwrite the field's base table if saving the default revision
+          // of an entity.
+          if ($entity->isDefaultRevision()) {
+            $this->database->delete($table_name)
+              ->condition('entity_id', $id)
+              ->condition('langcode', $langcodes, 'IN')
+              ->execute();
+          }
+          $this->database->delete($revision_name)
+            ->condition('entity_id', $id)
+            ->condition('revision_id', $vid)
+            ->condition('langcode', $langcodes, 'IN')
+            ->execute();
+        }
+      }
+
+      // Prepare the multi-insert query.
+      $do_insert = FALSE;
+      $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
+      foreach ($field['columns'] as $column => $attributes) {
+        $columns[] = static::_fieldColumnName($field, $column);
+      }
+      $query = $this->database->insert($table_name)->fields($columns);
+      $revision_query = $this->database->insert($revision_name)->fields($columns);
+
+      foreach ($field_langcodes as $langcode) {
+        $items = (array) $entity->{$field_name}[$langcode];
+        $delta_count = 0;
+        foreach ($items as $delta => $item) {
+          // We now know we have someting to insert.
+          $do_insert = TRUE;
+          $record = array(
+            'entity_id' => $id,
+            'revision_id' => $vid,
+            'bundle' => $bundle,
+            'delta' => $delta,
+            'langcode' => $langcode,
+          );
+          foreach ($field['columns'] as $column => $attributes) {
+            $column_name = static::_fieldColumnName($field, $column);
+            $value = isset($item[$column]) ? $item[$column] : NULL;
+            // Serialize the value if specified in the column schema.
+            $record[$column_name] = (!empty($attributes['serialize'])) ? serialize($value) : $value;
+          }
+          $query->values($record);
+          if (isset($vid)) {
+            $revision_query->values($record);
+          }
+
+          if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
+            break;
+          }
+        }
+      }
+
+      // Execute the query if we have values to insert.
+      if ($do_insert) {
+        // Only overwrite the field's base table if saving the default revision
+        // of an entity.
+        if ($entity->isDefaultRevision()) {
+          $query->execute();
+        }
+        $revision_query->execute();
+      }
     }
-    // Invoke the hook.
-    module_invoke_all($this->entityType . '_' . $hook, $entity);
-    // Invoke the respective entity-level hook.
-    module_invoke_all('entity_' . $hook, $entity, $this->entityType);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function baseFieldDefinitions() {
-    // @todo: Define abstract once all entity types have been converted.
-    return array();
+  protected function doFieldDelete(EntityInterface $entity) {
+    foreach ($this->fieldInfo->getBundleInstances($entity->getType(), $entity->bundle()) as $instance) {
+      $field = $instance->getField();
+      $table_name = static::_fieldTableName($field);
+      $revision_name = static::_fieldRevisionTableName($field);
+      $this->database->delete($table_name)
+        ->condition('entity_id', $entity->id())
+        ->execute();
+      $this->database->delete($revision_name)
+        ->condition('entity_id', $entity->id())
+        ->execute();
+    }
   }
 
   /**
-   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::getQueryServiceName().
+   * {@inheritdoc}
    */
-  public function getQueryServiceName() {
-    return 'entity.query.sql';
+  protected function doFieldRevisionDelete(EntityInterface $entity) {
+    $vid = $entity->getRevisionId();
+    if (isset($vid)) {
+      foreach ($this->fieldInfo->getBundleInstances($entity->getType(), $entity->bundle()) as $instance) {
+        $revision_name = static::_fieldRevisionTableName($instance->getField());
+        $this->database->delete($revision_name)
+          ->condition('entity_id', $entity->id())
+          ->condition('revision_id', $vid)
+          ->execute();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFieldCreate(FieldInterface $field) {
+    $schema = $this->_fieldSqlSchema($field);
+    foreach ($schema as $name => $table) {
+      $this->database->schema()->createTable($name, $table);
+    }
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFieldUpdate(FieldInterface $field, FieldInterface $original) {
+    if (!$field->hasData()) {
+      // There is no data. Re-create the tables completely.
+
+      if ($this->database->supportsTransactionalDDL()) {
+        // If the database supports transactional DDL, we can go ahead and rely
+        // on it. If not, we will have to rollback manually if something fails.
+        $transaction = $this->database->startTransaction();
+      }
+
+      try {
+        $original_schema = $this->_fieldSqlSchema($original);
+        foreach ($original_schema as $name => $table) {
+          $this->database->schema()->dropTable($name, $table);
+        }
+        $schema = $this->_fieldSqlSchema($field);
+        foreach ($schema as $name => $table) {
+          $this->database->schema()->createTable($name, $table);
+        }
+      }
+      catch (\Exception $e) {
+        if ($this->database->supportsTransactionalDDL()) {
+          $transaction->rollback();
+        }
+        else {
+          // Recreate tables.
+          $original_schema = $this->_fieldSqlSchema($original);
+          foreach ($original_schema as $name => $table) {
+            if (!$this->database->schema()->tableExists($name)) {
+              $this->database->schema()->createTable($name, $table);
+            }
+          }
+        }
+        throw $e;
+      }
+    }
+    else {
+      if ($field['columns'] != $original['columns']) {
+        throw new FieldUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data.");
+      }
+      // There is data, so there are no column changes. Drop all the prior
+      // indexes and create all the new ones, except for all the priors that
+      // exist unchanged.
+      $table = static::_fieldTableName($original);
+      $revision_table = static::_fieldRevisionTableName($original);
+
+      $schema = $field->getSchema();
+      $original_schema = $original->getSchema();
+
+      foreach ($original_schema['indexes'] as $name => $columns) {
+        if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) {
+          $real_name = static::_fieldIndexName($field, $name);
+          $this->database->schema()->dropIndex($table, $real_name);
+          $this->database->schema()->dropIndex($revision_table, $real_name);
+        }
+      }
+      $table = static::_fieldTableName($field);
+      $revision_table = static::_fieldRevisionTableName($field);
+      foreach ($schema['indexes'] as $name => $columns) {
+        if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) {
+          $real_name = static::_fieldIndexName($field, $name);
+          $real_columns = array();
+          foreach ($columns as $column_name) {
+            // Indexes can be specified as either a column name or an array with
+            // column name and length. Allow for either case.
+            if (is_array($column_name)) {
+              $real_columns[] = array(
+                static::_fieldColumnName($field, $column_name[0]),
+                $column_name[1],
+              );
+            }
+            else {
+              $real_columns[] = static::_fieldColumnName($field, $column_name);
+            }
+          }
+          $this->database->schema()->addIndex($table, $real_name, $real_columns);
+          $this->database->schema()->addIndex($revision_table, $real_name, $real_columns);
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFieldDelete(FieldInterface $field) {
+    // Mark all data associated with the field for deletion.
+    $field['deleted'] = FALSE;
+    $table = static::_fieldTableName($field);
+    $revision_table = static::_fieldRevisionTableName($field);
+    $this->database->update($table)
+      ->fields(array('deleted' => 1))
+      ->execute();
+
+    // Move the table to a unique name while the table contents are being
+    // deleted.
+    $field['deleted'] = TRUE;
+    $new_table = static::_fieldTableName($field);
+    $revision_new_table = static::_fieldRevisionTableName($field);
+    $this->database->schema()->renameTable($table, $new_table);
+    $this->database->schema()->renameTable($revision_table, $revision_new_table);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleInstanceDelete(FieldInstanceInterface $instance) {
+    $field = $instance->getField();
+    $table_name = static::_fieldTableName($field);
+    $revision_name = static::_fieldRevisionTableName($field);
+    $this->database->update($table_name)
+      ->fields(array('deleted' => 1))
+      ->condition('bundle', $instance['bundle'])
+      ->execute();
+    $this->database->update($revision_name)
+      ->fields(array('deleted' => 1))
+      ->condition('bundle', $instance['bundle'])
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleBundleRename($bundle, $bundle_new) {
+    // We need to account for deleted or inactive fields and instances.
+    $instances = field_read_instances(array('entity_type' => $this->entityType, 'bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
+    foreach ($instances as $instance) {
+      $field = $instance->getField();
+      if ($field['storage']['type'] == 'field_sql_storage') {
+        $table_name = static::_fieldTableName($field);
+        $revision_name = static::_fieldRevisionTableName($field);
+        $this->database->update($table_name)
+          ->fields(array('bundle' => $bundle_new))
+          ->condition('bundle', $bundle)
+          ->execute();
+        $this->database->update($revision_name)
+          ->fields(array('bundle' => $bundle_new))
+          ->condition('bundle', $bundle)
+          ->execute();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function fieldLoadPurgedValues(EntityInterface $entity, FieldInstanceInterface $instance) {
+    $field = $instance->getField();
+    $query = $this->database->select(static::_fieldTableName($field), 't', array('fetch' => \PDO::FETCH_ASSOC))
+      ->condition('entity_id', $entity->id())
+      ->orderBy('delta');
+    foreach ($field->getColumns() as $column_name => $data) {
+      $query->addField('t', static::_fieldColumnName($field, $column_name), $column_name);
+    }
+    return $query->execute()->fetchAll();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function doFieldPurgeData(EntityInterface $entity, FieldInstanceInterface $instance) {
+    $field = $instance->getField();
+    $table_name = static::_fieldTableName($field);
+    $revision_name = static::_fieldRevisionTableName($field);
+    $this->database->delete($table_name)
+      ->condition('entity_id', $entity->id())
+      ->execute();
+    $this->database->delete($revision_name)
+      ->condition('entity_id', $entity->id())
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFieldPurged(FieldInterface $field) {
+    $table_name = static::_fieldTableName($field);
+    $revision_name = static::_fieldRevisionTableName($field);
+    $this->database->schema()->dropTable($table_name);
+    $this->database->schema()->dropTable($revision_name);
+  }
+
+  /**
+   * Gets the SQL table schema.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field object
+   * @param array $schema
+   *   The field schema array. Mandatory for upgrades, omit otherwise.
+   *
+   * @return array
+   *   The same as a hook_schema() implementation for the data and the
+   *   revision tables.
+   *
+   * @see hook_schema()
+   */
+  public static function _fieldSqlSchema(FieldInterface $field, array $schema = NULL) {
+    if ($field['deleted']) {
+      $description_current = "Data storage for deleted field {$field['id']} ({$field['entity_type']}, {$field['field_name']}).";
+      $description_revision = "Revision archive storage for deleted field {$field['id']} ({$field['entity_type']}, {$field['field_name']}).";
+    }
+    else {
+      $description_current = "Data storage for {$field['entity_type']} field {$field['field_name']}.";
+      $description_revision = "Revision archive storage for {$field['entity_type']} field {$field['field_name']}.";
+    }
+
+    $current = array(
+      'description' => $description_current,
+      'fields' => array(
+        'bundle' => array(
+          'type' => 'varchar',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+          'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
+        ),
+        'deleted' => array(
+          'type' => 'int',
+          'size' => 'tiny',
+          'not null' => TRUE,
+          'default' => 0,
+          'description' => 'A boolean indicating whether this data item has been deleted'
+        ),
+        'entity_id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'The entity id this data is attached to',
+        ),
+        'revision_id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => FALSE,
+          'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
+        ),
+        'langcode' => array(
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => TRUE,
+          'default' => '',
+          'description' => 'The language code for this data item.',
+        ),
+        'delta' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'The sequence number for this data item, used for multi-value fields',
+        ),
+      ),
+      'primary key' => array('entity_id', 'deleted', 'delta', 'langcode'),
+      'indexes' => array(
+        'bundle' => array('bundle'),
+        'deleted' => array('deleted'),
+        'entity_id' => array('entity_id'),
+        'revision_id' => array('revision_id'),
+        'langcode' => array('langcode'),
+      ),
+    );
+
+    if (!$schema) {
+      $schema = $field->getSchema();
+    }
+
+    // Add field columns.
+    foreach ($schema['columns'] as $column_name => $attributes) {
+      $real_name = static::_fieldColumnName($field, $column_name);
+      $current['fields'][$real_name] = $attributes;
+    }
+
+    // Add indexes.
+    foreach ($schema['indexes'] as $index_name => $columns) {
+      $real_name = static::_fieldIndexName($field, $index_name);
+      foreach ($columns as $column_name) {
+        // Indexes can be specified as either a column name or an array with
+        // column name and length. Allow for either case.
+        if (is_array($column_name)) {
+          $current['indexes'][$real_name][] = array(
+            static::_fieldColumnName($field, $column_name[0]),
+            $column_name[1],
+          );
+        }
+        else {
+          $current['indexes'][$real_name][] = static::_fieldColumnName($field, $column_name);
+        }
+      }
+    }
+
+    // Add foreign keys.
+    foreach ($schema['foreign keys'] as $specifier => $specification) {
+      $real_name = static::_fieldIndexName($field, $specifier);
+      $current['foreign keys'][$real_name]['table'] = $specification['table'];
+      foreach ($specification['columns'] as $column_name => $referenced) {
+        $sql_storage_column = static::_fieldColumnName($field, $column_name);
+        $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
+      }
+    }
+
+    // Construct the revision table.
+    $revision = $current;
+    $revision['description'] = $description_revision;
+    $revision['primary key'] = array('entity_id', 'revision_id', 'deleted', 'delta', 'langcode');
+    $revision['fields']['revision_id']['not null'] = TRUE;
+    $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
+
+    return array(
+      static::_fieldTableName($field) => $current,
+      static::_fieldRevisionTableName($field) => $revision,
+    );
+  }
+
+  /**
+   * Generates a table name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field object.
+   *
+   * @return string
+   *   A string containing the generated name for the database table.
+   *
+   */
+  static public function _fieldTableName(FieldInterface $field) {
+    if ($field['deleted']) {
+      // When a field is a deleted, the table is renamed to
+      // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
+      // table names longer than 64 characters, we hash the uuid and return the
+      // first 10 characters so we end up with a short unique ID.
+      return "field_deleted_data_" . substr(hash('sha256', $field['uuid']), 0, 10);
+    }
+    else {
+      return static::_truncateFieldTableName("{$field->entity_type}__{$field->name}", $field->uuid);
+    }
+  }
+
+  /**
+   * Generates a table name for a field revision archive table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\field\Plugin\Core\Entity\FieldInterface $field
+   *   The field object.
+   *
+   * @return string
+   *   A string containing the generated name for the database table.
+   */
+  static public function _fieldRevisionTableName(FieldInterface $field) {
+    if ($field['deleted']) {
+      // When a field is a deleted, the table is renamed to
+      // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with
+      // table names longer than 64 characters, we hash the uuid and return the
+      // first 10 characters so we end up with a short unique ID.
+      return "field_deleted_revision_" . substr(hash('sha256', $field['uuid']), 0, 10);
+    }
+    else {
+      return static::_truncateFieldTableName("{$field->entity_type}_revision__{$field->name}", $field->uuid);
+    }
+  }
+
+  /**
+   * Generates a safe and unanbiguous field table name for a condidate name.
+   *
+   * The method accounts for a maximum table name length of 64 characters, and
+   * adds a hash of the field UUID for disambiguation.
+   *
+   * @param string $table_name
+   *   The candidate table name.
+   * @param string $field_uuid
+   *   The UUID of the field.
+   *
+   * @return string
+   *   The final table name.
+   */
+  static protected function _truncateFieldTableName($table_name, $field_uuid) {
+    // Limit the string to 48 characters, keeping a 16 characters margin for db
+    // prefixes.
+    if (strlen($table_name) > 48) {
+      $table_name = substr($table_name, 0, 38) . substr(hash('sha256', $field_uuid), 0, 10);
+    }
+    return $table_name;
+  }
+
+  /**
+   * Generates an index name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases.
+   *
+   * @param \Drupal\field\Plugin\Core\Entity\FieldInterface $field
+   *   The field structure
+   * @param string $index
+   *   The name of the index.
+   *
+   * @return string
+   *   A string containing a generated index name for a field data table that is
+   *   unique among all other fields.
+   */
+  static public function _fieldIndexName(FieldInterface $field, $index) {
+    return $field->getFieldName() . '_' . $index;
+  }
+
+  /**
+   * Generates a column name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param \Drupal\field\Plugin\Core\Entity\FieldInterface $field
+   *   The field object.
+   * @param string $column
+   *   The name of the column.
+   *
+   * @return string
+   *   A string containing a generated column name for a field data table that is
+   *   unique among all other fields.
+   */
+  static public function _fieldColumnName(FieldInterface $field, $column) {
+    return in_array($column, Field::getReservedColumns()) ? $column : $field->getFieldName() . '_' . $column;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
index ad361d7..0c325bd 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Core\Language\Language;
+use Drupal\field\FieldInfo;
 use PDO;
 
 use Drupal\Core\Entity\Query\QueryInterface;
@@ -53,8 +54,8 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
   /**
    * Overrides DatabaseStorageController::__construct().
    */
-  public function __construct($entity_type, array $entity_info, Connection $database) {
-    parent::__construct($entity_type,$entity_info, $database);
+  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
+    parent::__construct($entity_type,$entity_info, $database, $field_info);
     $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
     $this->entityClass = $this->entityInfo['class'];
 
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index ec05c1a..770b486 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -333,8 +333,7 @@ public function getTranslationLanguages($include_default = TRUE) {
       // Go through translatable properties and determine all languages for
       // which translated values are available.
       foreach (field_info_instances($this->entityType, $this->bundle()) as $field_name => $instance) {
-        $field = field_info_field($field_name);
-        if (field_is_translatable($this->entityType, $field) && isset($this->$field_name)) {
+        if (field_is_translatable($this->entityType, $instance->getField()) && isset($this->$field_name)) {
           foreach (array_filter($this->$field_name) as $langcode => $value)  {
             $languages[$langcode] = TRUE;
           }
diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php
index ceb09de..b9e3da5 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormController.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormController.php
@@ -295,6 +295,7 @@ protected function actions(array $form, array &$form_state) {
    */
   public function validate(array $form, array &$form_state) {
     $entity = $this->buildEntity($form, $form_state);
+    $entity_type = $entity->entityType();
     $entity_langcode = $entity->language()->id;
 
     $violations = array();
@@ -311,9 +312,9 @@ public function validate(array $form, array &$form_state) {
     else {
       // For BC entities, iterate through each field instance and
       // instantiate NG items objects manually.
-      $definitions = \Drupal::entityManager()->getFieldDefinitions($entity->entityType(), $entity->bundle());
-      foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $field_name => $instance) {
-        $langcode = field_is_translatable($entity->entityType(), $instance->getField()) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
+      $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity->bundle());
+      foreach (field_info_instances($entity_type, $entity->bundle()) as $field_name => $instance) {
+        $langcode = field_is_translatable($entity_type, $instance->getField()) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
 
         // Create the field object.
         $items = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
@@ -330,7 +331,7 @@ public function validate(array $form, array &$form_state) {
     // Map errors back to form elements.
     if ($violations) {
       foreach ($violations as $field_name => $field_violations) {
-        $langcode = field_is_translatable($entity->entityType(), field_info_field($field_name)) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
+        $langcode = field_is_translatable($entity_type , field_info_field($entity_type, $field_name)) ? $entity_langcode : Language::LANGCODE_NOT_SPECIFIED;
         $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
         $field_state['constraint_violations'] = $field_violations;
         field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
@@ -459,9 +460,8 @@ protected function submitEntityLanguage(array $form, array &$form_state) {
       $current_langcode = $this->isDefaultFormLangcode($form_state) ? $form_state['values']['langcode'] : $this->getFormLangcode($form_state);
 
       foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
-        $field_name = $instance['field_name'];
-        $field = field_info_field($field_name);
-
+        $field = $instance->getField();
+        $field_name = $field->name;
         if (isset($form[$field_name]['#language'])) {
           $previous_langcode = $form[$field_name]['#language'];
 
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index a662259..7a807ac 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -88,6 +88,15 @@ class EntityManager extends PluginManagerBase {
   protected $fieldDefinitions;
 
   /**
+   * The root paths.
+   *
+   * @see \Drupal\Core\Entity\EntityManager::__construct().
+   *
+   * @var \Traversable
+   */
+  protected $namespaces;
+
+  /**
    * Constructs a new Entity plugin manager.
    *
    * @param \Traversable $namespaces
@@ -104,21 +113,45 @@ class EntityManager extends PluginManagerBase {
    */
   public function __construct(\Traversable $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager) {
     // Allow the plugin definition to be altered by hook_entity_info_alter().
-    $annotation_namespaces = array(
-      'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
-    );
 
     $this->moduleHandler = $module_handler;
     $this->cache = $cache;
     $this->languageManager = $language_manager;
+    $this->namespaces = $namespaces;
 
-    $this->discovery = new AnnotatedClassDiscovery('Plugin/Core/Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
+    $this->doDiscovery($namespaces);
+    $this->factory = new DefaultFactory($this->discovery);
+    $this->container = $container;
+  }
+
+  protected function doDiscovery($namespaces) {
+    $annotation_namespaces = array(
+      'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
+    );
+    $this->discovery = new AnnotatedClassDiscovery('Plugin\Core/Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
     $this->discovery = new InfoHookDecorator($this->discovery, 'entity_info');
     $this->discovery = new AlterDecorator($this->discovery, 'entity_info');
     $this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
+  }
 
-    $this->factory = new DefaultFactory($this->discovery);
-    $this->container = $container;
+  /**
+   * Add more namespaces to the entity manager.
+   *
+   * This is usually only necessary for uninstall purposes.
+   *
+   * @todo Remove this method, along with doDiscovery(), when
+   * https://drupal.org/node/1199946 is fixed.
+   *
+   * @param \Traversable $namespaces
+   *
+   * @see comment_uninstall()
+   */
+  public function addNamespaces(\Traversable $namespaces) {
+    reset($this->namespaces);
+    $iterator = new \AppendIterator;
+    $iterator->append(new \IteratorIterator($this->namespaces));
+    $iterator->append($namespaces);
+    $this->doDiscovery($iterator);
   }
 
   /**
@@ -153,6 +186,9 @@ public function hasController($entity_type, $controller_type) {
    */
   public function getControllerClass($entity_type, $controller_type, $nested = NULL) {
     $definition = $this->getDefinition($entity_type);
+    if (!$definition) {
+      throw new \InvalidArgumentException(sprintf('The %s entity type does not exist.', $entity_type));
+    }
     $definition = $definition['controllers'];
     if (!$definition) {
       throw new \InvalidArgumentException(sprintf('The entity type (%s) does not exist.', $entity_type));
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
index 988afa7..22eb472 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
@@ -234,6 +234,22 @@ public function invokeFieldItemPrepareCache(EntityInterface $entity) {
   }
 
   /**
+   * Invokes a hook on behalf of the entity.
+   *
+   * @param string $hook
+   *   One of 'presave', 'insert', 'update', 'predelete', 'delete', or
+   *  'revision_delete'.
+   * @param \Drupal\Core\Entity\EntityInterface  $entity
+   *   The entity object.
+   */
+  protected function invokeHook($hook, EntityInterface $entity) {
+    // Invoke the hook.
+    module_invoke_all($this->entityType . '_' . $hook, $entity);
+    // Invoke the respective entity-level hook.
+    module_invoke_all('entity_' . $hook, $entity, $this->entityType);
+  }
+
+  /**
    * Checks translation statuses and invoke the related hooks if needed.
    *
    * @param \Drupal\Core\Entity\EntityInterface $entity
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
new file mode 100644
index 0000000..8c5ff5e
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php
@@ -0,0 +1,322 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\FieldableEntityStorageControllerBase.
+ */
+
+
+namespace Drupal\Core\Entity;
+
+use Drupal\field\FieldInterface;
+use Drupal\field\FieldInstanceInterface;
+use Symfony\Component\DependencyInjection\Container;
+
+abstract class FieldableEntityStorageControllerBase extends EntityStorageControllerBase implements FieldableEntityStorageControllerInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function invokeHook($hook, EntityInterface $entity) {
+    $method = Container::camelize('field_' . $hook);
+    if (!empty($this->entityInfo['fieldable']) && method_exists($this, $method)) {
+      $this->$method($entity);
+    }
+    parent::invokeHook($hook, $entity);
+  }
+
+  /**
+   * Loads fields for the current revisions of a group of entities.
+   *
+   * Loads all fields for each entity object in a group of a single entity type.
+   * The loaded field values are added directly to the entity objects.
+   *
+   * @param array $entities
+   *   An array of entities for which to load fields, keyed by entity ID.
+   * @param int $age
+   *   FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
+   *   FIELD_LOAD_REVISION to load the version indicated by each entity.
+   */
+  protected function fieldLoad($entities, $age) {
+    if (empty($entities)) {
+      return;
+    }
+
+    // Only the most current revision of non-deleted fields for cacheable entity
+    // types can be cached.
+    $load_current = $age == FIELD_LOAD_CURRENT;
+    $info = entity_get_info($this->entityType);
+    $use_cache = $load_current && $info['field_cache'];
+
+    // Ensure we are working with a BC mode entity.
+    foreach ($entities as $id => $entity) {
+      $entities[$id] = $entity->getBCEntity();
+    }
+
+    // Assume all entities will need to be queried. Entities found in the cache
+    // will be removed from the list.
+    $queried_entities = $entities;
+
+    // Fetch available entities from cache, if applicable.
+    if ($use_cache) {
+      // Build the list of cache entries to retrieve.
+      $cids = array();
+      foreach ($entities as $id => $entity) {
+        $cids[] = "field:{$this->entityType}:$id";
+      }
+      $cache = cache('field')->getMultiple($cids);
+      // Put the cached field values back into the entities and remove them from
+      // the list of entities to query.
+      foreach ($entities as $id => $entity) {
+        $cid = "field:{$this->entityType}:$id";
+        if (isset($cache[$cid])) {
+          unset($queried_entities[$id]);
+          foreach ($cache[$cid]->data as $field_name => $values) {
+            $entity->$field_name = $values;
+          }
+        }
+      }
+    }
+
+    // Fetch other entities from their storage location.
+    if ($queried_entities) {
+      // The invoke order is:
+      // - Entity storage controller's doFieldLoad() method
+      // - Field class's prepareCache() method.
+      // - hook_field_attach_load()
+
+      // Let the storage controller actually load the values.
+      $this->doFieldLoad($queried_entities, $age);
+
+      // Invoke the field type's prepareCache() method.
+      foreach ($queried_entities as $entity) {
+        \Drupal::entityManager()
+          ->getStorageController($this->entityType)
+          ->invokeFieldItemPrepareCache($entity);
+      }
+
+      // Invoke hook_field_attach_load(): let other modules act on loading the
+      // entity.
+      \Drupal::moduleHandler()->invokeAll('field_attach_load', array($this->entityType, $queried_entities, $age));
+
+      // Build cache data.
+      if ($use_cache) {
+        foreach ($queried_entities as $id => $entity) {
+          $data = array();
+          $instances = field_info_instances($this->entityType, $entity->bundle());
+          foreach ($instances as $instance) {
+            $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
+          }
+          $cid = "field:{$this->entityType}:$id";
+          cache('field')->set($cid, $data);
+        }
+      }
+    }
+  }
+
+  /**
+   * Save field data for a new entity.
+   *
+   * The passed-in entity must already contain its id and (if applicable)
+   * revision id attributes.
+   *
+   * It should be enough to override doFieldInsert() instead of this method.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity with fields to save.
+   */
+  protected function fieldInsert(EntityInterface $entity) {
+    // Ensure we are working with a BC mode entity.
+    $entity = $entity->getBCEntity();
+
+    $this->doFieldInsert($entity);
+  }
+
+  /**
+   * Saves field data for an existing entity.
+   *
+   * It should be enough to override doFieldUpdate() instead of this method.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity with fields to save.
+   */
+  protected function fieldUpdate(EntityInterface $entity) {
+    // Ensure we are working with a BC mode entity.
+    $entity = $entity->getBCEntity();
+
+    $this->doFieldUpdate($entity);
+
+    $entity_info = $entity->entityInfo();
+    if ($entity_info['field_cache']) {
+      cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
+    }
+  }
+
+  /**
+   * Deletes field data for an existing entity.
+   *
+   * This deletes all revisions of field data for the entity.
+   *
+   * It should be enough to override doFieldDelete() instead of this method.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity whose field data to delete.
+   */
+  protected function fieldDelete(EntityInterface $entity) {
+    // Ensure we are working with a BC mode entity.
+    $entity = $entity->getBCEntity();
+
+    $this->doFieldDelete($entity);
+
+    $entity_info = $entity->entityInfo();
+    if ($entity_info['field_cache']) {
+      cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
+    }
+  }
+
+  /**
+   * Delete field data for a single revision of an existing entity.
+   *
+   * The passed $entity must have a revision ID attribute.
+   *
+   * It should be enough to override doFieldRevisionDelete() instead of this
+   * method.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity with fields to save.
+   */
+  protected function fieldRevisionDelete(EntityInterface $entity) {
+    $this->dofieldRevisionDelete($entity->getBCEntity());
+  }
+
+  /**
+   * Load configurable fields from storage.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   */
+  protected function doFieldLoad($queried_entities, $age) { }
+
+  /**
+   * Insert configurable fields into storage.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   */
+  protected function doFieldInsert(EntityInterface $entity) { }
+
+  /**
+   * Update configurable fields in storage.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   */
+  protected function doFieldUpdate(EntityInterface $entity) { }
+
+  /**
+   * Delete configurable fields from storage.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   */
+  protected function doFieldDelete(EntityInterface $entity) { }
+
+  /**
+   * Delete specific revision of configurable fields from storage.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   */
+  protected function doFieldRevisionDelete(EntityInterface $entity) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFieldCreate(FieldInterface $field) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFieldUpdate(FieldInterface $field, FieldInterface $original) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFieldDelete(FieldInterface $field) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleInstanceCreate(FieldInstanceInterface $instance) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleInstanceUpdate(FieldInstanceInterface $instance, FieldInstanceInterface $orignal) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleInstanceDelete(FieldInstanceInterface $instance) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleBundleCreate($bundle) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleBundleRename($bundle, $bundle_new) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleBundleDelete($bundle) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fieldPurgeData(EntityInterface $entity, FieldInstanceInterface $instance) {
+    if ($values = $this->fieldLoadPurgedValues($entity, $instance)) {
+      $field = $instance->getField();
+      $definition = _field_generate_entity_field_definition($field, $instance);
+      $items = \Drupal::typedData()->create($definition, $values, $field->getFieldName(), $entity);
+      $items->delete();
+    }
+    $this->doFieldPurgeData($entity, $instance);
+  }
+
+  /**
+   * Reads values to be purged for a single field of a single entity.
+   *
+   * This method is called during field data purge, on fields for which
+   * handleFieldDelete() or handleFieldInstanceDelete() has previously run.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   * @param \Drupal\field\FieldInstanceInterface $instance
+   *   The field instance.
+   *
+   * @return array
+   *   The field values, in their canonical array format (numerically indexed
+   *   array of items, each item being a property/value array).
+   */
+  abstract protected function fieldLoadPurgedValues(EntityInterface $entity, FieldInstanceInterface $instance);
+
+  /**
+   * Removes field data from storage during purge.
+   *
+   * @param EntityInterface $entity
+   *   The entity whose values are being purged.
+   * @param FieldInstanceInterface $instance
+   *   The field whose values are bing purged.
+   */
+  protected function doFieldPurgeData(EntityInterface $entity, FieldInstanceInterface $instance) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFieldPurged(FieldInterface $field) { }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerInterface.php
new file mode 100644
index 0000000..8b8de91
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerInterface.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\ExtensibleEntityStorageControllerInterface.
+ */
+
+
+namespace Drupal\Core\Entity;
+
+
+use Drupal\field\FieldInterface;
+use Drupal\field\FieldInstanceInterface;
+
+interface FieldableEntityStorageControllerInterface extends EntityStorageControllerInterface {
+
+  /**
+   * Allows reaction to the creation of a configurable field.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field being created.
+   */
+  public function handleFieldCreate(FieldInterface $field);
+
+  /**
+   * Allows reaction to the update of a configurable field.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field being updated.
+   * @param \Drupal\field\FieldInterface $original
+   *   The previous state of the field.
+   */
+  public function handleFieldUpdate(FieldInterface $field, FieldInterface $original);
+
+  /**
+   * Allows reaction to the deletion of a configurable field.
+   *
+   * Stored values should not be wiped at once, but marked as 'deleted' so that
+   * they can go through a proper purge process later on.
+   *
+   * @param \Drupal\field\FieldInterface $field
+   *   The field being deleted.
+   *
+   * @see fieldPurgeData()
+   */
+  public function handleFieldDelete(FieldInterface $field);
+
+  /**
+   * Allows reaction to the creation of a configurable field instance.
+   *
+   * @param \Drupal\field\FieldInstanceInterface $instance
+   *   The instance being created.
+   */
+  public function handleInstanceCreate(FieldInstanceInterface $instance);
+
+  /**
+   * Allows reaction to the deletion of a configurable field instance.
+   *
+   * Stored values should not be wiped at once, but marked as 'deleted' so that
+   * they can go through a proper purge process later on.
+   *
+   * @param \Drupal\field\FieldInstanceInterface $instance
+   *   The instance being deleted.
+   *
+   * @see fieldPurgeData()
+   */
+  public function handleInstanceDelete(FieldInstanceInterface $instance);
+
+  /**
+   * Allows reaction to a bundle being created.
+   *
+   * @param string $bundle
+   *   The name of the bundle created.
+   */
+  public function handleBundleCreate($bundle);
+
+  /**
+   * Allows reaction to a bundle being renamed.
+   *
+   * @param string $bundle
+   *   The name of the bundle being renamed.
+   * @param string $bundle_new
+   *   The new name of the bundle.
+   */
+  public function handleBundleRename($bundle, $bundle_new);
+
+  /**
+   * Allows reaction to a bundle being deleted.
+   *
+   * @param string $bundle
+   *   The name of the bundle being deleted.
+   */
+  public function handleBundleDelete($bundle);
+
+  /**
+   * Purges the field data for a single field on a single entity.
+   *
+   * The entity itself is not being deleted, and it is quite possible that
+   * other field data will remain attached to it.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity whose field data is being purged.
+   * @param \Drupal\field\FieldInstanceInterface $instance
+   *   The deleted field instance whose data is being purged.
+   */
+  public function fieldPurgeData(EntityInterface $entity, FieldInstanceInterface $instance);
+
+  /**
+   * Performs final cleanup after all data on all instances has been purged.
+   *
+   * @param \Drupal\field\FieldInterface $instance
+   *   The field being purged.
+   */
+  public function handleFieldPurged(FieldInterface $field);
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index 2f7156a..88204f9 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity\Query\Sql;
 
 use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\Plugin\DataType\EntityReference;
 use Drupal\Core\Entity\Query\QueryException;
 use Drupal\field\Plugin\Core\Entity\Field;
@@ -107,8 +108,8 @@ function addField($field, $type, $langcode) {
       if (substr($specifier, 0, 3) == 'id:') {
         $field = field_info_field_by_id(substr($specifier, 3));
       }
-      elseif (isset($field_map[$specifier])) {
-        $field = field_info_field($specifier);
+      elseif (isset($field_map[$entity_type][$specifier])) {
+        $field = field_info_field($entity_type, $specifier);
       }
       else {
         $field = FALSE;
@@ -157,7 +158,7 @@ function addField($field, $type, $langcode) {
           $column = 'value';
         }
         $table = $this->ensureFieldTable($index_prefix, $field, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
-        $sql_column = _field_sql_storage_columnname($field['field_name'], $column);
+        $sql_column = DatabaseStorageController::_fieldColumnName($field, $column);
       }
       // This is an entity property (non-configurable field).
       else {
@@ -242,12 +243,12 @@ protected function ensureEntityTable($index_prefix, $property, $type, $langcode,
   protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field) {
     $field_name = $field['field_name'];
     if (!isset($this->fieldTables[$index_prefix . $field_name])) {
-      $table = $this->sqlQuery->getMetaData('age') == FIELD_LOAD_CURRENT ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
+      $table = $this->sqlQuery->getMetaData('age') == FIELD_LOAD_CURRENT ? DatabaseStorageController::_fieldTableName($field) : DatabaseStorageController::_fieldRevisionTableName($field);
       if ($field['cardinality'] != 1) {
         $this->sqlQuery->addMetaData('simple_query', FALSE);
       }
       $entity_type = $this->sqlQuery->getMetaData('entity_type');
-      $this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field AND %alias.entity_type = '$entity_type'", $langcode);
+      $this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field", $langcode);
     }
     return $this->fieldTables[$index_prefix . $field_name];
   }
diff --git a/core/modules/block/block.install b/core/modules/block/block.install
index 3c1755f..4363ef8 100644
--- a/core/modules/block/block.install
+++ b/core/modules/block/block.install
@@ -35,6 +35,10 @@ function block_update_dependencies() {
   $dependencies['block'][8005] = array(
     'user' => 8016,
   );
+  // Migrate custom blocks after field storage has been reorganized.
+  $dependencies['block'][8008] = array(
+    'field' => 8006,
+  );
   return $dependencies;
 }
 
@@ -247,9 +251,9 @@ function block_update_8008() {
 
     // First, create the body field.
     $body_field = array(
-      'id' => 'block_body',
+      'name' => 'block_body',
+      'entity_type' => 'custom_block',
       'type' => 'text_with_summary',
-      'entity_types' => array('custom_block'),
       'module' => 'text',
       'cardinality' => 1,
       'schema' => array(
@@ -283,8 +287,8 @@ function block_update_8008() {
     );
     _update_8003_field_create_field($body_field);
 
+
     $instance = array(
-      'id' => 'custom_block.basic.block_body',
       'entity_type' => 'custom_block',
       'bundle' => 'basic',
       'label' => 'Block body',
@@ -338,7 +342,7 @@ function block_update_8008() {
         );
         // This is a core update and no contrib modules are enabled yet, so
         // we can assume default field storage for a faster update.
-        _update_8000_field_sql_storage_write('custom_block', 'basic', $block->bid, $block->bid, 'block_body', $data);
+        _update_8006_field_write_data_sql('custom_block', 'basic', $block->bid, $block->bid, 'block_body', $data);
 
         $sandbox['last'] = $block->bid;
         $sandbox['count'] += 1;
diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module
index 0af9077..120d37b 100644
--- a/core/modules/block/custom_block/custom_block.module
+++ b/core/modules/block/custom_block/custom_block.module
@@ -207,13 +207,13 @@ function custom_block_entity_bundle_info() {
  */
 function custom_block_add_body_field($block_type_id, $label = 'Block body') {
   // Add or remove the body field, as needed.
-  $field = field_info_field('block_body');
+  $field = field_info_field('custom_block', 'block_body');
   $instance = field_info_instance('custom_block', 'block_body', $block_type_id);
   if (empty($field)) {
     $field = entity_create('field_entity', array(
-      'field_name' => 'block_body',
+      'name' => 'block_body',
+      'entity_type' => 'custom_block',
       'type' => 'text_with_summary',
-      'entity_types' => array('custom_block'),
     ));
     $field->save();
   }
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
index 0698234..5fb5330 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
@@ -16,7 +16,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageControllerNG class,
  * adding required special handling for custom block entities.
  */
-class CustomBlockStorageController extends DatabaseStorageControllerNG implements EntityStorageControllerInterface {
+class CustomBlockStorageController extends DatabaseStorageControllerNG {
 
   /**
    * Overrides \Drupal\Core\Entity\DatabaseStorageController::attachLoad().
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
index 7c1d7d5..721c0f8 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockFieldTest.php
@@ -65,13 +65,14 @@ public function testBlockFields() {
 
     // Create a field with settings to validate.
     $this->field = entity_create('field_entity', array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'name' => drupal_strtolower($this->randomName()),
+      'entity_type' => 'custom_block',
       'type' => 'link',
       'cardinality' => 2,
     ));
     $this->field->save();
     $this->instance = entity_create('field_instance', array(
-      'field_name' => $this->field->id(),
+      'field_name' => $this->field->name,
       'entity_type' => 'custom_block',
       'bundle' => 'link',
       'settings' => array(
diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index efaf9f3..04e1291 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -12,6 +12,10 @@ function comment_uninstall() {
   // Remove variables.
   variable_del('comment_block_count');
   $node_types = array_keys(node_type_get_types());
+  Drupal::entityManager()->addNamespaces(new ArrayIterator(array(
+    'Drupal\comment' => DRUPAL_ROOT . '/core/modules/comment/lib',
+  )));
+  drupal_classloader_register('comment', 'core/modules/comment');
   foreach ($node_types as $node_type) {
     entity_invoke_bundle_hook('delete', 'comment', 'comment_node_' . $node_type);
     variable_del('comment_' . $node_type);
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index a68f39b..94d9d81 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -322,11 +322,11 @@ function comment_node_type_delete($info) {
  */
 function _comment_body_field_create($info) {
   // Create the field if needed.
-  if (!field_read_field('comment_body', array('include_inactive' => TRUE))) {
+  if (!field_read_field('comment', 'comment_body', array('include_inactive' => TRUE))) {
     $field = entity_create('field_entity', array(
-      'field_name' => 'comment_body',
+      'name' => 'comment_body',
       'type' => 'text_long',
-      'entity_types' => array('comment'),
+      'entity_type' => 'comment',
     ));
     $field->save();
   }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
index 8928f4f..f9d25f1 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
@@ -45,7 +45,7 @@ function testCommentDefaultFields() {
     }
 
     // Check that the 'comment_body' field is deleted.
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $this->assertTrue(empty($field), 'The comment_body field was deleted');
 
     // Create a new content type.
@@ -54,7 +54,7 @@ function testCommentDefaultFields() {
 
     // Check that the 'comment_body' field exists and has an instance on the
     // new comment bundle.
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $this->assertTrue($field, 'The comment_body field exists');
     $instances = field_info_instances('comment');
     $this->assertTrue(isset($instances['comment_node_' . $type_name]['comment_body']), format_string('The comment_body field is present for comments on type @type', array('@type' => $type_name)));
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
index 84a080e..4064b22 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
@@ -71,7 +71,7 @@ function setUp() {
     $this->drupalPost("user/" . $admin_user->id() . "/edit", $edit, t('Save'));
 
     // Make comment body translatable.
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $field['translatable'] = TRUE;
     $field->save();
     $this->assertTrue(field_is_translatable('comment', $field), 'Comment body is translatable.');
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
index 296a3e2..e935ca6 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
@@ -63,7 +63,7 @@ protected function getTranslatorPermissions() {
    */
   function setupTestFields() {
     parent::setupTestFields();
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $field['translatable'] = TRUE;
     $field->save();
   }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentUninstallTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentUninstallTest.php
index 48e749f..127a611 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentUninstallTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentUninstallTest.php
@@ -42,7 +42,7 @@ protected function setUp() {
    */
   function testCommentUninstallWithField() {
     // Ensure that the field exists before uninstallation.
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $this->assertNotNull($field, 'The comment_body field exists.');
 
     // Uninstall the comment module which should trigger field deletion.
@@ -50,7 +50,7 @@ function testCommentUninstallWithField() {
     $this->container->get('module_handler')->uninstall(array('comment'));
 
     // Check that the field is now deleted.
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $this->assertNull($field, 'The comment_body field has been deleted.');
   }
 
@@ -60,12 +60,12 @@ function testCommentUninstallWithField() {
    */
   function testCommentUninstallWithoutField() {
     // Manually delete the comment_body field before module uninstallation.
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $this->assertNotNull($field, 'The comment_body field exists.');
     $field->delete();
 
     // Check that the field is now deleted.
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $this->assertNull($field, 'The comment_body field has been deleted.');
 
     // Ensure that uninstallation succeeds even if the field has already been
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
index 224340c..989c074 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
@@ -21,7 +21,7 @@ class ConfigLocaleOverride extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('locale', 'config_test', 'user', 'language', 'system');
+  public static $modules = array('locale', 'config_test', 'user', 'language', 'system', 'field');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php b/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
index c9e2e5d..271acfd 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
@@ -21,7 +21,7 @@ class MessageEntityTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'contact');
+  public static $modules = array('system', 'contact', 'field');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php b/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
index 166a64b..0b98f87 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\contact\Tests\Views;
 
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\views\Tests\ViewTestBase;
 
 /**
@@ -40,13 +41,14 @@ protected function setUp() {
     parent::setUp();
 
     $this->field = entity_create('field_entity', array(
-      'field_name' => strtolower($this->randomName()),
+      'name' => strtolower($this->randomName()),
+      'entity_type' => 'contact_message',
       'type' => 'text'
     ));
     $this->field->save();
 
     entity_create('field_instance', array(
-      'field_name' => $this->field->id(),
+      'field_name' => $this->field->name,
       'entity_type' => 'contact_message',
       'bundle' => 'contact_message',
     ))->save();
@@ -58,16 +60,11 @@ protected function setUp() {
    * Tests the views data generation.
    */
   public function testViewsData() {
-    $table_name = _field_sql_storage_tablename($this->field);
+    // Test that the field is not exposed to views, since contact_message
+    // entities have no storage.
+    $table_name = DatabaseStorageController::_fieldTableName($this->field);
     $data = $this->container->get('views.views_data')->get($table_name);
-
-    // Test that the expected data array is returned.
-    $expected = array('', '_value', '_format');
-    $this->assertEqual(count($data), count($expected), 'The expected amount of array keys were found.');
-    foreach ($expected as $suffix) {
-      $this->assertTrue(isset($data[$this->field->id() . $suffix]));
-    }
-    $this->assertTrue(empty($data['table']['join']), 'The field is not joined to the non existent contact message base table.');
+    $this->assertTrue(empty($data), 'The field is not exposed to Views.');
   }
 
 }
diff --git a/core/modules/content_translation/content_translation.admin.inc b/core/modules/content_translation/content_translation.admin.inc
index 63e71fc..a4186ba 100644
--- a/core/modules/content_translation/content_translation.admin.inc
+++ b/core/modules/content_translation/content_translation.admin.inc
@@ -95,7 +95,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
           // @todo Exploit field definitions once all core entities and field
           // types are migrated to the Entity Field API.
           foreach ($fields as $field_name => $instance) {
-            $field = field_info_field($field_name);
+            $field = $instance->getField();
             $form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
               '#label' => $instance['label'],
               '#type' => 'checkbox',
@@ -326,7 +326,7 @@ function _content_translation_update_field_translatability($settings) {
         foreach ($bundle_settings['fields'] as $field_name => $translatable) {
           // If a field is enabled for translation for at least one instance we
           // need to mark it as translatable.
-          $fields[$field_name] = $translatable || !empty($fields[$field_name]);
+          $fields[$entity_type][$field_name] = $translatable || !empty($fields[$entity_type][$field_name]);
         }
       }
       // @todo Store non-configurable field settings to be able to alter their
@@ -335,22 +335,24 @@ function _content_translation_update_field_translatability($settings) {
   }
 
   $operations = array();
-  foreach ($fields as $field_name => $translatable) {
-    $field = field_info_field($field_name);
-    if ($field['translatable'] != $translatable) {
-      // If a field is untranslatable, it can have no data except under
-      // Language::LANGCODE_NOT_SPECIFIED. Thus we need a field to be translatable before
-      // we convert data to the entity language. Conversely we need to switch
-      // data back to Language::LANGCODE_NOT_SPECIFIED before making a field
-      // untranslatable lest we lose information.
-      $field_operations = array(
-        array('content_translation_translatable_switch', array($translatable, $field_name)),
-      );
-      if ($field->hasData()) {
-        $field_operations[] = array('content_translation_translatable_batch', array($translatable, $field_name));
-        $field_operations = $translatable ? $field_operations : array_reverse($field_operations);
+  foreach ($fields as $entity_type => $entity_type_fields) {
+    foreach ($entity_type_fields as $field_name => $translatable) {
+      $field = field_info_field($entity_type, $field_name);
+      if ($field['translatable'] != $translatable) {
+        // If a field is untranslatable, it can have no data except under
+        // Language::LANGCODE_NOT_SPECIFIED. Thus we need a field to be translatable before
+        // we convert data to the entity language. Conversely we need to switch
+        // data back to Language::LANGCODE_NOT_SPECIFIED before making a field
+        // untranslatable lest we lose information.
+        $field_operations = array(
+          array('content_translation_translatable_switch', array($translatable, $entity_type, $field_name)),
+        );
+        if ($field->hasData()) {
+          $field_operations[] = array('content_translation_translatable_batch', array($translatable, $field_name));
+          $field_operations = $translatable ? $field_operations : array_reverse($field_operations);
+        }
+        $operations = array_merge($operations, $field_operations);
       }
-      $operations = array_merge($operations, $field_operations);
     }
   }
 
@@ -374,11 +376,13 @@ function _content_translation_update_field_translatability($settings) {
  * @param bool $translatable
  *   Indicator of whether the field should be made translatable (TRUE) or
  *   untranslatble (FALSE).
+ * @param string $entity_type
+ *   Field entity type.
  * @param string $field_name
  *   Field machine name.
  */
-function content_translation_translatable_switch($translatable, $field_name) {
-  $field = field_info_field($field_name);
+function content_translation_translatable_switch($translatable, $entity_type, $field_name) {
+  $field = field_info_field($entity_type, $field_name);
   if ($field['translatable'] !== $translatable) {
     $field['translatable'] = $translatable;
     $field->save();
@@ -395,10 +399,6 @@ function content_translation_translatable_switch($translatable, $field_name) {
  *   Field machine name.
  */
 function content_translation_translatable_batch($translatable, $field_name, &$context) {
-  $field = field_info_field($field_name);
-  $column = isset($field['columns']['value']) ? 'value' : key($field['columns']);
-  $query_field = "$field_name.$column";
-
   // Determine the entity types to act on.
   $entity_types = array();
   foreach (field_info_instances() as $entity_type => $info) {
@@ -417,6 +417,10 @@ function content_translation_translatable_batch($translatable, $field_name, &$co
     $context['sandbox']['max'] = 0;
 
     foreach ($entity_types as $entity_type) {
+      $field = field_info_field($entity_type, $field_name);
+      $column = isset($field['columns']['value']) ? 'value' : key($field['columns']);
+      $query_field = "$field_name.$column";
+
       // How many entities will need processing?
       $query = Drupal::entityQuery($entity_type);
       $count = $query
@@ -444,6 +448,9 @@ function content_translation_translatable_batch($translatable, $field_name, &$co
     $info = entity_get_info($entity_type);
     $offset = $context['sandbox']['progress_entity_type'][$entity_type];
     $query = Drupal::entityQuery($entity_type);
+    $field = field_info_field($entity_type, $field_name);
+    $column = isset($field['columns']['value']) ? 'value' : key($field['columns']);
+    $query_field = "$field_name.$column";
     $result = $query
       ->exists($query_field)
       ->sort($info['entity_keys']['id'])
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 34e918c..3edb6aa 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -200,7 +200,7 @@ function content_translation_menu() {
     }
   }
 
-  $items['admin/config/regional/content_translation/translatable/%'] = array(
+  $items['admin/config/regional/content_translation/translatable/%/%'] = array(
     'title' => 'Confirm change in translatability.',
     'description' => 'Confirm page for changing field translatability.',
     'route_name' => 'content_translation_translatable',
@@ -621,7 +621,7 @@ function content_translation_form_alter(array &$form, array &$form_state) {
       else {
         foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
           $field_name = $instance['field_name'];
-          $field = field_info_field($field_name);
+          $field = $instance->getField();
           $form[$field_name]['#multilingual'] = !empty($field['translatable']);
         }
       }
@@ -808,6 +808,7 @@ function content_translation_field_extra_fields() {
  */
 function content_translation_form_field_ui_field_edit_form_alter(array &$form, array &$form_state, $form_id) {
   $field = $form['#field'];
+  $entity_type = $field['entity_type'];
   $field_name = $field['field_name'];
   $translatable = $field['translatable'];
   $label = t('Field translation');
@@ -821,7 +822,7 @@ function content_translation_form_field_ui_field_edit_form_alter(array &$form, a
         '#type' => 'link',
         '#prefix' => t('This field has data in existing content.') . ' ',
         '#title' => !$translatable ? t('Enable translation') : t('Disable translation'),
-        '#href' => 'admin/config/regional/content_translation/translatable/' . $field_name,
+        '#href' => "admin/config/regional/content_translation/translatable/$entity_type/$field_name",
         '#options' => array('query' => drupal_get_destination()),
         '#access' => user_access('administer content translation'),
       ),
@@ -1022,7 +1023,7 @@ function content_translation_save_settings($settings) {
         // Store whether fields have translation enabled or not.
         if (!empty($bundle_settings['columns'])) {
           foreach ($bundle_settings['columns'] as $field_name => $column_settings) {
-            $field = field_info_field($field_name);
+            $field = field_info_field($entity_type, $field_name);
             $instance = field_info_instance($entity_type, $field_name, $bundle);
             if ($field['translatable']) {
               $instance['settings']['translation_sync'] = $column_settings;
diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc
index e277791..966d259 100644
--- a/core/modules/content_translation/content_translation.pages.inc
+++ b/core/modules/content_translation/content_translation.pages.inc
@@ -40,8 +40,7 @@ function content_translation_overview(EntityInterface $entity) {
     // Determine whether the current entity is translatable.
     $translatable = FALSE;
     foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-      $field_name = $instance['field_name'];
-      $field = field_info_field($field_name);
+      $field = $instance->getField();
       if ($field['translatable']) {
         $translatable = TRUE;
         break;
@@ -246,7 +245,7 @@ function content_translation_prepare_translation(EntityInterface $entity, Langua
   else {
     $instances = field_info_instances($entity->entityType(), $entity->bundle());
     foreach ($instances as $field_name => $instance) {
-      $field = field_info_field($field_name);
+      $field = $instance->getField();
       if (!empty($field['translatable'])) {
         $value = $entity->get($field_name);
         $value[$target->id] = isset($value[$source->id]) ? $value[$source->id] : array();
diff --git a/core/modules/content_translation/content_translation.routing.yml b/core/modules/content_translation/content_translation.routing.yml
index d7d55a1..64f210c 100644
--- a/core/modules/content_translation/content_translation.routing.yml
+++ b/core/modules/content_translation/content_translation.routing.yml
@@ -1,5 +1,5 @@
 content_translation_translatable:
-  pattern: 'admin/config/regional/content_translation/translatable/{field_name}'
+  pattern: 'admin/config/regional/content_translation/translatable/{entity_type}/{field_name}'
   defaults:
     _form: 'Drupal\content_translation\Form\TranslatableForm'
   requirements:
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
index 08cc693..56892de 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
@@ -51,7 +51,7 @@ public function removeTranslation(EntityInterface $entity, $langcode) {
     // Remove field translations.
     foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
       $field_name = $instance['field_name'];
-      $field = field_info_field($field_name);
+      $field = $instance->getField();
       if ($field['translatable']) {
         $entity->{$field_name}[$langcode] = array();
       }
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Form/TranslatableForm.php b/core/modules/content_translation/lib/Drupal/content_translation/Form/TranslatableForm.php
index bdeb983..ceb771c 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Form/TranslatableForm.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Form/TranslatableForm.php
@@ -8,7 +8,8 @@
 namespace Drupal\content_translation\Form;
 
 use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\field\Field;
+use Drupal\field\Plugin\Core\Entity\Field;
+use Drupal\field\field as FieldInfo;
 
 /**
  * Provides a confirm form for changing translatable status on translation
@@ -19,9 +20,9 @@ class TranslatableForm extends ConfirmFormBase {
   /**
    * The field info we are changing translatable status on.
    *
-   * @var array.
+   * @var \Drupal\field\Plugin\Core\Entity\Field
    */
-  protected $fieldInfo;
+  protected $field;
 
   /**
    * The field name we are changing translatable
@@ -42,7 +43,7 @@ public function getFormID() {
    * {@inheritdoc}
    */
   public function getQuestion() {
-    if ($field['translatable']) {
+    if ($this->field['translatable']) {
       $question = t('Are you sure you want to disable translation for the %name field?', array('%name' => $this->fieldName));
     }
     else {
@@ -58,7 +59,7 @@ public function getDescription() {
     $description = t('By submitting this form these changes will apply to the %name field everywhere it is used.',
       array('%name' => $this->fieldName)
     );
-    $description .= $this->fieldInfo['translatable'] ? "<br>" . t("<strong>All the existing translations of this field will be deleted.</strong><br>This action cannot be undone.") : '';
+    $description .= $this->field['translatable'] ? "<br>" . t("<strong>All the existing translations of this field will be deleted.</strong><br>This action cannot be undone.") : '';
     return $description;
   }
 
@@ -71,12 +72,14 @@ public function getCancelPath() {
 
   /**
    * {@inheritdoc}
+   * @param string $entity_type
+   *   The entity type.
    * @param string $field_name
    *   The field name.
    */
-  public function buildForm(array $form, array &$form_state, $field_name = NULL) {
+  public function buildForm(array $form, array &$form_state, $entity_type = NULL, $field_name = NULL) {
     $this->fieldName = $field_name;
-    $this->fieldInfo = Field::fieldInfo($field_name);
+    $this->fieldInfo = FieldInfo::fieldInfo()->getField($entity_type, $field_name);
 
     return parent::buildForm($form, $form_state);
   }
@@ -127,6 +130,7 @@ public function submitForm(array &$form, array &$form_state) {
       array(
         'content_translation_translatable_switch', array(
           !$translatable,
+          $this->field['entity_type'],
           $this->fieldName,
         ),
       ),
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php
index c17bd06..f4e2f13 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSettingsTest.php
@@ -89,7 +89,7 @@ function testSettingsUI() {
     );
     $this->assertSettings('comment', 'comment_node_article', TRUE, $edit);
     field_info_cache_clear();
-    $field = field_info_field('comment_body');
+    $field = field_info_field('comment', 'comment_body');
     $this->assertTrue($field['translatable'], 'Comment body is translatable.');
 
     // Test that language settings are correctly stored.
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSyncImageTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSyncImageTest.php
index 54d234e..c813790 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSyncImageTest.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationSyncImageTest.php
@@ -57,7 +57,8 @@ protected function setupTestFields() {
     $this->cardinality = 3;
 
     entity_create('field_entity', array(
-      'field_name' => $this->fieldName,
+      'name' => $this->fieldName,
+      'entity_type' => $this->entityType,
       'type' => 'image',
       'cardinality' => $this->cardinality,
       'translatable' => TRUE,
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
index b635c45..0ea5ae6 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationTestBase.php
@@ -162,8 +162,9 @@ protected function setupTestFields() {
     $this->fieldName = 'field_test_et_ui_test';
 
     entity_create('field_entity', array(
-      'field_name' => $this->fieldName,
+      'name' => $this->fieldName,
       'type' => 'text',
+      'entity_type' => $this->entityType,
       'cardinality' => 1,
       'translatable' => TRUE,
     ))->save();
diff --git a/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeItemTest.php b/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeItemTest.php
index 16731a2..f755730 100644
--- a/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeItemTest.php
+++ b/core/modules/datetime/lib/Drupal/datetime/Tests/DateTimeItemTest.php
@@ -36,8 +36,9 @@ public function setUp() {
 
     // Create a field with settings to validate.
     $this->field = entity_create('field_entity', array(
-      'field_name' => 'field_datetime',
+      'name' => 'field_datetime',
       'type' => 'datetime',
+      'entity_type' => 'entity_test',
       'settings' => array('datetime_type' => 'date'),
     ));
     $this->field->save();
diff --git a/core/modules/datetime/lib/Drupal/datetime/Tests/DatetimeFieldTest.php b/core/modules/datetime/lib/Drupal/datetime/Tests/DatetimeFieldTest.php
index af66e0d..3c739be 100644
--- a/core/modules/datetime/lib/Drupal/datetime/Tests/DatetimeFieldTest.php
+++ b/core/modules/datetime/lib/Drupal/datetime/Tests/DatetimeFieldTest.php
@@ -57,13 +57,14 @@ function setUp() {
 
     // Create a field with settings to validate.
     $this->field = entity_create('field_entity', array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'name' => drupal_strtolower($this->randomName()),
+      'entity_type' => 'entity_test',
       'type' => 'datetime',
       'settings' => array('datetime_type' => 'date'),
     ));
     $this->field->save();
     $this->instance = entity_create('field_instance', array(
-      'field_name' => $this->field->id(),
+      'field_name' => $this->field->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
       'settings' => array(
@@ -73,7 +74,7 @@ function setUp() {
     $this->instance->save();
 
     entity_get_form_display($this->instance->entity_type, $this->instance->bundle, 'default')
-      ->setComponent($this->field->id(), array(
+      ->setComponent($this->field->name, array(
         'type' => 'datetime_default',
       ))
       ->save();
@@ -84,7 +85,7 @@ function setUp() {
       'settings' => array('format_type' => 'medium'),
     );
     entity_get_display($this->instance->entity_type, $this->instance->bundle, 'full')
-      ->setComponent($this->field->id(), $this->display_options)
+      ->setComponent($this->field->name, $this->display_options)
       ->save();
   }
 
@@ -92,7 +93,7 @@ function setUp() {
    * Tests date field functionality.
    */
   function testDateField() {
-    $field_name = $this->field->id();
+    $field_name = $this->field->name;
 
     // Display creation form.
     $this->drupalGet('entity_test/add');
@@ -160,7 +161,7 @@ function testDateField() {
    * Tests date and time field.
    */
   function testDatetimeField() {
-    $field_name = $this->field->id();
+    $field_name = $this->field->name;
     // Change the field to a datetime field.
     $this->field['settings']['datetime_type'] = 'datetime';
     $this->field->save();
@@ -229,7 +230,7 @@ function testDatetimeField() {
    * Tests Date List Widget functionality.
    */
   function testDatelistWidget() {
-    $field_name = $this->field->id();
+    $field_name = $this->field->name;
     // Change the field to a datetime field.
     $this->field->settings['datetime_type'] = 'datetime';
     $this->field->save();
@@ -299,7 +300,7 @@ function testDefaultValue() {
     // Change the field to a datetime field.
     $this->field->settings['datetime_type'] = 'datetime';
     $this->field->save();
-    $field_name = $this->field->id();
+    $field_name = $this->field->name;
 
     // Set the default value to 'now'.
     $this->instance->settings['default_value'] = 'now';
@@ -341,7 +342,7 @@ function testInvalidField() {
     // Change the field to a datetime field.
     $this->field->settings['datetime_type'] = 'datetime';
     $this->field->save();
-    $field_name = $this->field->id();
+    $field_name = $this->field->name;
 
     // Display creation form.
     $this->drupalGet('entity_test/add');
diff --git a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
index 9559be7..01dcf0a 100644
--- a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
+++ b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
@@ -42,7 +42,8 @@ public function access(Route $route, Request $request) {
    * {@inheritdoc}
    */
   public function accessEditEntityField(EntityInterface $entity, $field_name) {
-    return $entity->access('update') && ($field = field_info_field($field_name)) && field_access('edit', $field, $entity->entityType(), $entity);
+    $entity_type = $entity->entityType();
+    return $entity->access('update') && ($field = field_info_field($entity_type, $field_name)) && field_access('edit', $field, $entity_type, $entity);
   }
 
   /**
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
index 649f033..a7d38af 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
@@ -19,7 +19,7 @@ class EditTestBase extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'text', 'edit');
+  public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_test', 'number', 'text', 'edit');
   /**
    * Sets the default field storage backend for fields created during tests.
    */
@@ -55,7 +55,8 @@ function setUp() {
   function createFieldWithInstance($field_name, $type, $cardinality, $label, $instance_settings, $widget_type, $widget_settings, $formatter_type, $formatter_settings) {
     $field = $field_name . '_field';
     $this->$field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => $type,
       'cardinality' => $cardinality,
     ));
diff --git a/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php b/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
index 2d76d76..25e599b 100644
--- a/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
+++ b/core/modules/email/lib/Drupal/email/Tests/EmailFieldTest.php
@@ -62,7 +62,8 @@ function testEmailField() {
     // Create a field with settings to validate.
     $field_name = drupal_strtolower($this->randomName());
     $this->field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'email',
     ));
     $this->field->save();
diff --git a/core/modules/email/lib/Drupal/email/Tests/EmailItemTest.php b/core/modules/email/lib/Drupal/email/Tests/EmailItemTest.php
index 7e72163..b4a1bfe 100644
--- a/core/modules/email/lib/Drupal/email/Tests/EmailItemTest.php
+++ b/core/modules/email/lib/Drupal/email/Tests/EmailItemTest.php
@@ -36,7 +36,8 @@ public function setUp() {
 
     // Create an email field and instance for validation.
     entity_create('field_entity', array(
-      'field_name' => 'field_email',
+      'name' => 'field_email',
+      'entity_type' => 'entity_test',
       'type' => 'email',
     ))->save();
     entity_create('field_instance', array(
diff --git a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php
index 6a1c9de..128bc7c 100644
--- a/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php
+++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php
@@ -209,7 +209,7 @@ public function setComponent($name, array $options = array()) {
     }
 
     if ($instance = field_info_instance($this->targetEntityType, $name, $this->bundle)) {
-      $field = field_info_field($instance['field_name']);
+      $field = $instance->getField();
       $options = $this->pluginManager->prepareConfiguration($field['type'], $options);
 
       // Clear the persisted plugin, if any.
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
index fdeaa1b..637489b 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
@@ -131,7 +131,7 @@ public function testExtraFieldComponent() {
    * Tests the behavior of a field component within an EntityDisplay object.
    */
   public function testFieldComponent() {
-    $this->enableModules(array('field_sql_storage', 'field_test'));
+    $this->enableModules(array('field_test'));
 
     $display = entity_create('entity_display', array(
       'targetEntityType' => 'entity_test',
@@ -142,7 +142,8 @@ public function testFieldComponent() {
     $field_name = 'test_field';
     // Create a field and an instance.
     $field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'test_field'
     ));
     $field->save();
@@ -202,7 +203,7 @@ public function testFieldComponent() {
    * Tests renaming and deleting a bundle.
    */
   public function testRenameDeleteBundle() {
-    $this->enableModules(array('field_sql_storage', 'field_test', 'node', 'system', 'text'));
+    $this->enableModules(array('field_test', 'node', 'system', 'text'));
     $this->installSchema('system', array('variable'));
     $this->installSchema('node', array('node'));
 
@@ -239,12 +240,13 @@ public function testRenameDeleteBundle() {
    * Tests deleting field instance.
    */
   public function testDeleteFieldInstance() {
-    $this->enableModules(array('field_sql_storage', 'field_test'));
+    $this->enableModules(array('field_test'));
 
     $field_name = 'test_field';
     // Create a field and an instance.
     $field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'test_field'
     ));
     $field->save();
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
index 8d7df46..595ac74 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
@@ -53,7 +53,7 @@ public function testEntityGetFromDisplay() {
    * Tests the behavior of a field component within an EntityFormDisplay object.
    */
   public function testFieldComponent() {
-    $this->enableModules(array('field_sql_storage', 'field_test'));
+    $this->enableModules(array('field_test'));
 
     $form_display = entity_create('entity_form_display', array(
       'targetEntityType' => 'entity_test',
@@ -64,7 +64,8 @@ public function testFieldComponent() {
     // Create a field and an instance.
     $field_name = 'test_field';
     $field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'test_field'
     ));
     $field->save();
diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module
index 52bb75f..31ace31 100644
--- a/core/modules/entity_reference/entity_reference.module
+++ b/core/modules/entity_reference/entity_reference.module
@@ -50,7 +50,7 @@ function entity_reference_field_info() {
 function entity_reference_entity_field_info_alter(&$info, $entity_type) {
   foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
     foreach ($instances as $field_name => $instance) {
-      $field = field_info_field($field_name);
+      $field = $instance->getField();
       if ($field['type'] != 'entity_reference') {
         continue;
       }
@@ -165,7 +165,7 @@ function entity_reference_field_entity_update(FieldInterface $field) {
     return;
   }
 
-  $field_name = $field->id();
+  $field_name = $field->getFieldName();
 
   foreach ($field->bundles() as $entity_type => $bundles) {
     foreach ($bundles as $bundle) {
@@ -394,14 +394,14 @@ function entity_reference_create_instance($entity_type, $bundle, $field_name, $f
   }
 
   // Look for or add the specified field to the requested entity bundle.
-  $field = field_info_field($field_name);
+  $field = field_info_field($entity_type, $field_name);
   $instance = field_info_instance($entity_type, $field_name, $bundle);
 
   if (empty($field)) {
     $field = array(
-      'field_name' => $field_name,
+      'name' => $field_name,
       'type' => 'entity_reference',
-      'entity_types' => array($entity_type),
+      'entity_type' => $entity_type,
       'settings' => array(
         'target_type' => $target_entity_type,
       ),
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php b/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php
index b6099f1..644304a 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php
@@ -69,7 +69,7 @@ public static function create(ContainerInterface $container) {
    *   The matched labels as json.
    */
   public function handleAutocomplete(Request $request, $type, $field_name, $entity_type, $bundle_name, $entity_id) {
-    if (!$field = field_info_field($field_name)) {
+    if (!$field = field_info_field($entity_type, $field_name)) {
       throw new AccessDeniedHttpException();
     }
 
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
index de4729c..ab8886d 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
@@ -108,7 +108,7 @@ public static function settingsForm(&$field, &$instance) {
     $fields = drupal_map_assoc(drupal_schema_fields_sql($entity_info['base_table']));
     foreach (field_info_instances($field['settings']['target_type']) as $bundle_instances) {
       foreach ($bundle_instances as $instance_name => $instance_info) {
-        $field_info = field_info_field($instance_name);
+        $field_info = $instance_info->getField();
         foreach ($field_info['columns'] as $column_name => $column_info) {
           $fields[$instance_name . '.' . $column_name] = t('@label (@column)', array('@label' => $instance_info['label'], '@column' => $column_name));
         }
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
index f5f74d0..6cccb4d 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAutoCreateTest.php
@@ -36,12 +36,13 @@ function setUp() {
     $this->referenced_type = $referenced->type;
 
     entity_create('field_entity', array(
+      'name' => 'test_field',
+      'entity_type' => 'node',
       'translatable' => FALSE,
       'entity_types' => array(),
       'settings' => array(
         'target_type' => 'node',
       ),
-      'field_name' => 'test_field',
       'type' => 'entity_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
     ))->save();
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
index 8c17fd6..2a50949 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\entity_reference\Tests;
 
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\Field\FieldInterface;
 use Drupal\Core\Entity\Field\FieldItemInterface;
 use Drupal\Core\Language\Language;
@@ -110,16 +111,16 @@ public function testEntityReferenceItem() {
    * Tests foreign key support.
    */
   public function testEntityReferenceFieldSchema() {
-    $field = field_info_field('field_test_taxonomy');
+    $field = field_info_field('entity_test', 'field_test_taxonomy');
     $foreign_key_column_name = 'target_id';
 
     // Grab the SQL schema and verify that the 'foreign keys' are present.
-    $schemas = _field_sql_storage_schema($field);
-    $schema = $schemas[_field_sql_storage_tablename($field)];
+    $schemas = DatabaseStorageController::_fieldSqlSchema($field);
+    $schema = $schemas[DatabaseStorageController::_fieldTableName($field)];
     $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema.');
 
     $foreign_key = reset($schema['foreign keys']);
-    $foreign_key_column = _field_sql_storage_columnname($field['field_name'], $foreign_key_column_name);
+    $foreign_key_column = DatabaseStorageController::_fieldColumnName($field, $foreign_key_column_name);
     $this->assertEqual($foreign_key['table'], 'taxonomy_term_data', 'Foreign key table name preserved in the schema.');
     $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'tid', 'Foreign key column name preserved in the schema.');
 
@@ -127,10 +128,10 @@ public function testEntityReferenceFieldSchema() {
     // foreign key is present.
     $field_name = 'field_test_vocabulary';
     entity_reference_create_instance('entity_test', 'entity_test', $field_name, 'Test vocabulary reference', 'taxonomy_vocabulary');
-    $field = field_info_field($field_name);
+    $field = field_info_field('entity_test', $field_name);
 
-    $schemas = _field_sql_storage_schema($field);
-    $schema = $schemas[_field_sql_storage_tablename($field)];
+    $schemas = DatabaseStorageController::_fieldSqlSchema($field);
+    $schema = $schemas[DatabaseStorageController::_fieldTableName($field)];
     $this->assertFalse(isset($schema['foreign keys']), 'There is no foreign key in the schema.');
   }
 }
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
index c3107ec..b97dce4 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
@@ -24,7 +24,7 @@ public static function getInfo() {
     );
   }
 
-  public static $modules = array('node', 'comment', 'entity_reference');
+  public static $modules = array('node', 'comment', 'entity_reference', 'entity_test');
 
   function setUp() {
     parent::setUp();
@@ -61,19 +61,20 @@ protected function assertReferenceable(FieldDefinitionInterface $field_definitio
   public function testNodeHandler() {
     // Create a field and instance.
     $field = entity_create('field_entity', array(
+      'name' => 'test_field',
+      'entity_type' => 'entity_test',
       'translatable' => FALSE,
       'entity_types' => array(),
       'settings' => array(
         'target_type' => 'node',
       ),
-      'field_name' => 'test_field',
       'type' => 'entity_reference',
       'cardinality' => '1',
     ));
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'default',
@@ -205,19 +206,19 @@ public function testNodeHandler() {
   public function testUserHandler() {
     // Create a field and instance.
     $field = entity_create('field_entity', array(
+      'name' => 'test_field',
+      'entity_type' => 'entity_test',
       'translatable' => FALSE,
-      'entity_types' => array(),
       'settings' => array(
         'target_type' => 'user',
       ),
-      'field_name' => 'test_field',
       'type' => 'entity_reference',
       'cardinality' => '1',
     ));
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'default',
@@ -351,19 +352,20 @@ public function testUserHandler() {
   public function testCommentHandler() {
     // Create a field and instance.
     $field = entity_create('field_entity', array(
+      'name' => 'test_field',
+      'entity_type' => 'entity_test',
       'translatable' => FALSE,
       'entity_types' => array(),
       'settings' => array(
         'target_type' => 'comment',
       ),
-      'field_name' => 'test_field',
       'type' => 'entity_reference',
       'cardinality' => '1',
     ));
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'default',
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
index b44c54d..6ed2ace 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
@@ -22,7 +22,7 @@ public static function getInfo() {
     );
   }
 
-  public static $modules = array('node', 'entity_reference');
+  public static $modules = array('node', 'entity_reference', 'entity_test');
 
   function setUp() {
     parent::setUp();
@@ -37,7 +37,8 @@ function setUp() {
   public function testSort() {
     // Add text field to entity, to sort by.
     entity_create('field_entity', array(
-      'field_name' => 'field_text',
+      'name' => 'field_text',
+      'entity_type' => 'node',
       'type' => 'text',
       'entity_types' => array('node'),
     ))->save();
@@ -54,19 +55,19 @@ public function testSort() {
 
     // Create a field and instance.
     $field = entity_create('field_entity', array(
+      'name' => 'test_field',
+      'entity_type' => 'entity_test',
       'translatable' => FALSE,
-      'entity_types' => array(),
       'settings' => array(
         'target_type' => 'node',
       ),
-      'field_name' => 'test_field',
       'type' => 'entity_reference',
       'cardinality' => 1,
     ));
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'default',
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
index cad4bd8..9c615e8 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
@@ -14,7 +14,7 @@
  */
 class SelectionTest extends WebTestBase {
 
-  public static $modules = array('views', 'entity_reference', 'entity_reference_test');
+  public static $modules = array('views', 'entity_reference', 'entity_reference_test', 'entity_test');
 
   public static function getInfo() {
     return array(
@@ -41,19 +41,19 @@ public function testSelectionHandler() {
 
     // Create a field and instance.
     $field = entity_create('field_entity', array(
+      'name' => 'test_field',
+      'entity_type' => 'entity_test',
       'translatable' => FALSE,
-      'entity_types' => array(),
       'settings' => array(
         'target_type' => 'node',
       ),
-      'field_name' => 'test_field',
       'type' => 'entity_reference',
       'cardinality' => '1',
     ));
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'views',
diff --git a/core/modules/field/config/field.settings.yml b/core/modules/field/config/field.settings.yml
index 5ca0000..6b569a9 100644
--- a/core/modules/field/config/field.settings.yml
+++ b/core/modules/field/config/field.settings.yml
@@ -1,3 +1,2 @@
-default_storage: field_sql_storage
 language_fallback: '1'
 purge_batch_size: 10
diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml
index 1b23198..2a942ea 100644
--- a/core/modules/field/config/schema/field.schema.yml
+++ b/core/modules/field/config/schema/field.schema.yml
@@ -4,9 +4,6 @@ field.settings:
   type: mapping
   label: 'Field settings'
   mapping:
-    default_storage:
-      type: string
-      label: 'The default storage backend for a field'
     language_fallback:
       type: boolean
       label: 'Whether the field display falls back to global language fallback configuration'
@@ -30,6 +27,9 @@ field.field.*:
     langcode:
       type: string
       label: 'Default language'
+    entity_type:
+      type: string
+      label: 'Entity type'
     type:
       type: string
       label: 'Type'
@@ -41,28 +41,6 @@ field.field.*:
     active:
       type: boolean
       label: 'Active'
-    entity_types:
-      type: sequence
-      label: 'Allowed entity types'
-      sequence:
-        - type: string
-          label: 'Entity type'
-    storage:
-      type: mapping
-      label: 'Storage'
-      mapping:
-        type:
-          type: string
-          label: 'Type'
-        settings:
-          type: field_storage.[%parent.type].settings
-          label: 'Settings'
-        module:
-          type: string
-          label: 'Module'
-        active:
-          type: boolean
-          label: 'Active'
     locked:
       type: boolean
       label: 'Locked'
@@ -101,9 +79,6 @@ field.instance.*.*.*:
     field_uuid:
       type: string
       label: 'Field UUID'
-    entity_type:
-      type: string
-      label: 'Allowed entity types'
     bundle:
       type: string
       label: 'Bundle'
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 312ebb5..3acbfb4 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -400,31 +400,6 @@ function hook_field_attach_preprocess_alter(&$variables, $context) {
 }
 
 /**
- * Act on field_purge_data().
- *
- * This hook is invoked in field_purge_data() and allows modules to act on
- * purging data from a single field pseudo-entity. For example, if a module
- * relates data in the field with its own data, it may purge its own data during
- * this process as well.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The pseudo-entity whose field data is being purged.
- * @param $field
- *   The (possibly deleted) field whose data is being purged.
- * @param $instance
- *   The deleted field instance whose data is being purged.
- *
- * @see @link field_purge Field API bulk data deletion @endlink
- * @see field_purge_data()
- */
-function hook_field_attach_purge(\Drupal\Core\Entity\EntityInterface $entity, $field, $instance) {
-  // find the corresponding data in mymodule and purge it
-  if ($entity->entityType() == 'node' && $field->field_name == 'my_field_name') {
-    mymodule_remove_mydata($entity->id());
-  }
-}
-
-/**
  * Perform alterations on field_attach_view() or field_view_field().
  *
  * This hook is invoked after the field module has performed the operation.
@@ -512,681 +487,6 @@ function hook_field_available_languages_alter(&$langcodes, $context) {
  */
 
 /**
- * @addtogroup field_storage
- * @{
- */
-
-/**
- * Expose Field API storage backends.
- *
- * @return
- *   An array describing the storage backends implemented by the module. The
- *   keys are storage backend names. To avoid name clashes, storage backend
- *   names should be prefixed with the name of the module that exposes them. The
- *   values are arrays describing the storage backend, with the following
- *   key/value pairs:
- *   - label: The human-readable name of the storage backend.
- *   - description: A short description for the storage backend.
- *   - settings: An array whose keys are the names of the settings available to
- *     the storage backend, and whose values are the default values of those
- *     settings.
- */
-function hook_field_storage_info() {
-  return array(
-    'field_sql_storage' => array(
-      'label' => t('Default SQL storage'),
-      'description' => t('Stores fields in the local SQL database, using per-field tables.'),
-      'settings' => array(),
-    ),
-  );
-}
-
-/**
- * Perform alterations on Field API storage types.
- *
- * @param $info
- *   Array of informations on storage types exposed by
- *   hook_field_field_storage_info() implementations.
- */
-function hook_field_storage_info_alter(&$info) {
-  // Add a setting to a storage type.
-  $info['field_sql_storage']['settings'] += array(
-    'mymodule_additional_setting' => 'default value',
-  );
-}
-
-/**
- * Reveal the internal details about the storage for a field.
- *
- * For example, an SQL storage module might return the Schema API structure for
- * the table. A key/value storage module might return the server name,
- * authentication credentials, and bin name.
- *
- * Field storage modules are not obligated to implement this hook. Modules that
- * rely on these details must only use them for read operations.
- *
- * @param $field
- *   A field structure.
- *
- * @return
- *   An array of details.
- *    - The first dimension is a store type (sql, solr, etc).
- *    - The second dimension indicates the age of the values in the store
- *      FIELD_LOAD_CURRENT or FIELD_LOAD_REVISION.
- *    - Other dimensions are specific to the field storage module.
- *
- * @see hook_field_storage_details_alter()
- */
-function hook_field_storage_details($field) {
-  $details = array();
-
-  // Add field columns.
-  foreach ((array) $field['columns'] as $column_name => $attributes) {
-    $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
-    $columns[$column_name] = $real_name;
-  }
-  return array(
-    'sql' => array(
-      FIELD_LOAD_CURRENT => array(
-        _field_sql_storage_tablename($field) => $columns,
-      ),
-      FIELD_LOAD_REVISION => array(
-        _field_sql_storage_revision_tablename($field) => $columns,
-      ),
-    ),
-  );
-}
-
-/**
- * Perform alterations on Field API storage details.
- *
- * @param $details
- *   An array of storage details for fields as exposed by
- *   hook_field_storage_details() implementations.
- * @param $field
- *   A field structure.
- *
- * @see hook_field_storage_details()
- */
-function hook_field_storage_details_alter(&$details, $field) {
-  if ($field['field_name'] == 'field_of_interest') {
-    $columns = array();
-    foreach ((array) $field['columns'] as $column_name => $attributes) {
-      $columns[$column_name] = $column_name;
-    }
-    $details['drupal_variables'] = array(
-      FIELD_LOAD_CURRENT => array(
-        'moon' => $columns,
-      ),
-      FIELD_LOAD_REVISION => array(
-        'mars' => $columns,
-      ),
-    );
-  }
-}
-
-/**
- * Load field data for a set of entities.
- *
- * This hook is invoked from field_attach_load() to ask the field storage module
- * to load field data.
- *
- * Modules implementing this hook should load field values and add them to
- * objects in $entities. Fields with no values should be added as empty arrays.
- *
- * @param $entity_type
- *   The type of entity, such as 'node' or 'user'.
- * @param $entities
- *   The array of entity objects to add fields to, keyed by entity ID.
- * @param $age
- *   FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- *   FIELD_LOAD_REVISION to load the version indicated by each entity.
- * @param $fields
- *   An array listing the fields to be loaded. The keys of the array are field
- *   UUIDs, and the values of the array are the entity IDs (or revision IDs,
- *   depending on the $age parameter) to add each field to.
- * @param $options
- *   An associative array of additional options, with the following keys:
- *   - deleted: If TRUE, deleted fields should be loaded as well as non-deleted
- *     fields. If unset or FALSE, only non-deleted fields should be loaded.
- */
-function hook_field_storage_load($entity_type, $entities, $age, $fields, $options) {
-  $load_current = $age == FIELD_LOAD_CURRENT;
-
-  foreach ($fields as $field_id => $ids) {
-    // By the time this hook runs, the relevant field definitions have been
-    // populated and cached in FieldInfo, so calling field_info_field_by_id()
-    // on each field individually is more efficient than loading all fields in
-    // memory upfront with field_info_field_by_ids().
-    $field = field_info_field_by_id($field_id);
-    $field_name = $field['field_name'];
-    $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
-
-    $query = db_select($table, 't')
-      ->fields('t')
-      ->condition('entity_type', $entity_type)
-      ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
-      ->condition('langcode', field_available_languages($entity_type, $field), 'IN')
-      ->orderBy('delta');
-
-    if (empty($options['deleted'])) {
-      $query->condition('deleted', 0);
-    }
-
-    $results = $query->execute();
-
-    $delta_count = array();
-    foreach ($results as $row) {
-      if (!isset($delta_count[$row->entity_id][$row->langcode])) {
-        $delta_count[$row->entity_id][$row->langcode] = 0;
-      }
-
-      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
-        $item = array();
-        // For each column declared by the field, populate the item
-        // from the prefixed database column.
-        foreach ($field['columns'] as $column => $attributes) {
-          $column_name = _field_sql_storage_columnname($field_name, $column);
-          $item[$column] = $row->$column_name;
-        }
-
-        // Add the item to the field values for the entity.
-        $entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
-        $delta_count[$row->entity_id][$row->langcode]++;
-      }
-    }
-  }
-}
-
-/**
- * Write field data for an entity.
- *
- * This hook is invoked from field_attach_insert() and field_attach_update(), to
- * ask the field storage module to save field data.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity on which to operate.
- * @param $op
- *   FIELD_STORAGE_UPDATE when updating an existing entity,
- *   FIELD_STORAGE_INSERT when inserting a new entity.
- * @param $fields
- *   An array listing the fields to be written. The keys and values of the
- *   array are field UUIDs.
- */
-function hook_field_storage_write(\Drupal\Core\Entity\EntityInterface $entity, $op, $fields) {
-  $id = $entity->id();
-  $vid = $entity->getRevisionId();
-  $bundle = $entity->bundle();
-  if (!isset($vid)) {
-    $vid = $id;
-  }
-
-  foreach ($fields as $field_id) {
-    $field = field_info_field_by_id($field_id);
-    $field_name = $field['field_name'];
-    $table_name = _field_sql_storage_tablename($field);
-    $revision_name = _field_sql_storage_revision_tablename($field);
-
-    $all_langcodes = field_available_languages($entity->entityType(), $field);
-    $field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
-
-    // Delete and insert, rather than update, in case a value was added.
-    if ($op == FIELD_STORAGE_UPDATE) {
-      // Delete language codes present in the incoming $entity->$field_name.
-      // Delete all language codes if $entity->$field_name is empty.
-      $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
-      if ($langcodes) {
-        db_delete($table_name)
-          ->condition('entity_type', $entity->entityType())
-          ->condition('entity_id', $id)
-          ->condition('langcode', $langcodes, 'IN')
-          ->execute();
-        db_delete($revision_name)
-          ->condition('entity_type', $entity->entityType())
-          ->condition('entity_id', $id)
-          ->condition('revision_id', $vid)
-          ->condition('langcode', $langcodes, 'IN')
-          ->execute();
-      }
-    }
-
-    // Prepare the multi-insert query.
-    $do_insert = FALSE;
-    $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
-    foreach ($field['columns'] as $column => $attributes) {
-      $columns[] = _field_sql_storage_columnname($field_name, $column);
-    }
-    $query = db_insert($table_name)->fields($columns);
-    $revision_query = db_insert($revision_name)->fields($columns);
-
-    foreach ($field_langcodes as $langcode) {
-      $items = (array) $entity->{$field_name}[$langcode];
-      $delta_count = 0;
-      foreach ($items as $delta => $item) {
-        // We now know we have someting to insert.
-        $do_insert = TRUE;
-        $record = array(
-          'entity_type' => $entity->entityType(),
-          'entity_id' => $id,
-          'revision_id' => $vid,
-          'bundle' => $bundle,
-          'delta' => $delta,
-          'langcode' => $langcode,
-        );
-        foreach ($field['columns'] as $column => $attributes) {
-          $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL;
-        }
-        $query->values($record);
-        if (isset($vid)) {
-          $revision_query->values($record);
-        }
-
-        if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
-          break;
-        }
-      }
-    }
-
-    // Execute the query if we have values to insert.
-    if ($do_insert) {
-      $query->execute();
-      $revision_query->execute();
-    }
-  }
-}
-
-/**
- * Delete all field data for an entity.
- *
- * This hook is invoked from field_attach_delete() to ask the field storage
- * module to delete field data.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity on which to operate.
- * @param $fields
- *   An array listing the fields to delete. The keys and values of the
- *   array are field UUIDs.
- */
-function hook_field_storage_delete(\Drupal\Core\Entity\EntityInterface $entity, $fields) {
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    if (isset($fields[$instance['field_id']])) {
-      $field = field_info_field_by_id($instance['field_id']);
-      field_sql_storage_field_storage_purge($entity, $field, $instance);
-    }
-  }
-}
-
-/**
- * Delete a single revision of field data for an entity.
- *
- * This hook is invoked from field_attach_delete_revision() to ask the field
- * storage module to delete field revision data.
- *
- * Deleting the current (most recently written) revision is not
- * allowed as has undefined results.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity on which to operate.
- * @param $fields
- *   An array listing the fields to delete. The keys and values of the
- *   array are field UUIDs.
- */
-function hook_field_storage_delete_revision(\Drupal\Core\Entity\EntityInterface $entity, $fields) {
-  $vid = $entity->getRevisionId();
-  if (isset($vid)) {
-    foreach ($fields as $field_id) {
-      $field = field_info_field_by_id($field_id);
-      $revision_name = _field_sql_storage_revision_tablename($field);
-      db_delete($revision_name)
-        ->condition('entity_type', $entity_type)
-        ->condition('entity_id', $entity->id())
-        ->condition('revision_id', $vid)
-        ->execute();
-    }
-  }
-}
-
-/**
- * Execute a Drupal\Core\Entity\EntityFieldQuery.
- *
- * This hook is called to find the entities having certain entity and field
- * conditions and sort them in the given field order. If the field storage
- * engine also handles property sorts and orders, it should unset those
- * properties in the called object to signal that those have been handled.
- *
- * @param Drupal\Core\Entity\EntityFieldQuery $query
- *   An EntityFieldQuery.
- *
- * @return
- *   See Drupal\Core\Entity\EntityFieldQuery::execute() for the return values.
- */
-function hook_field_storage_query($query) {
-  $groups = array();
-  if ($query->age == FIELD_LOAD_CURRENT) {
-    $tablename_function = '_field_sql_storage_tablename';
-    $id_key = 'entity_id';
-  }
-  else {
-    $tablename_function = '_field_sql_storage_revision_tablename';
-    $id_key = 'revision_id';
-  }
-  $table_aliases = array();
-  // Add tables for the fields used.
-  foreach ($query->fields as $key => $field) {
-    $tablename = $tablename_function($field);
-    // Every field needs a new table.
-    $table_alias = $tablename . $key;
-    $table_aliases[$key] = $table_alias;
-    if ($key) {
-      $select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key");
-    }
-    else {
-      $select_query = db_select($tablename, $table_alias);
-      $select_query->addTag('entity_field_access');
-      $select_query->addMetaData('base_table', $tablename);
-      $select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle'));
-      $field_base_table = $table_alias;
-    }
-    if ($field['cardinality'] != 1) {
-      $select_query->distinct();
-    }
-  }
-
-  // Add field conditions.
-  foreach ($query->fieldConditions as $key => $condition) {
-    $table_alias = $table_aliases[$key];
-    $field = $condition['field'];
-    // Add the specified condition.
-    $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $condition['column']);
-    $query->addCondition($select_query, $sql_field, $condition);
-    // Add delta / language group conditions.
-    foreach (array('delta', 'langcode') as $column) {
-      if (isset($condition[$column . '_group'])) {
-        $group_name = $condition[$column . '_group'];
-        if (!isset($groups[$column][$group_name])) {
-          $groups[$column][$group_name] = $table_alias;
-        }
-        else {
-          $select_query->where("$table_alias.$column = " . $groups[$column][$group_name] . ".$column");
-        }
-      }
-    }
-  }
-
-  if (isset($query->deleted)) {
-    $select_query->condition("$field_base_table.deleted", (int) $query->deleted);
-  }
-
-  // Is there a need to sort the query by property?
-  $has_property_order = FALSE;
-  foreach ($query->order as $order) {
-    if ($order['type'] == 'property') {
-      $has_property_order = TRUE;
-    }
-  }
-
-  if ($query->propertyConditions || $has_property_order) {
-    if (empty($query->entityConditions['entity_type']['value'])) {
-      throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
-    }
-    $entity_type = $query->entityConditions['entity_type']['value'];
-    $entity_base_table = _field_sql_storage_query_join_entity($select_query, $entity_type, $field_base_table);
-    $query->entityConditions['entity_type']['operator'] = '=';
-    foreach ($query->propertyConditions as $property_condition) {
-      $query->addCondition($select_query, "$entity_base_table." . $property_condition['column'], $property_condition);
-    }
-  }
-  foreach ($query->entityConditions as $key => $condition) {
-    $query->addCondition($select_query, "$field_base_table.$key", $condition);
-  }
-
-  // Order the query.
-  foreach ($query->order as $order) {
-    if ($order['type'] == 'entity') {
-      $key = $order['specifier'];
-      $select_query->orderBy("$field_base_table.$key", $order['direction']);
-    }
-    elseif ($order['type'] == 'field') {
-      $specifier = $order['specifier'];
-      $field = $specifier['field'];
-      $table_alias = $table_aliases[$specifier['index']];
-      $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $specifier['column']);
-      $select_query->orderBy($sql_field, $order['direction']);
-    }
-    elseif ($order['type'] == 'property') {
-      $select_query->orderBy("$entity_base_table." . $order['specifier'], $order['direction']);
-    }
-  }
-
-  return $query->finishQuery($select_query, $id_key);
-}
-
-/**
- * Act on creation of a new field.
- *
- * This hook is invoked during the creation of a field to ask the field storage
- * module to save field information and prepare for storing field instances. If
- * there is a problem, the field storage module should throw an exception.
- *
- * @param $field
- *   The field structure being created.
- */
-function hook_field_storage_create_field($field) {
-  $schema = _field_sql_storage_schema($field);
-  foreach ($schema as $name => $table) {
-    db_create_table($name, $table);
-  }
-  drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Update the storage information for a field.
- *
- * This is invoked on the field's storage module when updating the field,
- * before the new definition is saved to the database. The field storage module
- * should update its storage tables according to the new field definition. If
- * there is a problem, the field storage module should throw an exception.
- *
- * @param $field
- *   The updated field structure to be saved.
- * @param $prior_field
- *   The previously-saved field structure.
- */
-function hook_field_storage_update_field($field, $prior_field) {
-  if (!$field->hasData()) {
-    // There is no data. Re-create the tables completely.
-    $prior_schema = _field_sql_storage_schema($prior_field);
-    foreach ($prior_schema as $name => $table) {
-      db_drop_table($name, $table);
-    }
-    $schema = _field_sql_storage_schema($field);
-    foreach ($schema as $name => $table) {
-      db_create_table($name, $table);
-    }
-  }
-  else {
-    // There is data. See field_sql_storage_field_storage_update_field() for
-    // an example of what to do to modify the schema in place, preserving the
-    // old data as much as possible.
-  }
-  drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Act on deletion of a field.
- *
- * This hook is invoked during the deletion of a field to ask the field storage
- * module to mark all information stored in the field for deletion.
- *
- * @param $field
- *   The field being deleted.
- */
-function hook_field_storage_delete_field($field) {
-  // Mark all data associated with the field for deletion.
-  $field['deleted'] = FALSE;
-  $table = _field_sql_storage_tablename($field);
-  $revision_table = _field_sql_storage_revision_tablename($field);
-  db_update($table)
-    ->fields(array('deleted' => 1))
-    ->execute();
-
-  // Move the table to a unique name while the table contents are being deleted.
-  $field['deleted'] = TRUE;
-  $new_table = _field_sql_storage_tablename($field);
-  $revision_new_table = _field_sql_storage_revision_tablename($field);
-  db_rename_table($table, $new_table);
-  db_rename_table($revision_table, $revision_new_table);
-  drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Act on deletion of a field instance.
- *
- * This hook is invoked during the deletion of a field instance to ask the
- * field storage module to mark all information stored for the field instance
- * for deletion.
- *
- * @param $instance
- *   The instance being deleted.
- */
-function hook_field_storage_delete_instance($instance) {
-  $field = field_info_field($instance['field_name']);
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_update($table_name)
-    ->fields(array('deleted' => 1))
-    ->condition('entity_type', $instance['entity_type'])
-    ->condition('bundle', $instance['bundle'])
-    ->execute();
-  db_update($revision_name)
-    ->fields(array('deleted' => 1))
-    ->condition('entity_type', $instance['entity_type'])
-    ->condition('bundle', $instance['bundle'])
-    ->execute();
-}
-
-/**
- * Act before the storage backends load field data.
- *
- * This hook allows modules to load data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * This lets 3rd party modules override, mirror, share, or otherwise store a
- * subset of fields in a different way than the current storage engine. Possible
- * use cases include per-bundle storage, per-combo-field storage, etc.
- *
- * Modules implementing this hook should load field values and add them to
- * objects in $entities. Fields with no values should be added as empty arrays.
- * In addition, fields loaded should be added as keys to $skip_fields.
- *
- * @param $entity_type
- *   The type of entity, such as 'node' or 'user'.
- * @param $entities
- *   The array of entity objects to add fields to, keyed by entity ID.
- * @param $age
- *   FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- *   FIELD_LOAD_REVISION to load the version indicated by each entity.
- * @param $skip_fields
- *   An array keyed by field UUIDs whose data has already been loaded and
- *   therefore should not be loaded again. Add a key to this array to indicate
- *   that your module has already loaded a field.
- * @param $options
- *   An associative array of additional options, with the following keys:
- *   - field_id: The field UUID that should be loaded. If unset, all fields
- *     should be loaded.
- *   - deleted: If TRUE, deleted fields should be loaded as well as non-deleted
- *     fields. If unset or FALSE, only non-deleted fields should be loaded.
- */
-function hook_field_storage_pre_load($entity_type, $entities, $age, &$skip_fields, $options) {
-  // @todo Needs function body.
-}
-
-/**
- * Act before the storage backends insert field data.
- *
- * This hook allows modules to store data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- * @param $skip_fields
- *   An array keyed by field UUIDs whose data has already been written and
- *   therefore should not be written again. The values associated with these
- *   keys are not specified.
- * @return
- *   Saved field UUIDs are set as keys in $skip_fields.
- */
-function hook_field_storage_pre_insert(\Drupal\Core\Entity\EntityInterface $entity, &$skip_fields) {
-  if ($entity->entityType() == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
-    $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
-    foreach ($entity->taxonomy_forums as $language) {
-      foreach ($language as $delta) {
-        $query->values(array(
-          'nid' => $entity->id(),
-          'title' => $entity->title,
-          'tid' => $delta['value'],
-          'sticky' => $entity->sticky,
-          'created' => $entity->created,
-          'comment_count' => 0,
-          'last_comment_timestamp' => $entity->created,
-        ));
-      }
-    }
-    $query->execute();
-  }
-}
-
-/**
- * Act before the storage backends update field data.
- *
- * This hook allows modules to store data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- * @param $skip_fields
- *   An array keyed by field UUIDs whose data has already been written and
- *   therefore should not be written again. The values associated with these
- *   keys are not specified.
- * @return
- *   Saved field UUIDs are set as keys in $skip_fields.
- */
-function hook_field_storage_pre_update(\Drupal\Core\Entity\EntityInterface $entity, &$skip_fields) {
-  $first_call = &drupal_static(__FUNCTION__, array());
-
-  if ($entity->entityType() == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
-    // We don't maintain data for old revisions, so clear all previous values
-    // from the table. Since this hook runs once per field, per entity, make
-    // sure we only wipe values once.
-    if (!isset($first_call[$entity->id()])) {
-      $first_call[$entity->id()] = FALSE;
-      db_delete('forum_index')->condition('nid', $entity->id())->execute();
-    }
-    // Only save data to the table if the node is published.
-    if ($entity->status) {
-      $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
-      foreach ($entity->taxonomy_forums as $language) {
-        foreach ($language as $delta) {
-          $query->values(array(
-            'nid' => $entity->id(),
-            'title' => $entity->title,
-            'tid' => $delta['value'],
-            'sticky' => $entity->sticky,
-            'created' => $entity->created,
-            'comment_count' => 0,
-            'last_comment_timestamp' => $entity->created,
-          ));
-        }
-      }
-      $query->execute();
-      // The logic for determining last_comment_count is fairly complex, so
-      // call _forum_update_forum_index() too.
-      _forum_update_forum_index($entity->id());
-    }
-  }
-}
-
-/**
  * Returns the maximum weight for the entity components handled by the module.
  *
  * Field API takes care of fields and 'extra_fields'. This hook is intended for
@@ -1217,10 +517,6 @@ function hook_field_info_max_weight($entity_type, $bundle, $context, $context_mo
 }
 
 /**
- * @} End of "addtogroup field_storage".
- */
-
-/**
  * @addtogroup field_crud
  * @{
  */
@@ -1300,63 +596,6 @@ function hook_field_purge_instance($instance) {
 }
 
 /**
- * Remove field storage information when a field record is purged.
- *
- * Called from field_purge_field() to allow the field storage module to remove
- * field information when a field is being purged.
- *
- * @param $field
- *   The field being purged.
- */
-function hook_field_storage_purge_field($field) {
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_drop_table($table_name);
-  db_drop_table($revision_name);
-}
-
-/**
- * Remove field storage information when a field instance is purged.
- *
- * Called from field_purge_instance() to allow the field storage module to
- * remove field instance information when a field instance is being purged.
- *
- * @param $instance
- *   The instance being purged.
- */
-function hook_field_storage_purge_field_instance($instance) {
-  db_delete('my_module_field_instance_info')
-    ->condition('id', $instance['id'])
-    ->execute();
-}
-
-/**
- * Remove field storage information when field data is purged.
- *
- * Called from field_purge_data() to allow the field storage module to delete
- * field data information.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The pseudo-entity whose field data to delete.
- * @param $field
- *   The (possibly deleted) field whose data is being purged.
- * @param $instance
- *   The deleted field instance whose data is being purged.
- */
-function hook_field_storage_purge(\Drupal\Core\Entity\EntityInterface $entity, $field, $instance) {
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_delete($table_name)
-    ->condition('entity_type', $entity->entityType())
-    ->condition('entity_id', $entity->id())
-    ->execute();
-  db_delete($revision_name)
-    ->condition('entity_type', $entity->entityType())
-    ->condition('entity_id', $entity->id())
-    ->execute();
-}
-
-/**
  * @} End of "addtogroup field_crud".
  */
 
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index 5bab381..bf13405 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -13,45 +13,6 @@
 use Drupal\Core\Entity\Field\PrepareCacheInterface;
 
 /**
- * @defgroup field_storage Field Storage API
- * @{
- * Implements a storage engine for Field API data.
- *
- * The Field Attach API uses the Field Storage API to perform all "database
- * access". Each Field Storage API hook function defines a primitive database
- * operation such as read, write, or delete. The default field storage module,
- * field_sql_storage.module, uses the local SQL database to implement these
- * operations, but alternative field storage backends can choose to represent
- * the data in SQL differently or use a completely different storage mechanism
- * such as a cloud-based database.
- *
- * Each field defines which storage backend it uses. The Drupal configuration
- * 'field.settings.default_storage' identifies the storage backend used by
- * default.
- *
- * See @link field Field API @endlink for information about the other parts of
- * the Field API.
- */
-
-/**
- * Argument for an update operation.
- *
- * This is used in hook_field_storage_write when updating an existing entity.
- */
-const FIELD_STORAGE_UPDATE = 'update';
-
-/**
- * Argument for an insert operation.
- *
- * This is used in hook_field_storage_write when inserting a new entity.
- */
-const FIELD_STORAGE_INSERT = 'insert';
-
-/**
- * @} End of "defgroup field_storage".
- */
-
-/**
  * @defgroup field_attach Field Attach API
  * @{
  * Operates on Field API data attached to Drupal entities.
@@ -170,7 +131,7 @@ function field_invoke_method($method, $target_function, EntityInterface $entity,
     $target = call_user_func($target_function, $instance);
 
     if (method_exists($target, $method)) {
-      $field = field_info_field_by_id($instance['field_id']);
+      $field = $instance->getField();
       $field_name = $field['field_name'];
 
       // Determine the list of languages to iterate on.
@@ -277,7 +238,7 @@ function field_invoke_method_multiple($method, $target_function, array $entities
 
         // Unless a language code suggestion is provided we iterate on all the
         // available language codes.
-        $field = field_info_field_by_id($instance['field_id']);
+        $field = $instance->getField();
         $available_langcodes = field_available_languages($entity_type, $field);
         $langcode = !empty($options['langcode'][$id]) ? $options['langcode'][$id] : $options['langcode'];
         $langcodes = _field_language_suggestion($available_langcodes, $langcode, $field_name);
@@ -527,215 +488,6 @@ function field_attach_form(EntityInterface $entity, &$form, &$form_state, $langc
 }
 
 /**
- * Loads fields for the current revisions of a group of entities.
- *
- * Loads all fields for each entity object in a group of a single entity type.
- * The loaded field values are added directly to the entity objects.
- *
- * field_attach_load() is automatically called by the default entity controller
- * class, and thus, in most cases, doesn't need to be explicitly called by the
- * entity type module.
- *
- * @param $entity_type
- *   The type of entities in $entities; e.g., 'node' or 'user'.
- * @param $entities
- *   An array of entities for which to load fields, keyed by entity ID. Each
- *   entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
- *   filled in. The function adds the loaded field data directly in the entity
- *   objects of the $entities array.
- * @param $age
- *   FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- *   FIELD_LOAD_REVISION to load the version indicated by each entity. Defaults
- *   to FIELD_LOAD_CURRENT; use field_attach_load_revision() instead of passing
- *   FIELD_LOAD_REVISION.
- * @param $options
- *   An associative array of additional options, with the following keys:
- *   - instance: A field instance entity, If provided, only values for the
- *     corresponding field will be loaded, and no cache is written. This
- *     option is only supported when all $entities are within the same bundle.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $options = array()) {
-  $load_current = $age == FIELD_LOAD_CURRENT;
-  $load_deleted = !empty($options['instance']->deleted);
-
-  // Merge default options.
-  $options += array('instance' => NULL);
-  // Set options for hook invocations.
-  $hook_options = array(
-    'deleted' => $load_deleted,
-  );
-  if ($options['instance']) {
-    $hook_options['field_id'] = $options['instance']->field_uuid;
-  }
-
-  $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'] && !$load_deleted;
-  // In addition, do not write to the cache when loading a single field.
-  $cache_write = $cache_read && !isset($options['instance']);
-
-  if (empty($entities)) {
-    return;
-  }
-
-  // Ensure we are working with a BC mode entity.
-  foreach ($entities as $id => $entity) {
-    $entities[$id] = $entity->getBCEntity();
-  }
-
-  // Assume all entities will need to be queried. Entities found in the cache
-  // will be removed from the list.
-  $queried_entities = $entities;
-
-  // Fetch available entities from cache, if applicable.
-  if ($cache_read) {
-    // Build the list of cache entries to retrieve.
-    $cids = array();
-    foreach ($entities as $id => $entity) {
-      $cids[] = "field:$entity_type:$id";
-    }
-    $cache = cache('field')->getMultiple($cids);
-    // Put the cached field values back into the entities and remove them from
-    // the list of entities to query.
-    foreach ($entities as $id => $entity) {
-      $cid = "field:$entity_type:$id";
-      if (isset($cache[$cid])) {
-        unset($queried_entities[$id]);
-        foreach ($cache[$cid]->data as $field_name => $values) {
-          $entity->$field_name = $values;
-        }
-      }
-    }
-  }
-
-  // Fetch other entities from their storage location.
-  if ($queried_entities) {
-    // The invoke order is:
-    // - hook_field_storage_pre_load()
-    // - storage backend's hook_field_storage_load()
-    // - Field class's prepareCache() method.
-    // - hook_field_attach_load()
-
-    // Invoke hook_field_storage_pre_load(): let any module load field
-    // data before the storage engine, accumulating along the way.
-    $skip_fields = array();
-    foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_load') as $module) {
-      $function = $module . '_field_storage_pre_load';
-      $function($entity_type, $queried_entities, $age, $skip_fields, $hook_options);
-    }
-
-    // Collect the storage backends used by the remaining fields in the entities.
-    $storages = array();
-    foreach ($queried_entities as $entity) {
-      $id = $entity->id();
-      $vid = $entity->getRevisionId();
-
-      // Determine the list of field instances to work on.
-      if ($options['instance']) {
-        $instances = array($options['instance']);
-      }
-      else {
-        $instances = field_info_instances($entity_type, $entity->bundle());
-      }
-
-      foreach ($instances as $instance) {
-        $field = $instance->getField();
-        $field_name = $field->id();
-        if (!isset($queried_entities[$id]->{$field_name})) {
-          $queried_entities[$id]->{$field_name} = array();
-        }
-        if (!isset($skip_fields[$field->uuid])) {
-          $storages[$field->storage['type']][$field->uuid][] = $load_current ? $id : $vid;
-        }
-      }
-    }
-
-    // Invoke hook_field_storage_load() on the relevant storage backends.
-    foreach ($storages as $storage => $fields) {
-      $storage_info = field_info_storage_types($storage);
-      module_invoke($storage_info['module'], 'field_storage_load', $entity_type, $queried_entities, $age, $fields, $hook_options);
-    }
-
-    // Invoke the field type's prepareCache() method.
-    if (empty($options['instance'])) {
-      foreach ($queried_entities as $entity) {
-        \Drupal::entityManager()
-          ->getStorageController($entity_type)
-          ->invokeFieldItemPrepareCache($entity);
-      }
-    }
-    else {
-      // Do not rely on invokeFieldItemPrepareCache(), which only works on
-      // fields listed in getFieldDefinitions(), and will fail if we are loading
-      // values for a deleted field. Instead, generate FieldItem objects
-      // directly, and call their prepareCache() method.
-      foreach ($queried_entities as $entity) {
-        $field = $options['instance']->getField();
-        $field_name = $field->id();
-        // Call the prepareCache() method on each item.
-        foreach ($entity->{$field_name} as $langcode => $values) {
-          $definition = _field_generate_entity_field_definition($field, $options['instance']);
-          $items = \Drupal::typedData()->create($definition, $values, $field_name, $entity);
-          foreach ($items as $item) {
-            if ($item instanceof PrepareCacheInterface) {
-              $item->prepareCache();
-            }
-          }
-          $entity->{$field_name}[$langcode] = $items->getValue();
-        }
-      }
-    }
-
-    // Invoke hook_field_attach_load(): let other modules act on loading the
-    // entity.
-    Drupal::moduleHandler()->invokeAll('field_attach_load', $entity_type, $queried_entities, $age, $options);
-
-    // Build cache data.
-    if ($cache_write) {
-      foreach ($queried_entities as $id => $entity) {
-        $data = array();
-        $instances = field_info_instances($entity_type, $entity->bundle());
-        foreach ($instances as $instance) {
-          $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
-        }
-        $cid = "field:$entity_type:$id";
-        cache('field')->set($cid, $data);
-      }
-    }
-  }
-}
-
-/**
- * Loads all fields for previous versions of a group of entities.
- *
- * Loading different versions of the same entities is not supported, and should
- * be done by separate calls to the function.
- *
- * field_attach_load_revision() is automatically called by the default entity
- * controller class, and thus, in most cases, doesn't need to be explicitly
- * called by the entity type module.
- *
- * @param $entity_type
- *   The type of entities in $entities; e.g. 'node' or 'user'.
- * @param $entities
- *   An array of entities for which to load fields, keyed by entity ID. Each
- *   entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
- *   filled. The function adds the loaded field data directly in the entity
- *   objects of the $entities array.
- * @param $options
- *   An associative array of additional options. See field_attach_load() for
- *   details.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_load_revision($entity_type, $entities, $options = array()) {
-  return field_attach_load($entity_type, $entities, FIELD_LOAD_REVISION, $options);
-}
-
-/**
  * Performs field validation against form-submitted field values.
  *
  * There are two levels of validation for fields in forms: widget validation and
@@ -772,6 +524,7 @@ function field_attach_form_validate(EntityInterface $entity, $form, &$form_state
     return;
   }
 
+  $entity_type = $entity->entityType();
   $has_violations = FALSE;
   foreach ($entity as $field_name => $field) {
     $definition = $field->getDefinition();
@@ -781,7 +534,7 @@ function field_attach_form_validate(EntityInterface $entity, $form, &$form_state
         $has_violations = TRUE;
 
         // Place violations in $form_state.
-        $langcode = field_is_translatable($entity->entityType(), field_info_field($field_name)) ? $entity->language()->id : Language::LANGCODE_NOT_SPECIFIED;
+        $langcode = field_is_translatable($entity_type, field_info_field($entity_type, $field_name)) ? $entity->language()->id : Language::LANGCODE_NOT_SPECIFIED;
         $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
         $field_state['constraint_violations'] = $field_violations;
         field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
@@ -833,159 +586,6 @@ function field_attach_extract_form_values(EntityInterface $entity, $form, &$form
 }
 
 /**
- * Save field data for a new entity.
- *
- * The passed-in entity must already contain its id and (if applicable)
- * revision id attributes.
- * Default values (if any) will be saved for fields not present in the
- * $entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- * @return
- *   Default values (if any) will be added to the $entity parameter for fields
- *   it leaves unspecified.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_insert(EntityInterface $entity) {
-  // Ensure we are working with a BC mode entity.
-  $entity = $entity->getBCEntity();
-
-  // Let any module insert field data before the storage engine, accumulating
-  // saved fields along the way.
-  $skip_fields = array();
-  foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_insert') as $module) {
-    $function = $module . '_field_storage_pre_insert';
-    $function($entity, $skip_fields);
-  }
-
-  // Collect the storage backends used by the remaining fields in the entities.
-  $storages = array();
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    $field_id = $field['uuid'];
-    $field_name = $field['field_name'];
-    if (!empty($entity->$field_name)) {
-      // Collect the storage backend if the field has not been written yet.
-      if (!isset($skip_fields[$field_id])) {
-        $storages[$field['storage']['type']][$field_id] = $field_id;
-      }
-    }
-  }
-
-  // Field storage backends save any remaining unsaved fields.
-  foreach ($storages as $storage => $fields) {
-    $storage_info = field_info_storage_types($storage);
-    module_invoke($storage_info['module'], 'field_storage_write', $entity, FIELD_STORAGE_INSERT, $fields);
-  }
-}
-
-/**
- * Saves field data for an existing entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_update(EntityInterface $entity) {
-  // Ensure we are working with a BC mode entity.
-  $entity = $entity->getBCEntity();
-
-  // Let any module update field data before the storage engine, accumulating
-  // saved fields along the way.
-  $skip_fields = array();
-  foreach (Drupal::moduleHandler()->getImplementations('field_storage_pre_update') as $module) {
-    $function = $module . '_field_storage_pre_update';
-    $function($entity, $skip_fields);
-  }
-
-  // Collect the storage backends used by the remaining fields in the entities.
-  $storages = array();
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    $field_id = $field['uuid'];
-    // Collect the storage backend if the field has not been written yet.
-    if (!isset($skip_fields[$field_id])) {
-      $storages[$field['storage']['type']][$field_id] = $field_id;
-    }
-  }
-
-  // Field storage backends save any remaining unsaved fields.
-  foreach ($storages as $storage => $fields) {
-    $storage_info = field_info_storage_types($storage);
-    module_invoke($storage_info['module'], 'field_storage_write', $entity, FIELD_STORAGE_UPDATE, $fields);
-  }
-
-  $entity_info = $entity->entityInfo();
-  if ($entity_info['field_cache']) {
-    cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
-  }
-}
-
-/**
- * Deletes field data for an existing entity. This deletes all revisions of
- * field data for the entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity whose field data to delete.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_delete(EntityInterface $entity) {
-  // Ensure we are working with a BC mode entity.
-  $entity = $entity->getBCEntity();
-
-  // Collect the storage backends used by the fields in the entities.
-  $storages = array();
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    $field_id = $field['uuid'];
-    $storages[$field['storage']['type']][$field_id] = $field_id;
-  }
-
-  // Field storage backends delete their data.
-  foreach ($storages as $storage => $fields) {
-    $storage_info = field_info_storage_types($storage);
-    module_invoke($storage_info['module'], 'field_storage_delete', $entity, $fields);
-  }
-
-  $entity_info = $entity->entityInfo();
-  if ($entity_info['field_cache']) {
-    cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
-  }
-}
-
-/**
- * Delete field data for a single revision of an existing entity. The passed
- * entity must have a revision ID attribute.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_delete_revision(EntityInterface $entity) {
-  // Ensure we are working with a BC mode entity.
-  $entity = $entity->getBCEntity();
-
-  // Collect the storage backends used by the fields in the entities.
-  $storages = array();
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    $field_id = $field['uuid'];
-    $storages[$field['storage']['type']][$field_id] = $field_id;
-  }
-
-  // Field storage backends delete their data.
-  foreach ($storages as $storage => $fields) {
-    $storage_info = field_info_storage_types($storage);
-    module_invoke($storage_info['module'], 'field_storage_delete_revision', $entity, $fields);
-  }
-}
-
-/**
  * Prepares field data prior to display.
  *
  * This function lets field types and formatters load additional data needed for
diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc
index c66fd54..582ebd6 100644
--- a/core/modules/field/field.crud.inc
+++ b/core/modules/field/field.crud.inc
@@ -32,6 +32,8 @@
  * This function will not return deleted fields. Use field_read_fields() instead
  * for this purpose.
  *
+ * @param $entity_type
+ *   The entity type.
  * @param $field_name
  *   The field name to read.
  * @param array $include_additional
@@ -45,8 +47,8 @@
  * @deprecated as of Drupal 8.0. Use
  *   entity_load('field_entity', 'field_name').
  */
-function field_read_field($field_name, $include_additional = array()) {
-  $fields = field_read_fields(array('field_name' => $field_name), $include_additional);
+function field_read_field($entity_type, $field_name, $include_additional = array()) {
+  $fields = field_read_fields(array('entity_type' => $entity_type, 'name' => $field_name), $include_additional);
   return $fields ? current($fields) : FALSE;
 }
 
@@ -254,7 +256,7 @@ function field_purge_batch($batch_size) {
       'bundle' => $instance['bundle'],
     );
     // field_purge_data() will need the field array.
-    $field = field_info_field_by_id($instance['field_id']);
+    $field = $instance->getField();
     // Retrieve some entities.
     $query = $factory->get($entity_type)
       ->condition('id:' . $field['uuid'] . '.deleted', 1)
@@ -264,21 +266,12 @@ function field_purge_batch($batch_size) {
       $query->condition($info[$entity_type]['entity_keys']['bundle'], $ids->bundle);
     }
     $results = $query->execute();
-
     if ($results) {
-      $entities = array();
       foreach ($results as $revision_id => $entity_id) {
-        // This might not be the revision id if the entity type does not support
-        // revisions but _field_create_entity_from_ids() checks that and
-        // disregards this value so there is no harm setting it.
         $ids->revision_id = $revision_id;
         $ids->entity_id = $entity_id;
-        $entities[$entity_id] = _field_create_entity_from_ids($ids);
-      }
-      field_attach_load($entity_type, $entities, FIELD_LOAD_CURRENT, array('instance' => $instance));
-      foreach ($entities as $entity) {
-        // Purge the data for the entity.
-        field_purge_data($entity, $field, $instance);
+        $entity = _field_create_entity_from_ids($ids);
+        Drupal::entityManager()->getStorageController($instance->entity_type)->fieldPurgeData($entity, $instance);
       }
     }
     else {
@@ -299,37 +292,6 @@ function field_purge_batch($batch_size) {
 }
 
 /**
- * Purges the field data for a single field on a single pseudo-entity.
- *
- * This is basically the same as field_attach_delete() except it only applies to
- * a single field. The entity itself is not being deleted, and it is quite
- * possible that other field data will remain attached to it.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The pseudo-entity whose field data is being purged.
- * @param $field
- *   The (possibly deleted) field whose data is being purged.
- * @param $instance
- *   The deleted field instance whose data is being purged.
- */
-function field_purge_data(EntityInterface $entity, $field, $instance) {
-  foreach ($entity->{$field->id()} as $value) {
-    $definition = _field_generate_entity_field_definition($field, $instance);
-    $items = \Drupal::typedData()->create($definition, $value, $field->id(), $entity);
-    $items->delete();
-  }
-
-  // Tell the field storage system to purge the data.
-  module_invoke($field['storage']['module'], 'field_storage_purge', $entity, $field, $instance);
-
-  // Let other modules act on purging the data.
-  foreach (Drupal::moduleHandler()->getImplementations('field_attach_purge') as $module) {
-    $function = $module . '_field_attach_purge';
-    $function($entity, $field, $instance);
-  }
-}
-
-/**
  * Purges a field instance record from the database.
  *
  * This function assumes all data for the instance has already been purged and
@@ -339,10 +301,6 @@ function field_purge_data(EntityInterface $entity, $field, $instance) {
  *   The instance record to purge.
  */
 function field_purge_instance($instance) {
-  // Notify the storage engine.
-  $field = field_info_field_by_id($instance['field_id']);
-  module_invoke($field['storage']['module'], 'field_storage_purge_instance', $instance);
-
   $state = Drupal::state();
   $deleted_instances = $state->get('field.instance.deleted');
   unset($deleted_instances[$instance['uuid']]);
@@ -375,8 +333,8 @@ function field_purge_field($field) {
   unset($deleted_fields[$field['uuid']]);
   $state->set('field.field.deleted', $deleted_fields);
 
-  // Notify the storage engine.
-  module_invoke($field['storage']['module'], 'field_storage_purge_field', $field);
+  // Notify the storage layer.
+  Drupal::entityManager()->getStorageController($field->entity_type)->handleFieldPurged($field);
 
   // Clear the cache.
   field_info_cache_clear();
diff --git a/core/modules/field/field.info.inc b/core/modules/field/field.info.inc
index 824d74e..b94305b 100644
--- a/core/modules/field/field.info.inc
+++ b/core/modules/field/field.info.inc
@@ -42,77 +42,10 @@ function field_info_cache_clear() {
   Drupal::typedData()->clearCachedDefinitions();
   Drupal::service('plugin.manager.entity.field.field_type')->clearCachedDefinitions();
 
-  _field_info_collate_types_reset();
   Field::fieldInfo()->flush();
 }
 
 /**
- * Collates all information on field types, widget types and related structures.
- *
- * @return
- *   An associative array containing:
- *   - 'storage types': Array of hook_field_storage_info() results, keyed by
- *     storage type names. Each element has the following components: label,
- *     description, and settings from hook_field_storage_info(), as well as
- *     module, giving the module that exposes the storage type.
- *
- * @see _field_info_collate_types_reset()
- */
-function _field_info_collate_types() {
-  $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)) {
-    $drupal_static_fast['field_info_collate_types'] = &drupal_static(__FUNCTION__);
-  }
-  $info = &$drupal_static_fast['field_info_collate_types'];
-
-  // The _info() hooks invoked below include translated strings, so each
-  // language is cached separately.
-  $langcode = $language_interface->id;
-
-  if (!isset($info)) {
-    if ($cached = cache('field')->get("field_info_types:$langcode")) {
-      $info = $cached->data;
-    }
-    else {
-      $info = array(
-        'storage types' => array(),
-      );
-
-      // Populate storage types.
-      foreach (Drupal::moduleHandler()->getImplementations('field_storage_info') as $module) {
-        $storage_types = (array) module_invoke($module, 'field_storage_info');
-        foreach ($storage_types as $name => $storage_info) {
-          // Provide defaults.
-          $storage_info += array(
-            'settings' => array(),
-          );
-          $info['storage types'][$name] = $storage_info;
-          $info['storage types'][$name]['module'] = $module;
-        }
-      }
-      drupal_alter('field_storage_info', $info['storage types']);
-
-      cache('field')->set("field_info_types:$langcode", $info, CacheBackendInterface::CACHE_PERMANENT, array('field_info_types' => TRUE));
-    }
-  }
-
-  return $info;
-}
-
-/**
- * Clears collated information on field and widget types and related structures.
- */
-function _field_info_collate_types_reset() {
-  drupal_static_reset('_field_info_collate_types');
-  // Clear all languages.
-  cache('field')->deleteTags(array('field_info_types' => TRUE));
-}
-
-/**
  * Determines the behavior of a widget with respect to an operation.
  *
  * @param string $op
@@ -141,18 +74,20 @@ function field_behaviors_widget($op, $instance) {
  * The function only returns active, non deleted fields.
  *
  * @return
- *   An array keyed by field name. Each value is an array with two entries:
- *   - type: The field type.
- *   - bundles: The bundles in which the field appears, as an array with entity
- *     types as keys and the array of bundle names as values.
+ *  An array keyed by entity type. Each value is an array which keys are
+ *  field names and value is an array with two entries:
+ *  - type: The field type.
+ *  - bundles: The bundles in which the field appears.
  * Example:
  * @code
  * array(
- *   'body' => array(
- *     'bundles' => array(
- *       'node' => array('page', 'article'),
+ *   'node' => array(
+ *     'body' => array(
+ *       'bundles' => array(
+ *         'page', 'article'
+ *       ),
+ *       'type' => 'text_with_summary',
  *     ),
- *     'type' => 'text_with_summary',
  *   ),
  * );
  * @endcode
@@ -240,31 +175,6 @@ function field_info_formatter_types($formatter_type = NULL) {
 }
 
 /**
- * Returns information about field storage from hook_field_storage_info().
- *
- * @param $storage_type
- *   (optional) A storage type name. If omitted, all storage types will be
- *   returned.
- *
- * @return
- *   Either a storage type description, as provided by
- *   hook_field_storage_info(), or an array of all existing storage types, keyed
- *   by storage type name.
- */
-function field_info_storage_types($storage_type = NULL) {
-  $info = _field_info_collate_types();
-  $storage_types = $info['storage types'];
-  if ($storage_type) {
-    if (isset($storage_types[$storage_type])) {
-      return $storage_types[$storage_type];
-    }
-  }
-  else {
-    return $storage_types;
-  }
-}
-
-/**
  * Returns all field definitions.
  *
  * Use of this function should be avoided when possible, since it loads and
@@ -276,28 +186,22 @@ function field_info_storage_types($storage_type = NULL) {
  * field_info_field() on each individual field instead.
  *
  * @return
- *   An array of field definitions, keyed by field name. Each field has an
- *   additional property, 'bundles', which is an array of all the bundles to
- *   which this field belongs, keyed by entity type.
+ *   An array of field definitions, keyed by field UUID.
  *
  * @see field_info_field_map()
  */
 function field_info_fields() {
-  $info = Field::fieldInfo()->getFields();
-
-  $fields = array();
-  foreach ($info as $key => $field) {
-    if (!$field['deleted']) {
-      $fields[$field['field_name']] = $field;
-    }
-  }
-
-  return $fields;
+  // Filter out deleted fields.
+  return array_filter(Field::fieldInfo()->getFields(), function ($field) {
+    return !$field->deleted;
+  });
 }
 
 /**
- * Returns data about an individual field, given a field name.
+ * Returns data about an individual field.
  *
+ * @param $entity_type
+ *   The entity type.
  * @param $field_name
  *   The name of the field to retrieve. $field_name can only refer to a
  *   non-deleted, active field. For deleted fields, use
@@ -315,8 +219,8 @@ function field_info_fields() {
  * @deprecated as of Drupal 8.0. Use
  *   Field::fieldInfo()->getField($field_name).
  */
-function field_info_field($field_name) {
-  return Field::fieldInfo()->getField($field_name);
+function field_info_field($entity_type, $field_name) {
+  return Field::fieldInfo()->getField($entity_type, $field_name);
 }
 
 /**
@@ -564,21 +468,5 @@ function field_info_formatter_settings($type) {
 }
 
 /**
- * Returns a field storage type's default settings.
- *
- * @param $type
- *   A field storage type name.
- *
- * @return
- *   The storage type's default settings, as provided by
- *   hook_field_storage_info(), or an empty array if type or settings are
- *   undefined.
- */
-function field_info_storage_settings($type) {
-  $info = field_info_storage_types($type);
-  return isset($info['settings']) ? $info['settings'] : array();
-}
-
-/**
  * @} End of "defgroup field_info".
  */
diff --git a/core/modules/field/field.install b/core/modules/field/field.install
index c46346a..12e78ba 100644
--- a/core/modules/field/field.install
+++ b/core/modules/field/field.install
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Uuid\Uuid;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
@@ -29,8 +30,8 @@ function _update_8003_field_create_field(array &$field_config) {
 
   // Merge in default values.
   $field_config += array(
+    'id' => $field_config['entity_type'] . '.' . $field_config['name'],
     'uuid' => $uuid->generate(),
-    'entity_types' => array(),
     'cardinality' => 1,
     'translatable' => FALSE,
     'locked' => FALSE,
@@ -41,14 +42,6 @@ function _update_8003_field_create_field(array &$field_config) {
     'langcode' => 'und',
   );
 
-  // Set the storage.
-  $field_config['storage'] = array(
-    'type' => 'field_sql_storage',
-    'module' => 'field_sql_storage',
-    'active' => TRUE,
-    'settings' => array(),
-  );
-
   // Save in config.
   Drupal::config('field.field.' . $field_config['id'])
     ->setData($field_config)
@@ -56,15 +49,17 @@ function _update_8003_field_create_field(array &$field_config) {
 
   // Create storage for the field. This requires a field entity, but cannot use
   // the regular entity_create() function here.
-  $field_entity = new Field($field_config);
-  field_sql_storage_field_storage_create_field($field_entity);
+  $schema = DatabaseStorageController::_fieldSqlSchema(new Field($field_config), $field_config['schema']);
+  foreach ($schema as $name => $table) {
+    db_create_table($name, $table);
+  }
 }
 
 /**
  * Writes a field instance directly to configuration.
  *
  * Upgrades using this function need to use hook_update_dependencies() to ensure
-  * they get executed after field_update_8003().
+ * they get executed after field_update_8003().
  *
  * @param array $field_config
  *   An array of field properties.
@@ -78,6 +73,7 @@ function _update_8003_field_create_instance(array $field_config, array &$instanc
 
   // Merge in defaults.
   $instance_config += array(
+    'id' => $instance_config['entity_type'] . '.' . $instance_config['bundle'] . '.' . $field_config['name'],
     'description' => '',
     'required' => FALSE,
     'uuid' => $uuid->generate(),
@@ -98,6 +94,67 @@ function _update_8003_field_create_instance(array $field_config, array &$instanc
 }
 
 /**
+ * Writes field data directly to SQL storage.
+ *
+ * @param string $entity_type
+ * @param string $bundle
+ * @param int $entity_id
+ * @param int $revision_id
+ * @param int $field_name
+ * @param array $data
+ *
+ * @ingroup update_api
+ */
+function _update_8006_field_write_data_sql($entity_type, $bundle, $entity_id, $revision_id, $field_name, array $data) {
+  $field_config = Drupal::config("field.field.$entity_type.$field_name");
+  $field = new Field($field_config);
+
+  $table_name = DatabaseStorageController::_fieldTableName($field);
+  $revision_name = DatabaseStorageController::_fieldRevisionTableName($field);
+
+  db_delete($table_name)
+    ->condition('entity_id', $entity_id)
+    ->execute();
+  db_delete($revision_name)
+    ->condition('entity_id', $entity_id)
+    ->condition('revision_id', $revision_id)
+    ->execute();
+
+  $columns = array();
+  foreach ($data as $langcode => $items) {
+    foreach ($items as $delta => $item) {
+      $record = array(
+        'entity_id' => $entity_id,
+        'revision_id' => $revision_id,
+        'bundle' => $bundle,
+        'delta' => $delta,
+        'langcode' => $langcode,
+      );
+      foreach ($item as $column => $value) {
+        $record[DatabaseStorageController::_fieldColumnName($field_name, $column)] = $value;
+      }
+
+      $records[] = $record;
+      // Record the columns used.
+      $columns += $record;
+    }
+  }
+
+  if ($columns) {
+    $query = db_insert($table_name)->fields(array_keys($columns));
+    $revision_query = db_insert($revision_name)->fields(array_keys($columns));
+    foreach ($records as $record) {
+      $query->values($record);
+      if ($revision_id) {
+        $revision_query->values($record);
+      }
+    }
+    $query->execute();
+    $revision_query->execute();
+  }
+}
+
+/**
  * @addtogroup updates-7.x-to-8.x
  * @{
  */
@@ -255,27 +312,22 @@ function field_update_8003() {
   $field_data = array();
 
   // Migrate field definitions.
-  $records = db_query("SELECT * FROM {field_config}")->fetchAll(PDO::FETCH_ASSOC);
+  $records = db_query("SELECT DISTINCT entity_type, fc.* FROM {field_config} fc INNER JOIN {field_config_instance} fci ON fc.id = fci.field_id")->fetchAll(PDO::FETCH_ASSOC);
   foreach ($records as $record) {
     $record['data'] = unserialize($record['data']);
 
     $config = array(
-      'id' => $record['field_name'],
+      'id' => $record['entity_type'] . '.' . $record['field_name'],
+      'name' => $record['field_name'],
       'uuid' => $uuid->generate(),
       'type' => $record['type'],
       'module' => $record['module'],
       'active' => $record['active'],
+      'entity_type' => $record['entity_type'],
       'settings' => $record['data']['settings'],
-      'storage' => array(
-        'type' => $record['storage_type'],
-        'module' => $record['storage_module'],
-        'active' => $record['storage_active'],
-        'settings' => $record['data']['storage']['settings'],
-      ),
       'locked' => $record['locked'],
       'cardinality' => $record['cardinality'],
       'translatable' => $record['translatable'],
-      'entity_types' => $record['data']['entity_types'],
       'indexes' => $record['data']['indexes'] ?: array(),
       'status' => 1,
       'langcode' => 'und',
@@ -295,19 +347,17 @@ function field_update_8003() {
     else {
       $config['deleted'] = TRUE;
       $deleted_fields[$config['uuid']] = $config;
-      // Additionally, rename the data tables for deleted fields. Technically
-      // this would belong in an update in field_sql_storage.module, but it is
-      // easier to do it now, when the old numeric ID is available.
-      if ($config['storage']['type'] == 'field_sql_storage') {
-        $field = new Field($config);
-        $tables = array(
-          "field_deleted_data_{$record['id']}" => _field_sql_storage_tablename($field),
-          "field_deleted_revision_{$record['id']}" => _field_sql_storage_revision_tablename($field),
-        );
-        foreach ($tables as $table_old => $table_new) {
-          if (db_table_exists($table_old)) {
-            db_rename_table($table_old, $table_new);
-          }
+      // This will not be saved but the DatabaseStorageController helpers need
+      // the field object.
+      $field = new Field($config);
+      // Additionally, rename the data tables for deleted fields.
+      $tables = array(
+        "field_deleted_data_{$record['id']}" => 'old_' . DatabaseStorageController::_fieldTableName($field),
+        "field_deleted_revision_{$record['id']}" => 'old_' . DatabaseStorageController::_fieldRevisionTableName($field),
+      );
+      foreach ($tables as $table_old => $table_new) {
+        if (db_table_exists($table_old)) {
+          db_rename_table($table_old, $table_new);
         }
       }
     }
@@ -371,14 +421,13 @@ function field_update_8003() {
 }
 
 /**
- * Moves field_storage_default and field_language_fallback to config.
+ * Moves field_language_fallback to config.
  *
  * @ingroup config_upgrade
  */
 function field_update_8004() {
   update_variable_set('field_language_fallback', TRUE);
   update_variables_to_config('field.settings', array(
-    'field_storage_default' => 'default_storage',
     'field_language_fallback' => 'language_fallback',
   ));
 }
@@ -416,6 +465,106 @@ function field_update_8005() {
     ->save();
 }
 
+
+/**
+ * Splits the field storage tables by entity type and also migrate langcode.
+ */
+function field_update_8006(&$sandbox) {
+  // Get field definitions from config, and deleted fields from state system.
+  $config_names = config_get_storage_names_with_prefix('field.field');
+  $deleted_fields = Drupal::state()->get('field.field.deleted') ?: array();
+  // Ditch UUID keys, we will iterate through deleted fields using a numeric
+  // index.
+  $deleted_fields = array_values($deleted_fields);
+
+  if (empty($config_names) && empty($deleted_fields)) {
+    return;
+  }
+
+  if (!isset($sandbox['index'])) {
+    $sandbox['index'] = 0;
+    $sandbox['max'] = count($config_names) + count($deleted_fields);
+  }
+
+  // Retrieve the next field definition. When the index exceeds the number of
+  // 'configuration' fields, use it to iterate on deleted fields.
+  if (isset($config_names[$sandbox['index']])) {
+    $field_config = Drupal::config($config_names[$sandbox['index']])->get();
+  }
+  else {
+    $field_config = $deleted_fields[$sandbox['index'] - count($config_names)];
+  }
+
+  // Prepare updated schema data structures.
+  $field = new Field($field_config);
+  $tables = array(
+    array(
+      'old_table' => 'field_data_' . $field_config['name'],
+      'new_table' => DatabaseStorageController::_fieldTableName($field),
+      'primary_key' => array(
+        'entity_id',
+        'deleted',
+        'delta',
+        'langcode',
+      ),
+    ),
+    array(
+      'old_table' => 'field_revision_' . $field_config['name'],
+      'new_table' => DatabaseStorageController::_fieldRevisionTableName($field),
+      'primary_key' => array(
+        'entity_id',
+        'revision_id',
+        'deleted',
+        'delta',
+        'langcode',
+      ),
+    ),
+  );
+
+  // Move the field data to the new table.
+  foreach ($tables as $table_data) {
+    // Split data from the old "per field" table to the new "per entity type and
+    // field" table.
+    $new_table = $table_data['new_table'];
+    $original_table = empty($field_config['deleted']) ? $table_data['old_table'] : 'old_' . $new_table;
+    if (db_table_exists($original_table)) {
+      // Create the new table, with the same schema as the old one.
+      if (!db_table_exists($new_table)) {
+        db_copy_table_schema($original_table, $new_table);
+      }
+      // Copy relevant rows.
+      $from_query = db_select($original_table, 'original')
+        ->fields('original')
+        ->condition('entity_type', $field_config['entity_type']);
+      db_insert($new_table)
+        ->from($from_query)
+        ->execute();
+      // Drop the 'entity_type' column and previous primary key.
+      db_drop_primary_key($new_table);
+      db_drop_field($new_table, 'entity_type');
+      // Rename 'language' to 'langcode'. Tables created during the upgrade
+      // before this update function might already have the langcode column.
+      if (db_field_exists($new_table, 'language')) {
+        db_drop_index($new_table, 'language');
+        db_change_field($new_table, 'language', 'langcode', array(
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => TRUE,
+          'default' => '',
+        ));
+        db_add_index($new_table, 'langcode', array('langcode'));
+      }
+      // Create new primary key.
+      db_add_primary_key($new_table, $table_data['primary_key']);
+    }
+  }
+
+  $sandbox['index']++;
+  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['index'] / $sandbox['max']);
+}
+
+
+
 /**
  * @} End of "addtogroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index c2eda35..c5476d5 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -259,7 +259,7 @@ function field_populate_default_values(EntityInterface $entity, $langcode = NULL
   $entity_type = $entity->entityType();
   $langcode = $langcode ?: $entity->language()->id;
   foreach (field_info_instances($entity_type, $entity->bundle()) as $field_name => $instance) {
-    $field = field_info_field($field_name);
+    $field = $instance->getField();
     $field_langcode = field_is_translatable($entity_type, $field) ? $langcode : Language::LANGCODE_NOT_SPECIFIED;
     // We need to preserve existing values.
     if (empty($entity->{$field_name}) || !array_key_exists($field_langcode, $entity->{$field_name})) {
@@ -310,7 +310,7 @@ function field_entity_field_info($entity_type) {
 function _field_generate_entity_field_definition(FieldInterface $field, FieldInstanceInterface $instance = NULL) {
   // @todo: Allow for adding field type settings.
   $definition = array(
-    'label' => t('Field !name', array('!name' => $field->id())),
+    'label' => t('Field !name', array('!name' => $field->name)),
     'type' => 'field_item:' . $field->type,
     'list' => TRUE,
     'configurable' => TRUE,
@@ -422,24 +422,8 @@ function field_sync_field_status() {
   // modules.
   $changed = array();
   $modules = $module_handler->getModuleList();
-  foreach ($modules as $module => $module_info) {
-    // Collect storage backends exposed by the module.
-    $storage_types = (array) $module_handler->invoke($module, 'field_storage_info');
-
-    if ($storage_types) {
-      foreach ($fields as $uuid => &$field) {
-        // Associate storage backends.
-        if (isset($storage_types[$field['storage']['type']]) && ($field['storage']['module'] !== $module || !$field['storage']['active'])) {
-          $field['storage']['module'] = $module;
-          $field['storage']['active'] = TRUE;
-          $changed[$uuid] = $field;
-        }
-      }
-    }
-  }
-
   $field_types = Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions();
-  // Set fields with missing field type or storage modules to inactive.
+  // Set fields with missing field type modules to inactive.
   foreach ($fields as $uuid => &$field) {
     // Associate field types.
     if (isset($field_types[$field['type']]) && ($field['module'] != $field_types[$field['type']]['provider'] || !$field['active'])) {
@@ -451,11 +435,6 @@ function field_sync_field_status() {
       $field['active'] = FALSE;
       $changed[$uuid] = $field;
     }
-    // Disassociate storage backends.
-    if (!isset($modules[$field['storage']['module']]) && $field['storage']['active']) {
-      $field['storage']['active'] = FALSE;
-      $changed[$uuid] = $field;
-    }
   }
 
   // Store the updated field definitions.
@@ -467,8 +446,6 @@ function field_sync_field_status() {
       Drupal::config('field.field.' . $field['id'])
         ->set('module', $field['module'])
         ->set('active', $field['active'])
-        ->set('storage.module', $field['storage']['module'])
-        ->set('storage.active', $field['storage']['active'])
         ->save();
     }
   }
@@ -699,7 +676,7 @@ function field_view_value(EntityInterface $entity, $field_name, $item, $display
   // Ensure we are working with a BC mode entity.
   $entity = $entity->getBCEntity();
 
-  if ($field = field_info_field($field_name)) {
+  if ($field = field_info_field($entity->entityType(), $field_name)) {
     // Determine the langcode that will be used by language fallback.
     $langcode = field_language($entity, $field_name, $langcode);
 
@@ -789,7 +766,7 @@ function field_view_field(EntityInterface $entity, $field_name, $display_options
     $view_mode = '_custom';
     // hook_field_attach_display_alter() needs to receive the 'prepared'
     // $display_options, so we cannot let preparation happen internally.
-    $field = field_info_field($field_name);
+    $field = field_info_field($entity_type, $field_name);
     $formatter_manager = drupal_container()->get('plugin.manager.field.formatter');
     $display_options = $formatter_manager->prepareConfiguration($field['type'], $display_options);
     $formatter = $formatter_manager->getInstance(array(
diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc
index e5dea3b..7e24b44 100644
--- a/core/modules/field/field.views.inc
+++ b/core/modules/field/field.views.inc
@@ -8,30 +8,30 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\field\FieldInterface;
 
 /**
  * Implements hook_views_data().
  *
- * Field modules can implement hook_field_views_data() to override
- * the default behavior for adding fields.
+ * Field modules can implement hook_field_views_data() to override the default
+ * behavior for adding fields.
  */
 function field_views_data() {
   $data = array();
-  foreach (field_info_fields() as $field) {
-    if ($field['storage']['type'] != 'field_sql_storage') {
-      continue;
-    }
+  $module_handler = Drupal::moduleHandler();
 
-    $module = $field['module'];
-    $result = (array) module_invoke($module, 'field_views_data', $field);
-
-    if (empty($result)) {
-      $result = field_views_field_default_views_data($field);
-    }
-    Drupal::moduleHandler()->alter('field_views_data', $result, $field, $module);
+  foreach (field_info_fields() as $field) {
+    if (_field_views_is_sql_entity_type($field)) {
+      $result = (array) $module_handler->invoke($field['module'], 'field_views_data', array($field));
+      if (empty($result)) {
+        $result = field_views_field_default_views_data($field);
+      }
+      $module_handler->alter('field_views_data', $result, $field);
 
-    if (is_array($result)) {
-      $data = NestedArray::mergeDeep($result, $data);
+      if (is_array($result)) {
+        $data = NestedArray::mergeDeep($result, $data);
+      }
     }
   }
 
@@ -48,15 +48,34 @@ function field_views_data() {
  */
 function field_views_data_alter(&$data) {
   foreach (field_info_fields() as $field) {
-    if ($field['storage']['type'] != 'field_sql_storage') {
-      continue;
+    if (_field_views_is_sql_entity_type($field)) {
+      $function = $field['module'] . '_field_views_data_views_data_alter';
+      if (function_exists($function)) {
+        $function($data, $field);
+      }
     }
+  }
+}
 
-    $function = $field['module'] . '_field_views_data_views_data_alter';
-    if (function_exists($function)) {
-      $function($data, $field);
+/**
+ * Determines whether the entity type the field appears in is SQL based.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ *   The field definition.
+ *
+ * @return bool
+ *   True if the entity type uses DatabaseStorageController.
+ */
+function _field_views_is_sql_entity_type(FieldInterface $field) {
+  $entity_manager = Drupal::entityManager();
+  try {
+    if ($entity_manager->getStorageController($field->entity_type) instanceof DatabaseStorageController) {
+      return TRUE;
     }
   }
+  catch (\InvalidArgumentException $e) {
+    // Disabled entity type, nothing to do.
+  }
 }
 
 /**
@@ -64,17 +83,16 @@ function field_views_data_alter(&$data) {
  *
  * Therefore it looks up in all bundles to find the most used instance.
  */
-function field_views_field_label($field_name) {
+function field_views_field_label($entity_type, $field_name) {
   $label_counter = array();
   $all_labels = array();
   // Count the amount of instances per label per field.
-  $instances = field_info_instances();
-  foreach ($instances as $entity_name => $entity_type) {
-    foreach ($entity_type as $bundle) {
-      if (isset($bundle[$field_name])) {
-        $label_counter[$bundle[$field_name]['label']] = isset($label_counter[$bundle[$field_name]['label']]) ? ++$label_counter[$bundle[$field_name]->label] : 1;
-        $all_labels[$entity_name][$bundle[$field_name]['label']] = TRUE;
-      }
+  $instances = field_info_instances($entity_type);
+  foreach ($instances as $bundle => $bundle_instances) {
+    if (isset($bundle_instances[$field_name])) {
+      $instance = $bundle_instances[$field_name];
+      $label_counter[$instance->label] = isset($label_counter[$instance->label]) ? ++$label_counter[$instance->label] : 1;
+      $all_labels[$instance->label] = TRUE;
     }
   }
   if (empty($label_counter)) {
@@ -88,111 +106,105 @@ function field_views_field_label($field_name) {
 
 /**
  * Default views data implementation for a field.
+ *
+ * @param \Drupal\field\FieldInterface $field
+ *   The field definition.
+ *
+ * @return array
+ *   The default views data for the field.
  */
-function field_views_field_default_views_data($field) {
-  $field_types = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions();
+function field_views_field_default_views_data(FieldInterface $field) {
+  $data = array();
 
-  // Check the field module is available.
-  if (!isset($field_types[$field['type']])) {
-    return;
+  // Check the field type is available.
+  if (!\Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field['type'])) {
+    return $data;
+  }
+  // Check the field has instances.
+  if (empty($field['bundles'])) {
+    return $data;
   }
 
-  $data = array();
-
-  $current_table = _field_sql_storage_tablename($field);
-  $revision_table = _field_sql_storage_revision_tablename($field);
+  $field_name = $field['field_name'];
 
-  // The list of entity:bundle that this field is used in.
-  $bundles_names = array();
-  $supports_revisions = FALSE;
-  $entity_tables = array();
-  $current_tables = array();
-  $revision_tables = array();
-  $groups = array();
+  // Grab information about the entity type tables.
+  $entity_manager = Drupal::entityManager();
+  $entity_type = $field->entity_type;
+  $entity_info = $entity_manager->getDefinition($entity_type);
+  if (!isset($entity_info['base_table'])) {
+    return $data;
+  }
+  $entity_table = $entity_info['base_table'];
+  $entity_tables = array($entity_table => $entity_type);
+  $supports_revisions = !empty($entity_info['entity_keys']['revision']) && !empty($entity_info['revision_table']);
+  if ($supports_revisions) {
+    $entity_revision_table = $entity_info['revision_table'];
+    $entity_tables[$entity_revision_table] = $entity_type;
+  }
 
-  $group_name = count($field['bundles']) > 1 ? t('Field') : NULL;
+  // Description of the field tables.
+  $field_tables = array(
+    FIELD_LOAD_CURRENT => array(
+      'table' => DatabaseStorageController::_fieldTableName($field),
+      'alias' => "{$entity_type}__{$field->name}",
+    ),
+  );
+  if ($supports_revisions) {
+    $field_tables[FIELD_LOAD_REVISION] = array(
+      'table' => DatabaseStorageController::_fieldRevisionTableName($field),
+      'alias' => "{$entity_type}_revision__{$field->name}",
+    );
+  }
 
   // Build the relationships between the field table and the entity tables.
-  foreach ($field['bundles'] as $entity => $bundles) {
-    $entity_info = entity_get_info($entity);
-    $groups[$entity] = $entity_info['label'];
-
-    // Override Node to Content.
-    if ($groups[$entity] == t('Node')) {
-      $groups[$entity] = t('Content');
-    }
-
-    // If only one bundle use this as the default name.
-    if (empty($group_name)) {
-      $group_name = $groups[$entity];
-    }
-
-    if (!isset($entity_info['base_table'])) {
-      continue;
-    }
-    $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'],
-      'field' => 'entity_id',
+  $table_alias = $field_tables[FIELD_LOAD_CURRENT]['alias'];
+  $data[$table_alias]['table']['join'][$entity_table] = array(
+    'left_field' => $entity_info['entity_keys']['id'],
+    'field' => 'entity_id',
+    'extra' => array(
+      array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
+    ),
+  );
+  if ($supports_revisions) {
+    $table_alias = $field_tables[FIELD_LOAD_REVISION]['alias'];
+    $data[$table_alias]['table']['join'][$entity_revision_table] = array(
+      'left_field' => $entity_info['entity_keys']['revision'],
+      'field' => 'revision_id',
       'extra' => array(
-        array('field' => 'entity_type', 'value' => $entity),
         array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
       ),
     );
-
-    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),
-          array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
-        ),
-      );
-
-      $supports_revisions = TRUE;
-    }
-
-    foreach ($bundles as $bundle) {
-      $bundles_names[] = t('@entity:@bundle', array('@entity' => $entity, '@bundle' => $bundle));
-    }
-  }
-
-  $tables = array();
-  $tables[FIELD_LOAD_CURRENT] = $current_table;
-  if ($supports_revisions) {
-    $tables[FIELD_LOAD_REVISION] = $revision_table;
   }
 
+  // Override Node to Content.
+  $group_name = ($entity_info['label'] == t('Node')) ? t('Content') : $entity_info['label'];
+  // Get the list of bundles the field appears in.
+  $bundles_names = $field->getBundles();
+  // Build the list of additional fields to add to queries.
   $add_fields = array('delta', 'langcode', 'bundle');
-  foreach ($field['columns'] as $column_name => $attributes) {
-    $add_fields[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+  foreach (array_keys($field['columns']) as $column) {
+    $add_fields[] = DatabaseStorageController::_fieldColumnName($field, $column);
   }
+  // Determine the label to use for the field. We don't have a label available
+  // at the field level, so we just go through all instances and take the one
+  // which is used the most frequently.
+  list($label, $all_labels) = field_views_field_label($entity_type, $field_name);
+
+  // Expose data for the field as a whole.
+  foreach ($field_tables as $type => $table_info) {
+    $table = $table_info['table'];
+    $table_alias = $table_info['alias'];
 
-  // Note: we don't have a label available here, because we are at the field
-  // level, not at the instance level. So we just go through all instances
-  // and take the one which is used the most frequently.
-  $field_name = $field['field_name'];
-  list($label, $all_labels) = field_views_field_label($field_name);
-  foreach ($tables as $type => $table) {
     if ($type == FIELD_LOAD_CURRENT) {
       $group = $group_name;
-      $old_column = 'entity_id';
-      $column = $field['field_name'];
+      $field_alias = $field_name;
     }
     else {
       $group = t('@group (historical data)', array('@group' => $group_name));
-      $old_column = 'revision_id';
-      $column = $field['field_name'] . '-' . $old_column;
+      $field_alias = $field_name . '-revision_id';
     }
 
-    $data[$table][$column] = array(
+    $data[$table_alias][$field_alias] = array(
       'group' => $group,
       'title' => $label,
       'title short' => $label,
@@ -203,48 +215,42 @@ function field_views_field_default_views_data($field) {
     // entity type + name.
     $aliases = array();
     $also_known = array();
-    foreach ($all_labels as $entity_name => $labels) {
-      if (!isset($current_tables[$entity_name])) {
-        continue;
-      }
-      foreach ($labels as $label_name => $true) {
-        if ($type == FIELD_LOAD_CURRENT) {
-          if ($group_name != $groups[$entity_name] || $label != $label_name) {
-            $aliases[] = array(
-              'base' => $current_tables[$entity_name],
-              'group' => $groups[$entity_name],
-              'title' => $label_name,
-              'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
-            );
-          }
-          $also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $label_name));
-        }
-        else {
-          if ($group_name != $groups[$entity_name] && $label != $label_name && isset($revision_tables[$entity_name])) {
-            $aliases[] = array(
-              'base' => $revision_tables[$entity_name],
-              'group' => t('@group (historical data)', array('@group' => $groups[$entity_name])),
-              'title' => $label_name,
-              'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
-            );
-          }
-          $also_known[] = t('@group (historical data): @field', array('@group' => $groups[$entity_name], '@field' => $label_name));
+    foreach ($all_labels as $label_name => $true) {
+      if ($type == FIELD_LOAD_CURRENT) {
+        if ($label != $label_name) {
+          $aliases[] = array(
+            'base' => $entity_table,
+            'group' => $group_name,
+            'title' => $label_name,
+            'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
+          );
+          $also_known[] = t('@group: @field', array('@group' => $group_name, '@field' => $label_name));
         }
       }
+      elseif ($supports_revisions && $label != $label_name) {
+        $aliases[] = array(
+          'base' => $table,
+          'group' => t('@group (historical data)', array('@group' => $group_name)),
+          'title' => $label_name,
+          'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
+        );
+        $also_known[] = t('@group (historical data): @field', array('@group' => $group_name, '@field' => $label_name));
+      }
     }
     if ($aliases) {
-      $data[$table][$column]['aliases'] = $aliases;
-      $data[$table][$column]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
+      $data[$table_alias][$field_alias]['aliases'] = $aliases;
+      $data[$table_alias][$field_alias]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
     }
 
     $keys = array_keys($field['columns']);
     $real_field = reset($keys);
-    $data[$table][$column]['field'] = array(
+    $data[$table_alias][$field_alias]['field'] = array(
       'table' => $table,
       'id' => 'field',
-      'field_name' => $field['field_name'],
+      'field_name' => $field_name,
+      'entity_type' => $entity_type,
       // Provide a real field for group by.
-      'real field' => $column . '_' . $real_field,
+      'real field' => $field_alias . '_' . $real_field,
       'additional fields' => $add_fields,
       'entity_tables' => $entity_tables,
       // Default the element type to div, let the UI change it if necessary.
@@ -253,6 +259,7 @@ function field_views_field_default_views_data($field) {
     );
   }
 
+  // Expose data for each field property individually.
   foreach ($field['columns'] as $column => $attributes) {
     $allow_sort = TRUE;
 
@@ -281,27 +288,32 @@ function field_views_field_default_views_data($field) {
     }
 
     if (count($field['columns']) == 1 || $column == 'value') {
-      $title = t('@label (!name)', array('@label' => $label, '!name' => $field['field_name']));
+      $title = t('@label (!name)', array('@label' => $label, '!name' => $field_name));
       $title_short = $label;
     }
     else {
-      $title = t('@label (!name:!column)', array('@label' => $label, '!name' => $field['field_name'], '!column' => $column));
+      $title = t('@label (!name:!column)', array('@label' => $label, '!name' => $field_name, '!column' => $column));
       $title_short = t('@label:!column', array('@label' => $label, '!column' => $column));
     }
 
-    foreach ($tables as $type => $table) {
+    // Expose data for the property.
+    foreach ($field_tables as $type => $table_info) {
+      $table = $table_info['table'];
+      $table_alias = $table_info['alias'];
+
       if ($type == FIELD_LOAD_CURRENT) {
         $group = $group_name;
       }
       else {
         $group = t('@group (historical data)', array('@group' => $group_name));
       }
-      $column_real_name = $field['storage_details']['sql'][$type][$table][$column];
+      $column_real_name = DatabaseStorageController::_fieldColumnName($field, $column);
 
       // Load all the fields from the table by default.
-      $additional_fields = array_values($field['storage_details']['sql'][$type][$table]);
+      $field_sql_schema = DatabaseStorageController::_fieldSqlSchema($field);
+      $additional_fields = array_keys($field_sql_schema[$table]['fields']);
 
-      $data[$table][$column_real_name] = array(
+      $data[$table_alias][$column_real_name] = array(
         'group' => $group,
         'title' => $title,
         'title short' => $title_short,
@@ -312,130 +324,137 @@ function field_views_field_default_views_data($field) {
       // entity type + name.
       $aliases = array();
       $also_known = array();
-      foreach ($all_labels as $entity_name => $labels) {
-        foreach ($labels as $label_name => $true) {
-          if ($group_name != $groups[$entity_name] || $label != $label_name) {
-            if (count($field['columns']) == 1 || $column == 'value') {
-              $alias_title = t('@label (!name)', array('@label' => $label_name, '!name' => $field['field_name']));
-            }
-            else {
-              $alias_title = t('@label (!name:!column)', array('@label' => $label_name, '!name' => $field['field_name'], '!column' => $column));
-            }
-            $aliases[] = array(
-              'group' => $groups[$entity_name],
-              'title' => $alias_title,
-              'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $title)),
-            );
+      foreach ($all_labels as $label_name => $true) {
+        if ($label != $label_name) {
+          if (count($field['columns']) == 1 || $column == 'value') {
+            $alias_title = t('@label (!name)', array('@label' => $label_name, '!name' => $field_name));
+          }
+          else {
+            $alias_title = t('@label (!name:!column)', array('@label' => $label_name, '!name' => $field_name, '!column' => $column));
           }
-          $also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $title));
+          $aliases[] = array(
+            'group' => $group_name,
+            'title' => $alias_title,
+            'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $title)),
+          );
+          $also_known[] = t('@group: @field', array('@group' => $group_name, '@field' => $title));
         }
       }
       if ($aliases) {
-        $data[$table][$column_real_name]['aliases'] = $aliases;
-        $data[$table][$column_real_name]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
+        $data[$table_alias][$column_real_name]['aliases'] = $aliases;
+        $data[$table_alias][$column_real_name]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
       }
 
-      $data[$table][$column_real_name]['argument'] = array(
+      $data[$table_alias][$column_real_name]['argument'] = array(
         'field' => $column_real_name,
         'table' => $table,
         'id' => $argument,
         'additional fields' => $additional_fields,
-        'field_name' => $field['field_name'],
+        'field_name' => $field_name,
+        'entity_type' => $entity_type,
         'empty field name' => t('- No value -'),
       );
-      $data[$table][$column_real_name]['filter'] = array(
+      $data[$table_alias][$column_real_name]['filter'] = array(
         'field' => $column_real_name,
         'table' => $table,
         'id' => $filter,
         'additional fields' => $additional_fields,
-        'field_name' => $field['field_name'],
+        'field_name' => $field_name,
+        'entity_type' => $entity_type,
         'allow empty' => TRUE,
       );
       if (!empty($allow_sort)) {
-        $data[$table][$column_real_name]['sort'] = array(
+        $data[$table_alias][$column_real_name]['sort'] = array(
           'field' => $column_real_name,
           'table' => $table,
           'id' => $sort,
           'additional fields' => $additional_fields,
-          'field_name' => $field['field_name'],
+          'field_name' => $field_name,
+          'entity_type' => $entity_type,
         );
       }
 
       // Expose additional delta column for multiple value fields.
       if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
-        $title_delta = t('@label (!name:delta)', array('@label' => $label, '!name' => $field['field_name']));
+        $title_delta = t('@label (!name:delta)', array('@label' => $label, '!name' => $field_name));
         $title_short_delta = t('@label:delta', array('@label' => $label));
 
-        $data[$table]['delta'] = array(
+        $data[$table_alias]['delta'] = array(
           'group' => $group,
           'title' => $title_delta,
           'title short' => $title_short_delta,
           'help' => t('Delta - Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))),
         );
-        $data[$table]['delta']['field'] = array(
+        $data[$table_alias]['delta']['field'] = array(
           'id' => 'numeric',
         );
-        $data[$table]['delta']['argument'] = array(
+        $data[$table_alias]['delta']['argument'] = array(
           'field' => 'delta',
           'table' => $table,
           'id' => 'numeric',
           'additional fields' => $additional_fields,
           'empty field name' => t('- No value -'),
-          'field_name' => $field['field_name'],
+          'field_name' => $field_name,
+          'entity_type' => $entity_type,
         );
-        $data[$table]['delta']['filter'] = array(
+        $data[$table_alias]['delta']['filter'] = array(
           'field' => 'delta',
           'table' => $table,
           'id' => 'numeric',
           'additional fields' => $additional_fields,
-          'field_name' => $field['field_name'],
+          'field_name' => $field_name,
+          'entity_type' => $entity_type,
           'allow empty' => TRUE,
         );
-        $data[$table]['delta']['sort'] = array(
+        $data[$table_alias]['delta']['sort'] = array(
           'field' => 'delta',
           'table' => $table,
           'id' => 'standard',
           'additional fields' => $additional_fields,
-          'field_name' => $field['field_name'],
+          'field_name' => $field_name,
+          'entity_type' => $entity_type,
         );
       }
 
       // Expose additional language column for translatable fields.
       if (!empty($field['translatable'])) {
-        $title_language = t('@label (!name:language)', array('@label' => $label, '!name' => $field['field_name']));
+        $title_language = t('@label (!name:language)', array('@label' => $label, '!name' => $field_name));
         $title_short_language = t('@label:language', array('@label' => $label));
 
-        $data[$table]['language'] = array(
+        $data[$table_alias]['language'] = array(
           'group' => $group,
           'title' => $title_language,
           'title short' => $title_short_language,
           'help' => t('Language - Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))),
         );
-        $data[$table]['language']['field'] = array(
+        $data[$table_alias]['language']['field'] = array(
           'id' => 'language',
         );
-        $data[$table]['language']['argument'] = array(
+        $data[$table_alias]['language']['argument'] = array(
           'field' => 'language',
           'table' => $table,
           'id' => 'language',
           'additional fields' => $additional_fields,
           'empty field name' => t('- No value -'),
-          'field_name' => $field['field_name'],
+          'field_name' => $field_name,
+          'entity_type' => $entity_type,
         );
-        $data[$table]['language']['filter'] = array(
+        $data[$table_alias]['language']['filter'] = array(
           'field' => 'language',
           'table' => $table,
           'id' => 'language',
           'additional fields' => $additional_fields,
-          'field_name' => $field['field_name'],
+          'field_name' => $field_name,
+          'entity_type' => $entity_type,
           'allow empty' => TRUE,
         );
-        $data[$table]['language']['sort'] = array(
+        $data[$table_alias]['language']['sort'] = array(
           'field' => 'language',
           'table' => $table,
           'id' => 'standard',
           'additional fields' => $additional_fields,
-          'field_name' => $field['field_name'],
+          'field_name' => $field_name,
+          'entity_type' => $entity_type,
         );
       }
     }
diff --git a/core/modules/field/lib/Drupal/field/FieldInfo.php b/core/modules/field/lib/Drupal/field/FieldInfo.php
index 2639c25..096841d 100644
--- a/core/modules/field/lib/Drupal/field/FieldInfo.php
+++ b/core/modules/field/lib/Drupal/field/FieldInfo.php
@@ -165,10 +165,10 @@ public function flush() {
    * Collects a lightweight map of fields across bundles.
    *
    * @return
-   *   An array keyed by field name. Each value is an array with two entries:
+   *   An array keyed by entity type. Each value is an array which keys are
+   *   field names and value is an array with two entries:
    *   - type: The field type.
-   *   - bundles: The bundles in which the field appears, as an array with
-   *     entity types as keys and the array of bundle names as values.
+   *   - bundles: The bundles in which the field appears.
    */
   public function getFieldMap() {
     // Read from the "static" cache.
@@ -191,7 +191,7 @@ public function getFieldMap() {
     // Get active fields.
     foreach (config_get_storage_names_with_prefix('field.field.') as $config_id) {
       $field_config = $this->config->get($config_id)->get();
-      if ($field_config['active'] && $field_config['storage']['active']) {
+      if ($field_config['active']) {
         $fields[$field_config['uuid']] = $field_config;
       }
     }
@@ -203,8 +203,8 @@ public function getFieldMap() {
       // entity types.
       if (isset($fields[$field_uuid])) {
         $field = $fields[$field_uuid];
-        $map[$field['id']]['bundles'][$instance_config['entity_type']][] = $instance_config['bundle'];
-        $map[$field['id']]['type'] = $field['type'];
+        $map[$instance_config['entity_type']][$field['name']]['bundles'][] = $instance_config['bundle'];
+        $map[$instance_config['entity_type']][$field['name']]['type'] = $field['type'];
       }
     }
 
@@ -244,7 +244,7 @@ public function getFields() {
     // Fill the name/ID map.
     foreach ($this->fieldsById as $field) {
       if (!$field['deleted']) {
-        $this->fieldIdsByName[$field['id']] = $field['uuid'];
+        $this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
       }
     }
 
@@ -280,7 +280,7 @@ public function getInstances($entity_type = NULL) {
         $this->getFields();
 
         foreach (field_read_instances() as $instance) {
-          $field = $this->getField($instance['field_name']);
+          $field = $this->getField($instance['entity_type'], $instance['field_name']);
           $instance = $this->prepareInstance($instance, $field['type']);
           $this->bundleInstances[$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
         }
@@ -305,36 +305,38 @@ public function getInstances($entity_type = NULL) {
    *
    * This method only retrieves active, non-deleted fields.
    *
-   * @param $field_name
+   * @param string $entity_type
+   *   The entity type.
+   * @param string $field_name
    *   The field name.
    *
    * @return
    *   The field definition, or NULL if no field was found.
    */
-  public function getField($field_name) {
+  public function getField($entity_type, $field_name) {
     // Read from the "static" cache.
-    if (isset($this->fieldIdsByName[$field_name])) {
-      $field_id = $this->fieldIdsByName[$field_name];
+    if (isset($this->fieldIdsByName[$entity_type][$field_name])) {
+      $field_id = $this->fieldIdsByName[$entity_type][$field_name];
       return $this->fieldsById[$field_id];
     }
-    if (isset($this->unknownFields[$field_name])) {
+    if (isset($this->unknownFields[$entity_type][$field_name])) {
       return;
     }
 
     // Do not check the (large) persistent cache, but read the definition.
 
     // Cache miss: read from definition.
-    if ($field = field_read_field($field_name)) {
+    if ($field = entity_load('field_entity', $entity_type . '.' . $field_name)) {
       $field = $this->prepareField($field);
 
       // Save in the "static" cache.
       $this->fieldsById[$field['uuid']] = $field;
-      $this->fieldIdsByName[$field['field_name']] = $field['uuid'];
+      $this->fieldIdsByName[$entity_type][$field_name] = $field['uuid'];
 
       return $field;
     }
     else {
-      $this->unknownFields[$field_name] = TRUE;
+      $this->unknownFields[$entity_type][$field_name] = TRUE;
     }
   }
 
@@ -369,7 +371,7 @@ public function getFieldById($field_id) {
       // Store in the static cache.
       $this->fieldsById[$field['uuid']] = $field;
       if (!$field['deleted']) {
-        $this->fieldIdsByName[$field['field_name']] = $field['uuid'];
+        $this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
       }
 
       return $field;
@@ -411,7 +413,7 @@ public function getBundleInstances($entity_type, $bundle) {
         if (!isset($this->fieldsById[$field['uuid']])) {
           $this->fieldsById[$field['uuid']] = $field;
           if (!$field['deleted']) {
-            $this->fieldIdsByName[$field['field_name']] = $field['uuid'];
+            $this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
           }
         }
       }
@@ -431,39 +433,42 @@ public function getBundleInstances($entity_type, $bundle) {
 
     // Cache miss: collect from the definitions.
     $instances = array();
+    $field_map = $this->getFieldMap();
 
     // Do not return anything for unknown entity types.
-    if (entity_get_info($entity_type)) {
+    if (entity_get_info($entity_type) && !empty($field_map[$entity_type])) {
 
       // Collect names of fields and instances involved in the bundle, using the
       // field map. The field map is already filtered to active, non-deleted
       // fields and instances, so those are kept out of the persistent caches.
       $config_ids = array();
-      foreach ($this->getFieldMap() as $field_name => $field_data) {
-        if (isset($field_data['bundles'][$entity_type]) && in_array($bundle, $field_data['bundles'][$entity_type])) {
-          $config_ids[$field_name] = "$entity_type.$bundle.$field_name";
+      foreach ($field_map[$entity_type] as $field_name => $field_data) {
+        if (in_array($bundle, $field_data['bundles'])) {
+          $config_ids["$entity_type.$field_name"] = "$entity_type.$bundle.$field_name";
         }
       }
 
       // Load and prepare the corresponding fields and instances entities.
       if ($config_ids) {
+        // Place the fields in our global "static".
         $loaded_fields = entity_load_multiple('field_entity', array_keys($config_ids));
-        $loaded_instances = entity_load_multiple('field_instance', array_values($config_ids));
-
-        foreach ($loaded_instances as $instance) {
-          $field = $loaded_fields[$instance['field_name']];
-
-          $instance = $this->prepareInstance($instance, $field['type']);
-          $instances[$field['field_name']] = $instance;
-
-          // If the field is not in our global "static" list yet, add it.
+        foreach ($loaded_fields as $field) {
           if (!isset($this->fieldsById[$field['uuid']])) {
             $field = $this->prepareField($field);
 
             $this->fieldsById[$field['uuid']] = $field;
-            $this->fieldIdsByName[$field['field_name']] = $field['uuid'];
+            $this->fieldIdsByName[$field->entity_type][$field->name] = $field['uuid'];
           }
         }
+
+        // Then collect the instances.
+        $loaded_instances = entity_load_multiple('field_instance', array_values($config_ids));
+        foreach ($loaded_instances as $instance) {
+          $field = $instance->getField();
+
+          $instance = $this->prepareInstance($instance, $field['type']);
+          $instances[$field['field_name']] = $instance;
+        }
       }
     }
 
@@ -565,7 +570,6 @@ public function getBundleExtraFields($entity_type, $bundle) {
   public function prepareField($field) {
     // Make sure all expected field settings are present.
     $field['settings'] += $this->fieldTypeManager->getDefaultSettings($field['type']);
-    $field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
 
     return $field;
   }
diff --git a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
index f9ce249..2649c6a 100644
--- a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
@@ -136,7 +136,6 @@ public function loadByProperties(array $conditions = array()) {
     // Translate "do not include inactive fields" into actual conditions.
     if (!$include_inactive) {
       $conditions['field.active'] = TRUE;
-      $conditions['field.storage.active'] = TRUE;
     }
 
     // Collect matching instances.
@@ -155,17 +154,13 @@ public function loadByProperties(array $conditions = array()) {
         // Extract the actual value against which the condition is checked.
         switch ($key) {
           case 'field_name':
-            $checked_value = $field->id;
+            $checked_value = $field->name;
             break;
 
           case 'field.active':
             $checked_value = $field->active;
             break;
 
-          case 'field.storage.active':
-            $checked_value = $field->storage['active'];
-            break;
-
           case 'field_id':
             $checked_value = $instance->field_uuid;
             break;
diff --git a/core/modules/field/lib/Drupal/field/FieldInterface.php b/core/modules/field/lib/Drupal/field/FieldInterface.php
index 132bc39..8d7edd8 100644
--- a/core/modules/field/lib/Drupal/field/FieldInterface.php
+++ b/core/modules/field/lib/Drupal/field/FieldInterface.php
@@ -45,34 +45,6 @@ public function getSchema();
   public function getColumns();
 
   /**
-   * Returns information about how the storage backend stores the field data.
-   *
-   * The content of the returned value depends on the storage backend, and some
-   * storage backends might provide no information.
-   *
-   * It is strongly discouraged to use this information to perform direct write
-   * operations to the field data storage, bypassing the regular field saving
-   * APIs.
-   *
-   * Example return value for the default field_sql_storage backend:
-   * - 'sql'
-   *   - FIELD_LOAD_CURRENT
-   *     - Table name (string).
-   *       - Table schema (array)
-   *   - FIELD_LOAD_REVISION
-   *     - Table name (string).
-   *       - Table schema (array).
-   *
-   * @return array
-   *   The storage details.
-   *    - The first dimension is a store type (sql, solr, etc).
-   *    - The second dimension indicates the age of the values in the store
-   *      FIELD_LOAD_CURRENT or FIELD_LOAD_REVISION.
-   *    - Other dimensions are specific to the field storage backend.
-   */
-  public function getStorageDetails();
-
-  /**
    * Returns the list of bundles where the field has instances.
    *
    * @return array
diff --git a/core/modules/field/lib/Drupal/field/FieldStorageController.php b/core/modules/field/lib/Drupal/field/FieldStorageController.php
index 41efe60..8b0f92e 100644
--- a/core/modules/field/lib/Drupal/field/FieldStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldStorageController.php
@@ -98,9 +98,10 @@ public function loadByProperties(array $conditions = array()) {
     unset($conditions['include_deleted']);
 
     // Get fields stored in configuration.
-    if (isset($conditions['field_name'])) {
+    if (isset($conditions['entity_type']) && isset($conditions['field_name'])) {
       // Optimize for the most frequent case where we do have a specific ID.
-      $fields = $this->entityManager->getStorageController($this->entityType)->loadMultiple(array($conditions['field_name']));
+      $id = $conditions['entity_type'] . $conditions['field_name'];
+      $fields = $this->entityManager->getStorageController($this->entityType)->loadMultiple(array($id));
     }
     else {
       // No specific ID, we need to examine all existing fields.
@@ -118,7 +119,6 @@ public function loadByProperties(array $conditions = array()) {
     // Translate "do not include inactive instances" into actual conditions.
     if (!$include_inactive) {
       $conditions['active'] = TRUE;
-      $conditions['storage.active'] = TRUE;
     }
 
     // Collect matching fields.
@@ -127,12 +127,8 @@ public function loadByProperties(array $conditions = array()) {
       foreach ($conditions as $key => $value) {
         // Extract the actual value against which the condition is checked.
         switch ($key) {
-          case 'storage.active':
-            $checked_value = $field->storage['active'];
-            break;
-
           case 'field_name';
-            $checked_value = $field->id;
+            $checked_value = $field->name;
             break;
 
           default:
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
index e669900..3c777ce 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
@@ -37,24 +37,35 @@
 class Field extends ConfigEntityBase implements FieldInterface {
 
   /**
-   * The maximum length of the field ID (machine name), in characters.
+   * The maximum length of the field name, in characters.
    *
    * For fields created through Field UI, this includes the 'field_' prefix.
    */
-  const ID_MAX_LENGTH = 32;
+  const NAME_MAX_LENGTH = 32;
 
   /**
-   * The field ID (machine name).
+   * The field ID.
+   *
+   * The ID consists of 2 parts: the entity type and the field name.
+   *
+   * Example: node.body, user.field_main_image.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The field name.
    *
    * This is the name of the property under which the field values are placed in
-   * an entity : $entity-{>$field_id}. The maximum length is
-   * Field:ID_MAX_LENGTH.
+   * an entity: $entity->{$field_name}. The maximum length is
+   * Field:NAME_MAX_LENGTH.
    *
    * Example: body, field_main_image.
    *
    * @var string
    */
-  public $id;
+  public $name;
 
   /**
    * The field UUID.
@@ -66,6 +77,13 @@ class Field extends ConfigEntityBase implements FieldInterface {
   public $uuid;
 
   /**
+   * The name of the entity type the field can be attached to.
+   *
+   * @var string
+   */
+  public $entity_type;
+
+  /**
    * The field type.
    *
    * Example: text, number_integer.
@@ -118,16 +136,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
   public $translatable = FALSE;
 
   /**
-   * The entity types on which the field is allowed to have instances.
-   *
-   * If empty or not specified, the field is allowed to have instances in any
-   * entity type.
-   *
-   * @var array
-   */
-  public $entity_types = array();
-
-  /**
    * Flag indicating whether the field is available for editing.
    *
    * If TRUE, some actions not available though the UI (but are still possible
@@ -142,25 +150,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
   public $locked = FALSE;
 
   /**
-   * The field storage definition.
-   *
-   * An array of key/value pairs identifying the storage backend to use for the
-   * field:
-   * - type: (string) The storage backend used by the field. Storage backends
-   *   are defined by modules that implement hook_field_storage_info().
-   * - settings: (array) A sub-array of key/value pairs of settings. The keys
-   *   and default values are defined by the storage backend in the 'settings'
-   *   entry of hook_field_storage_info().
-   * - module: (string, read-only) The name of the module that implements the
-   *   storage backend.
-   * - active: (integer, read-only) TRUE if the module that implements the
-   *   storage backend is currently enabled, FALSE otherwise.
-   *
-   * @var array
-   */
-  public $storage = array();
-
-  /**
    * The custom storage indexes for the field data storage.
    *
    * This set of indexes is merged with the "default" indexes specified by the
@@ -201,13 +190,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
   protected $schema;
 
   /**
-   * The storage information for the field.
-   *
-   * @var array
-   */
-  protected $storageDetails;
-
-  /**
    * The original field.
    *
    * @var \Drupal\field\Plugin\Core\Entity\Field
@@ -222,8 +204,9 @@ class Field extends ConfigEntityBase implements FieldInterface {
    *   elements will be used to set the corresponding properties on the class;
    *   see the class property documentation for details. Some array elements
    *   have special meanings and a few are required. Special elements are:
-   *   - id: required. As a temporary Backwards Compatibility layer right now,
+   *   - name: required. As a temporary Backwards Compatibility layer right now,
    *     a 'field_name' property can be accepted in place of 'id'.
+   *   - entity_type: required.
    *   - type: required.
    *
    * In most cases, Field entities are created via
@@ -236,22 +219,18 @@ class Field extends ConfigEntityBase implements FieldInterface {
    */
   public function __construct(array $values, $entity_type = 'field_entity') {
     // Check required properties.
-    if (empty($values['type'])) {
-      throw new FieldException('Attempt to create a field with no type.');
-    }
-    // Temporary BC layer: accept both 'id' and 'field_name'.
-    // @todo $field_name and the handling for it will be removed in
-    //   http://drupal.org/node/1953408.
-    if (empty($values['field_name']) && empty($values['id'])) {
+    if (empty($values['name'])) {
       throw new FieldException('Attempt to create an unnamed field.');
     }
-    if (empty($values['id'])) {
-      $values['id'] = $values['field_name'];
-      unset($values['field_name']);
-    }
-    if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['id'])) {
+    if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['name'])) {
       throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
     }
+    if (empty($values['type'])) {
+      throw new FieldException('Attempt to create a field with no type.');
+    }
+    if (empty($values['entity_type'])) {
+      throw new FieldException('Attempt to create a field with no entity_type.');
+    }
 
     parent::__construct($values, $entity_type);
   }
@@ -259,18 +238,25 @@ public function __construct(array $values, $entity_type = 'field_entity') {
   /**
    * {@inheritdoc}
    */
+  public function id() {
+    return $this->entity_type . '.' . $this->name;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getExportProperties() {
     $names = array(
       'id',
       'uuid',
       'status',
       'langcode',
+      'name',
+      'entity_type',
       'type',
       'settings',
       'module',
       'active',
-      'entity_types',
-      'storage',
       'locked',
       'cardinality',
       'translatable',
@@ -296,7 +282,7 @@ public function getExportProperties() {
    */
   public function save() {
     // Clear the derived data about the field.
-    unset($this->schema, $this->storageDetails);
+    unset($this->schema);
 
     if ($this->isNew()) {
       return $this->saveNew();
@@ -318,18 +304,20 @@ public function save() {
    *   In case of failures at the configuration storage level.
    */
   protected function saveNew() {
-    $module_handler = \Drupal::moduleHandler();
     $entity_manager = \Drupal::entityManager();
     $storage_controller = $entity_manager->getStorageController($this->entityType);
 
-    // Field name cannot be longer than Field::ID_MAX_LENGTH characters. We
+    // Assign the ID.
+    $this->id = $this->id();
+
+    // Field name cannot be longer than Field::NAME_MAX_LENGTH characters. We
     // use drupal_strlen() because the DB layer assumes that column widths
     // are given in characters rather than bytes.
-    if (drupal_strlen($this->id) > static::ID_MAX_LENGTH) {
+    if (drupal_strlen($this->name) > static::NAME_MAX_LENGTH) {
       throw new FieldException(format_string(
-        'Attempt to create a field with an ID longer than @max characters: %id', array(
-          '@max' => static::ID_MAX_LENGTH,
-          '%id' => $this->id,
+        'Attempt to create a field with an ID longer than @max characters: %name', array(
+          '@max' => static::NAME_MAX_LENGTH,
+          '%name' => $this->name,
         )
       ));
     }
@@ -337,17 +325,17 @@ protected function saveNew() {
     // Ensure the field name is unique (we do not care about deleted fields).
     if ($prior_field = $storage_controller->load($this->id)) {
       $message = $prior_field->active ?
-        'Attempt to create field name %id which already exists and is active.' :
-        'Attempt to create field name %id which already exists, although it is inactive.';
-      throw new FieldException(format_string($message, array('%id' => $this->id)));
+        'Attempt to create field name %name which already exists and is active.' :
+        'Attempt to create field name %name which already exists, although it is inactive.';
+      throw new FieldException(format_string($message, array('%name' => $this->name)));
     }
 
     // Disallow reserved field names. This can't prevent all field name
     // collisions with existing entity properties, but some is better than
     // none.
     foreach ($entity_manager->getDefinitions() as $type => $info) {
-      if (in_array($this->id, $info['entity_keys'])) {
-        throw new FieldException(format_string('Attempt to create field %id which is reserved by entity type %type.', array('%id' => $this->id, '%type' => $type)));
+      if (in_array($this->name, $info['entity_keys'])) {
+        throw new FieldException(format_string('Attempt to create field %name which is reserved by entity type %type.', array('%name' => $this->name, '%type' => $type)));
       }
     }
 
@@ -363,23 +351,8 @@ protected function saveNew() {
     // definition is passed to the various hooks and written to config.
     $this->settings += $field_type['settings'];
 
-    // Provide default storage.
-    $this->storage += array(
-      'type' => variable_get('field_storage_default', 'field_sql_storage'),
-      'settings' => array(),
-    );
-    // Check that the storage type is known.
-    $storage_type = field_info_storage_types($this->storage['type']);
-    if (!$storage_type) {
-      throw new FieldException(format_string('Attempt to create a field with unknown storage type %type.', array('%type' => $this->storage['type'])));
-    }
-    $this->storage['module'] = $storage_type['module'];
-    $this->storage['active'] = TRUE;
-    // Provide default storage settings.
-    $this->storage['settings'] += $storage_type['settings'];
-
-    // Invoke the storage backend's hook_field_storage_create_field().
-    $module_handler->invoke($this->storage['module'], 'field_storage_create_field', array($this));
+    // Notify the entity storage controller.
+    $entity_manager->getStorageController($this->entity_type)->handleFieldCreate($this);
 
     // Save the configuration.
     $result = parent::save();
@@ -401,7 +374,8 @@ protected function saveNew() {
    */
   protected function saveUpdated() {
     $module_handler = \Drupal::moduleHandler();
-    $storage_controller = \Drupal::entityManager()->getStorageController($this->entityType);
+    $entity_manager = \Drupal::entityManager();
+    $storage_controller = $entity_manager->getStorageController($this->entityType);
 
     $original = $storage_controller->loadUnchanged($this->id());
     $this->original = $original;
@@ -410,11 +384,8 @@ protected function saveUpdated() {
     if ($this->type != $original->type) {
       throw new FieldException("Cannot change an existing field's type.");
     }
-    if ($this->entity_types != $original->entity_types) {
-      throw new FieldException("Cannot change an existing field's entity_types property.");
-    }
-    if ($this->storage['type'] != $original->storage['type']) {
-      throw new FieldException("Cannot change an existing field's storage type.");
+    if ($this->entity_type != $original->entity_type) {
+      throw new FieldException("Cannot change an existing field's entity_type.");
     }
 
     // Make sure all settings are present, so that a complete field definition
@@ -426,11 +397,10 @@ protected function saveUpdated() {
     // invokes hook_field_update_forbid().
     $module_handler->invokeAll('field_update_forbid', array($this, $original));
 
-    // Tell the storage engine to update the field by invoking the
-    // hook_field_storage_update_field(). The storage engine can reject the
-    // definition update as invalid by raising an exception, which stops
-    // execution before the definition is written to config.
-    $module_handler->invoke($this->storage['module'], 'field_storage_update_field', array($this, $original));
+    // Notify the storage controller. The controller can reject the definition
+    // update as invalid by raising an exception, which stops execution before
+    // the definition is written to config.
+    $entity_manager->getStorageController($this->entity_type)->handleFieldUpdate($this, $original);
 
     // Save the configuration.
     $result = parent::save();
@@ -444,16 +414,13 @@ protected function saveUpdated() {
    */
   public function delete() {
     if (!$this->deleted) {
-      $module_handler = \Drupal::moduleHandler();
       $instance_controller = \Drupal::entityManager()->getStorageController('field_instance');
       $state = \Drupal::state();
 
       // Delete all non-deleted instances.
       $instance_ids = array();
-      foreach ($this->getBundles() as $entity_type => $bundles) {
-        foreach ($bundles as $bundle) {
-          $instance_ids[] = "$entity_type.$bundle.$this->id";
-        }
+      foreach ($this->getBundles() as $bundle) {
+        $instance_ids[] = "{$this->entity_type}.$bundle.{$this->name}";
       }
       foreach ($instance_controller->loadMultiple($instance_ids) as $instance) {
         // By default, FieldInstance::delete() will automatically try to delete
@@ -463,9 +430,7 @@ public function delete() {
         $instance->delete(FALSE);
       }
 
-      // Mark field data for deletion by invoking
-      // hook_field_storage_delete_field().
-      $module_handler->invoke($this->storage['module'], 'field_storage_delete_field', array($this));
+      \Drupal::entityManager()->getStorageController($this->entity_type)->handleFieldDelete($this);
 
       // Delete the configuration of this field and save the field configuration
       // in the key_value table so we can use it later during
@@ -528,30 +493,11 @@ public function getColumns() {
   /**
    * {@inheritdoc}
    */
-  public function getStorageDetails() {
-    if (!isset($this->storageDetails)) {
-      $module_handler = \Drupal::moduleHandler();
-
-      // Collect the storage details from the storage backend, and let other
-      // modules alter it. This invokes hook_field_storage_details() and
-      // hook_field_storage_details_alter().
-      $details = (array) $module_handler->invoke($this->storage['module'], 'field_storage_details', array($this));
-      $module_handler->alter('field_storage_details', $details, $this);
-
-      $this->storageDetails = $details;
-    }
-
-    return $this->storageDetails;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getBundles() {
     if (empty($this->deleted)) {
       $map = field_info_field_map();
-      if (isset($map[$this->id]['bundles'])) {
-        return $map[$this->id]['bundles'];
+      if (isset($map[$this->entity_type][$this->name]['bundles'])) {
+        return $map[$this->entity_type][$this->name]['bundles'];
       }
     }
     return array();
@@ -561,7 +507,7 @@ public function getBundles() {
    * {@inheritdoc}
    */
   public function getFieldName() {
-    return $this->id;
+    return $this->name;
   }
 
   /**
@@ -664,7 +610,7 @@ public function &offsetGet($offset) {
         return $this->uuid;
 
       case 'field_name':
-        return $this->id;
+        return $this->name;
 
       case 'columns':
         $this->getSchema();
@@ -677,10 +623,6 @@ public function &offsetGet($offset) {
       case 'bundles':
         $bundles = $this->getBundles();
         return $bundles;
-
-      case 'storage_details':
-        $this->getStorageDetails();
-        return $this->storageDetails;
     }
 
     return $this->{$offset};
@@ -735,19 +677,19 @@ public static function getReservedColumns() {
    *   TRUE if the field has data for any entity; FALSE otherwise.
    */
   public function hasData() {
-    $storage_details = $this->getSchema();
-    $columns = array_keys($storage_details['columns']);
-    $factory = \Drupal::service('entity.query');
-    foreach ($this->getBundles() as $entity_type => $bundle) {
+    if ($this->getBundles()) {
+      $storage_details = $this->getSchema();
+      $columns = array_keys($storage_details['columns']);
+      $factory = \Drupal::service('entity.query');
       // Entity Query throws an exception if there is no base table.
-      $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
+      $entity_info = \Drupal::entityManager()->getDefinition($this->entity_type);
       if (!isset($entity_info['base_table'])) {
-        continue;
+        return FALSE;
       }
-      $query = $factory->get($entity_type);
+      $query = $factory->get($this->entity_type);
       $group = $query->orConditionGroup();
       foreach ($columns as $column) {
-        $group->exists($this->id() . '.' . $column);
+        $group->exists($this->name . '.' . $column);
       }
       $result = $query
         ->condition($group)
@@ -762,4 +704,5 @@ public function hasData() {
 
     return FALSE;
   }
+
 }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
index 1950a9f..cfe66b2 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\field\Plugin\Core\Entity;
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\Entity\Annotation\EntityType;
 use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
@@ -34,7 +35,7 @@
 class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
 
   /**
-   * The instance ID (machine name).
+   * The instance ID.
    *
    * The ID consists of 3 parts: the entity type, bundle and the field name.
    *
@@ -234,10 +235,10 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
   public function __construct(array $values, $entity_type = 'field_instance') {
     // Accept incoming 'field_name' instead of 'field_uuid', for easier DX on
     // creation of new instances.
-    if (isset($values['field_name']) && !isset($values['field_uuid'])) {
-      $field = field_info_field($values['field_name']);
+    if (isset($values['field_name']) && isset($values['entity_type']) && !isset($values['field_uuid'])) {
+      $field = field_info_field($values['entity_type'], $values['field_name']);
       if (!$field) {
-        throw new FieldException(format_string('Attempt to create an instance of unknown, disabled, or deleted field @field_id', array('@field_id' => $values['field_name'])));
+        throw new FieldException(format_string('Attempt to create an instance of field @field_name that does not exist on entity type @entity_type.', array('@field_name' => $values['field_name'], '@entity_type' => $values['entity_type'])));
       }
       $values['field_uuid'] = $field->uuid;
     }
@@ -266,16 +267,16 @@ public function __construct(array $values, $entity_type = 'field_instance') {
 
     // Check required properties.
     if (empty($values['entity_type'])) {
-      throw new FieldException(format_string('Attempt to create an instance of field @field_id without an entity type.', array('@field_id' => $this->field->id)));
+      throw new FieldException(format_string('Attempt to create an instance of field @field_name without an entity_type.', array('@field_name' => $this->field->name)));
     }
     if (empty($values['bundle'])) {
-      throw new FieldException(format_string('Attempt to create an instance of field @field_id without a bundle.', array('@field_id' => $this->field->id)));
+      throw new FieldException(format_string('Attempt to create an instance of field @field_name without a bundle.', array('@field_name' => $this->field->name)));
     }
 
-    // 'Label' defaults to the field ID (mostly useful for field instances
+    // 'Label' defaults to the field name (mostly useful for field instances
     // created in tests).
     $values += array(
-      'label' => $this->field->id,
+      'label' => $this->field->name,
     );
     parent::__construct($values, $entity_type);
   }
@@ -284,7 +285,7 @@ public function __construct(array $values, $entity_type = 'field_instance') {
    * {@inheritdoc}
    */
   public function id() {
-    return $this->entity_type . '.' . $this->bundle . '.' . $this->field->id;
+    return $this->entity_type . '.' . $this->bundle . '.' . $this->field->name;
   }
 
   /**
@@ -354,14 +355,9 @@ public function save() {
   protected function saveNew() {
     $instance_controller = \Drupal::entityManager()->getStorageController($this->entityType);
 
-    // Check that the field can be attached to this entity type.
-    if (!empty($this->field->entity_types) && !in_array($this->entity_type, $this->field->entity_types)) {
-      throw new FieldException(format_string('Attempt to create an instance of field @field_id on forbidden entity type @entity_type.', array('@field_id' => $this->field->id, '@entity_type' => $this->entity_type)));
-    }
-
     // Ensure the field instance is unique within the bundle.
     if ($prior_instance = $instance_controller->load($this->id())) {
-      throw new FieldException(format_string('Attempt to create an instance of field @field_id on bundle @bundle that already has an instance of that field.', array('@field_id' => $this->field->id, '@bundle' => $this->bundle)));
+      throw new FieldException(format_string('Attempt to create an instance of field %name on bundle @bundle that already has an instance of that field.', array('%name' => $this->field->name, '@bundle' => $this->bundle)));
     }
 
     // Set the field UUID.
@@ -409,6 +405,9 @@ protected function saveUpdated() {
     // Ensure default values are present.
     $this->prepareSave();
 
+    // Notify the entity storage controller.
+    \Drupal::entityManager()->getStorageController($this->entity_type)->handleInstanceUpdate($this, $original);
+
     // Save the configuration.
     $result = parent::save();
     field_cache_clear();
@@ -436,7 +435,6 @@ protected function prepareSave() {
    */
   public function delete($field_cleanup = TRUE) {
     if (!$this->deleted) {
-      $module_handler = \Drupal::moduleHandler();
       $state = \Drupal::state();
 
       // Delete the configuration of this instance and save the configuration
@@ -450,16 +448,15 @@ public function delete($field_cleanup = TRUE) {
 
       parent::delete();
 
+      // Notify the entity storage controller.
+      \Drupal::entityManager()->getStorageController($this->entity_type)->handleInstanceDelete($this);
+
       // Clear the cache.
       field_cache_clear();
 
-      // Mark instance data for deletion by invoking
-      // hook_field_storage_delete_instance().
-      $module_handler->invoke($this->field->storage['module'], 'field_storage_delete_instance', array($this));
-
       // Remove the instance from the entity form displays.
       if ($form_display = entity_load('entity_form_display', $this->entity_type . '.' . $this->bundle . '.default')) {
-        $form_display->removeComponent($this->field->id())->save();
+        $form_display->removeComponent($this->field->name)->save();
       }
 
       // Remove the instance from the entity displays.
@@ -469,7 +466,7 @@ public function delete($field_cleanup = TRUE) {
         $ids[] = $this->entity_type . '.' . $this->bundle . '.' . $view_mode;
       }
       foreach (entity_load_multiple('entity_display', $ids) as $display) {
-        $display->removeComponent($this->field->id())->save();
+        $display->removeComponent($this->field->name)->save();
       }
 
       // Delete the field itself if we just deleted its last instance.
@@ -490,7 +487,7 @@ public function getField() {
    * {@inheritdoc}
    */
   public function getFieldName() {
-    return $this->field->id;
+    return $this->field->name;
   }
 
   /**
@@ -604,7 +601,7 @@ public function &offsetGet($offset) {
       return $this->field_uuid;
     }
     if ($offset == 'field_name') {
-      return $this->field->id;
+      return $this->field->name;
     }
     return $this->{$offset};
   }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigField.php b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigField.php
index 31e65ca..da703fa 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigField.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigField.php
@@ -42,8 +42,8 @@ public function validate() {
     $entity = $this->getParent();
     $langcode = $entity->language()->id;
 
-    if (isset($legacy_errors[$this->getInstance()->getField()->id()][$langcode])) {
-      foreach ($legacy_errors[$this->getInstance()->getField()->id()][$langcode] as $delta => $item_errors) {
+    if (isset($legacy_errors[$this->getInstance()->getField()->name][$langcode])) {
+      foreach ($legacy_errors[$this->getInstance()->getField()->name][$langcode] as $delta => $item_errors) {
         foreach ($item_errors as $item_error) {
           // We do not have the information about which column triggered the
           // error, so assume the first column...
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/argument/FieldList.php b/core/modules/field/lib/Drupal/field/Plugin/views/argument/FieldList.php
index d6ddcac..75b7252 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/argument/FieldList.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/argument/FieldList.php
@@ -35,7 +35,7 @@ class FieldList extends Numeric {
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     parent::init($view, $display, $options);
 
-    $field = field_info_field($this->definition['field_name']);
+    $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
     $this->allowed_values = options_allowed_values($field);
   }
 
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/argument/ListString.php b/core/modules/field/lib/Drupal/field/Plugin/views/argument/ListString.php
index 0c34d1b..83e24d9 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/argument/ListString.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/argument/ListString.php
@@ -35,7 +35,7 @@ class ListString extends String {
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     parent::init($view, $display, $options);
 
-    $field = field_info_field($this->definition['field_name']);
+    $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
     $this->allowed_values = options_allowed_values($field);
   }
 
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index fdfea10..a0e8f6e 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\field\Plugin\views\field;
 
-use Drupal\Core\Language\Language;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\Language;
 use Drupal\field\Plugin\Type\Formatter\FormatterPluginManager;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
@@ -111,7 +112,7 @@ public static function create(ContainerInterface $container, array $configuratio
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     parent::init($view, $display, $options);
 
-    $this->field_info = $field = field_info_field($this->definition['field_name']);
+    $this->field_info = $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
     $this->multiple = FALSE;
     $this->limit_values = FALSE;
 
@@ -286,7 +287,8 @@ public function clickSort($order) {
     }
 
     $this->ensureMyTable();
-    $column = _field_sql_storage_columnname($this->definition['field_name'], $this->options['click_sort_column']);
+    $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
+    $column = DatabaseStorageController::_fieldColumnName($field, $this->options['click_sort_column']);
     if (!isset($this->aliases[$column])) {
       // Column is not in query; add a sort on it (without adding the column).
       $this->aliases[$column] = $this->tableAlias . '.' . $column;
@@ -298,7 +300,7 @@ protected function defineOptions() {
     $options = parent::defineOptions();
 
     // defineOptions runs before init/construct, so no $this->field_info
-    $field = field_info_field($this->definition['field_name']);
+    $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
     $field_type = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field['type']);
     $column_names = array_keys($field['columns']);
     $default_column = '';
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/filter/FieldList.php b/core/modules/field/lib/Drupal/field/Plugin/views/filter/FieldList.php
index cfd2f6c..2b1d57c 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/filter/FieldList.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/filter/FieldList.php
@@ -20,7 +20,7 @@
 class FieldList extends ManyToOne {
 
   public function getValueOptions() {
-    $field = field_info_field($this->definition['field_name']);
+    $field = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
     $this->value_options = list_allowed_values($field);
   }
 
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php b/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
index 481105f..3103945 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
@@ -28,7 +28,7 @@ class EntityReverse extends RelationshipPluginBase  {
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     parent::init($view, $display, $options);
 
-    $this->field_info = field_info_field($this->definition['field_name']);
+    $this->field_info = field_info_field($this->definition['entity_type'], $this->definition['field_name']);
   }
 
   /**
diff --git a/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php b/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
index c74d426..7b021e2 100644
--- a/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
@@ -14,7 +14,7 @@ class ActiveTest extends FieldTestBase {
    *
    * @var array
    */
-  public static $modules = array('field_test');
+  public static $modules = array('field_test', 'entity_test');
 
   public static function getInfo() {
     return array(
@@ -28,62 +28,28 @@ public static function getInfo() {
    * Test that fields are properly marked active or inactive.
    */
   function testActive() {
-    $field_definition = array(
-      'field_name' => 'field_1',
+    $field_name = 'field_1';
+    entity_create('field_entity', array(
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
-      // For this test, we need a storage backend provided by a different
-      // module than field_test.module.
-      'storage' => array(
-        'type' => 'field_sql_storage',
-      ),
-    );
-    entity_create('field_entity', $field_definition)->save();
-
-    // Test disabling and enabling:
-    // - the field type module,
-    // - the storage module,
-    // - both.
-    $this->_testActiveHelper($field_definition, array('field_test'));
-    $this->_testActiveHelper($field_definition, array('field_sql_storage'));
-    $this->_testActiveHelper($field_definition, array('field_test', 'field_sql_storage'));
-  }
-
-  /**
-   * Helper function for testActive().
-   *
-   * Test dependency between a field and a set of modules.
-   *
-   * @param $field_definition
-   *   A field definition.
-   * @param $modules
-   *   An aray of module names. The field will be tested to be inactive as long
-   *   as any of those modules is disabled.
-   */
-  function _testActiveHelper($field_definition, $modules) {
-    $field_name = $field_definition['field_name'];
-
-    // Read the field.
-    $field = field_read_field($field_name);
-    $this->assertTrue($field_definition <= $field, 'The field was properly read.');
-
-    module_disable($modules, FALSE);
-
-    $fields = field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE));
-    $this->assertTrue(isset($fields[$field_name]) && $field_definition < $field, 'The field is properly read when explicitly fetching inactive fields.');
-
-    // Re-enable modules one by one, and check that the field is still inactive
-    // while some modules remain disabled.
-    while ($modules) {
-      $field = field_read_field($field_name);
-      $this->assertTrue(empty($field), format_string('%modules disabled. The field is marked inactive.', array('%modules' => implode(', ', $modules))));
-
-      $module = array_shift($modules);
-      module_enable(array($module), FALSE);
-    }
-
-    // Check that the field is active again after all modules have been
-    // enabled.
-    $field = field_read_field($field_name);
-    $this->assertTrue($field_definition <= $field, 'The field was was marked active.');
+    ))->save();
+
+    // Check that the field is correctly found.
+    $field = field_read_field('entity_test', $field_name);
+    $this->assertFalse(empty($field), 'The field was properly read.');
+
+    // Disable the module providing the field type, and check that the field is
+    // found only if explicitly requesting inactive fields.
+    module_disable(array('field_test'));
+    $field = field_read_field('entity_test', $field_name);
+    $this->assertTrue(empty($field), 'The field is marked inactive when the field type is absent.');
+    $field = field_read_field('entity_test', $field_name, array('include_inactive' => TRUE));
+    $this->assertFalse(empty($field), 'The field is properly read when explicitly fetching inactive fields.');
+
+    // Re-enable the module, and check that the field is active again.
+    module_enable(array('field_test'));
+    $field = field_read_field('entity_test', $field_name);
+    $this->assertFalse(empty($field), 'The field was was marked active.');
   }
 }
diff --git a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
index 9637826..8c66ab1 100644
--- a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
@@ -7,10 +7,10 @@
 
 namespace Drupal\field\Tests;
 
-use Drupal\field\Plugin\Core\Entity\FieldInstance;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\FieldInterface;
 
-use Drupal\Core\Language\Language;
 
 /**
  * Unit test class for field bulk delete and batch purge functionality.
@@ -50,7 +50,7 @@ class BulkDeleteTest extends FieldUnitTestBase {
    *
    * @var array
    */
-  protected $entity_type = 'test_entity';
+  protected $entity_type = 'entity_test';
 
   public static function getInfo() {
     return array(
@@ -114,14 +114,16 @@ function setUp() {
 
     // Create two fields.
     $field = entity_create('field_entity', array(
-      'field_name' => 'bf_1',
+      'name' => 'bf_1',
+      'entity_type' => $this->entity_type,
       'type' => 'test_field',
       'cardinality' => 1
     ));
     $field->save();
     $this->fields[] = $field;
     $field = entity_create('field_entity', array(
-      'field_name' => 'bf_2',
+      'name' => 'bf_2',
+      'entity_type' => $this->entity_type,
       'type' => 'test_field',
       'cardinality' => 4
     ));
@@ -130,11 +132,10 @@ function setUp() {
 
     // For each bundle, create an instance of each field, and 10
     // entities with values for each field.
-    $this->entity_type = 'entity_test';
     foreach ($this->bundles as $bundle) {
       foreach ($this->fields as $field) {
         entity_create('field_instance', array(
-          'field_name' => $field->id(),
+          'field_name' => $field->name,
           'entity_type' => $this->entity_type,
           'bundle' => $bundle,
         ))->save();
@@ -165,7 +166,7 @@ function setUp() {
   function testDeleteFieldInstance() {
     $bundle = reset($this->bundles);
     $field = reset($this->fields);
-    $field_name = $field->id();
+    $field_name = $field->name;
     $factory = \Drupal::service('entity.query');
 
     // There are 10 entities of this bundle.
@@ -175,7 +176,7 @@ function testDeleteFieldInstance() {
     $this->assertEqual(count($found), 10, 'Correct number of entities found before deleting');
 
     // Delete the instance.
-    $instance = field_info_instance($this->entity_type, $field->id(), $bundle);
+    $instance = field_info_instance($this->entity_type, $field->name, $bundle);
     $instance->delete();
 
     // The instance still exists, deleted.
@@ -198,19 +199,25 @@ function testDeleteFieldInstance() {
       ->condition("$field_name.deleted", 1)
       ->sort('id')
       ->execute();
-    $ids = (object) array(
-      'entity_type' => 'entity_test',
-      'bundle' => $bundle,
-    );
-    $entities = array();
-    foreach ($found as $entity_id) {
-      $ids->entity_id = $entity_id;
-      $entities[$entity_id] = _field_create_entity_from_ids($ids);
-    }
-    field_attach_load($this->entity_type, $entities, FIELD_LOAD_CURRENT, array('instance' => $instance));
     $this->assertEqual(count($found), 10, 'Correct number of entities found after deleting');
-    foreach ($entities as $id => $entity) {
-      $this->assertEqual($this->entities[$id]->{$field->id()}->value, $entity->{$field->id()}[Language::LANGCODE_NOT_SPECIFIED][0]['value'], "Entity $id with deleted data loaded correctly");
+    $this->assertFalse(array_diff($found, array_keys($this->entities)));
+    $this->checkFieldTableContents($field);
+  }
+
+  /**
+   * Test that the actual stored content didn't change during delete.
+   *
+   * @param FieldInterface $field
+   */
+  protected function checkFieldTableContents(FieldInterface $field) {
+    $schema = DatabaseStorageController::_fieldSqlSchema($field);
+    $table = DatabaseStorageController::_fieldTableName($field);
+    $column = DatabaseStorageController::_fieldColumnName($field, 'value');
+    $result = db_select($table, 't')
+      ->fields('t', array_keys($schema[$table]['fields']))
+      ->execute();
+    foreach ($result as $row) {
+      $this->assertEqual($this->entities[$row->entity_id]->{$field->name}->value, $row->$column);
     }
   }
 
@@ -226,7 +233,7 @@ function testPurgeInstance() {
     $field = reset($this->fields);
 
     // Delete the instance.
-    $instance = field_info_instance($this->entity_type, $field->id(), $bundle);
+    $instance = field_info_instance($this->entity_type, $field->name, $bundle);
     $instance->delete();
 
     // No field hooks were called.
@@ -241,19 +248,18 @@ function testPurgeInstance() {
       // There are $count deleted entities left.
       $found = \Drupal::entityQuery('entity_test')
         ->condition('type', $bundle)
-        ->condition($field->id() . '.deleted', 1)
+        ->condition($field->name . '.deleted', 1)
         ->execute();
       $this->assertEqual(count($found), $count, 'Correct number of entities found after purging 2');
     }
 
     // Check hooks invocations.
-    // hook_field_load() and hook_field_delete() should have been called once
-    // for each entity in the bundle.
+    // hook_field_delete() should have been called once for each entity in the
+    // bundle.
     $actual_hooks = field_test_memorize();
     $hooks = array();
     $entities = $this->entities_by_bundles[$bundle];
     foreach ($entities as $id => $entity) {
-      $hooks['field_test_field_load'][] = array($id => $entity);
       $hooks['field_test_field_delete'][] = $entity;
     }
     $this->checkHooksInvocations($hooks, $actual_hooks);
@@ -286,7 +292,7 @@ function testPurgeField() {
 
     // Delete the first instance.
     $bundle = reset($this->bundles);
-    $instance = field_info_instance($this->entity_type, $field->id(), $bundle);
+    $instance = field_info_instance($this->entity_type, $field->name, $bundle);
     $instance->delete();
 
     // Assert that hook_field_delete() was not called yet.
@@ -297,13 +303,12 @@ function testPurgeField() {
     field_purge_batch(10);
 
     // Check hooks invocations.
-    // hook_field_load() and hook_field_delete() should have been called once
-    // for each entity in the bundle.
+    // hook_field_delete() should have been called once for each entity in the
+    // bundle.
     $actual_hooks = field_test_memorize();
     $hooks = array();
     $entities = $this->entities_by_bundles[$bundle];
     foreach ($entities as $id => $entity) {
-      $hooks['field_test_field_load'][] = array($id => $entity);
       $hooks['field_test_field_delete'][] = $entity;
     }
     $this->checkHooksInvocations($hooks, $actual_hooks);
@@ -317,7 +322,7 @@ function testPurgeField() {
 
     // Delete the second instance.
     $bundle = next($this->bundles);
-    $instance = field_info_instance($this->entity_type, $field->id(), $bundle);
+    $instance = field_info_instance($this->entity_type, $field->name, $bundle);
     $instance->delete();
 
     // Assert that hook_field_delete() was not called yet.
@@ -332,7 +337,6 @@ function testPurgeField() {
     $hooks = array();
     $entities = $this->entities_by_bundles[$bundle];
     foreach ($entities as $id => $entity) {
-      $hooks['field_test_field_load'][] = array($id => $entity);
       $hooks['field_test_field_delete'][] = $entity;
     }
     $this->checkHooksInvocations($hooks, $actual_hooks);
diff --git a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
index 8e16c3e..fd9c189 100644
--- a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
@@ -37,14 +37,15 @@ public static function getInfo() {
    */
   function testCreateField() {
     $field_definition = array(
-      'field_name' => 'field_2',
+      'name' => 'field_2',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     );
     field_test_memorize();
     $field = entity_create('field_entity', $field_definition);
     $field->save();
     $mem = field_test_memorize();
-    $this->assertIdentical($mem['field_test_field_entity_create'][0][0]['field_name'], $field_definition['field_name'], 'hook_entity_create() called with correct arguments.');
+    $this->assertIdentical($mem['field_test_field_entity_create'][0][0]['field_name'], $field_definition['name'], 'hook_entity_create() called with correct arguments.');
     $this->assertIdentical($mem['field_test_field_entity_create'][0][0]['type'], $field_definition['type'], 'hook_entity_create() called with correct arguments.');
 
     // Read the configuration. Check against raw configuration data rather than
@@ -53,7 +54,9 @@ function testCreateField() {
     $field_config = \Drupal::config('field.field.' . $field->id())->get();
 
     // Ensure that basic properties are preserved.
-    $this->assertEqual($field_config['id'], $field_definition['field_name'], 'The field name is properly saved.');
+    $this->assertEqual($field_config['name'], $field_definition['name'], 'The field name is properly saved.');
+    $this->assertEqual($field_config['entity_type'], $field_definition['entity_type'], 'The field entity type is properly saved.');
+    $this->assertEqual($field_config['id'], $field_definition['entity_type'] . '.' . $field_definition['name'], 'The field id is properly saved.');
     $this->assertEqual($field_config['type'], $field_definition['type'], 'The field type is properly saved.');
 
     // Ensure that cardinality defaults to 1.
@@ -63,9 +66,6 @@ function testCreateField() {
     $field_type = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field_definition['type']);
     $this->assertEqual($field_config['settings'], $field_type['settings'], 'Default field settings have been written.');
 
-    // Ensure that default storage was set.
-    $this->assertEqual($field_config['storage']['type'], \Drupal::config('field.settings')->get('default_storage'), 'The field type is properly saved.');
-
     // Guarantee that the name is unique.
     try {
       entity_create('field_entity', $field_definition)->save();
@@ -78,7 +78,8 @@ function testCreateField() {
     // Check that field type is required.
     try {
       $field_definition = array(
-        'field_name' => 'field_1',
+        'name' => 'field_1',
+        'entity_type' => 'entity_type',
       );
       entity_create('field_entity', $field_definition)->save();
       $this->fail(t('Cannot create a field with no type.'));
@@ -90,7 +91,8 @@ function testCreateField() {
     // Check that field name is required.
     try {
       $field_definition = array(
-        'type' => 'test_field'
+        'type' => 'test_field',
+        'entity_type' => 'entity_test',
       );
       entity_create('field_entity', $field_definition)->save();
       $this->fail(t('Cannot create an unnamed field.'));
@@ -98,11 +100,24 @@ function testCreateField() {
     catch (FieldException $e) {
       $this->pass(t('Cannot create an unnamed field.'));
     }
+    // Check that entity type is required.
+    try {
+      $field_definition = array(
+        'name' => 'test_field',
+        'type' => 'test_field'
+      );
+      entity_create('field_entity', $field_definition)->save();
+      $this->fail('Cannot create a field without an entity type.');
+    }
+    catch (FieldException $e) {
+      $this->pass('Cannot create a field without an entity type.');
+    }
 
     // Check that field name must start with a letter or _.
     try {
       $field_definition = array(
-        'field_name' => '2field_2',
+        'name' => '2field_2',
+        'entity_type' => 'entity_test',
         'type' => 'test_field',
       );
       entity_create('field_entity', $field_definition)->save();
@@ -115,7 +130,8 @@ function testCreateField() {
     // Check that field name must only contain lowercase alphanumeric or _.
     try {
       $field_definition = array(
-        'field_name' => 'field#_3',
+        'name' => 'field#_3',
+        'entity_type' => 'entity_test',
         'type' => 'test_field',
       );
       entity_create('field_entity', $field_definition)->save();
@@ -128,7 +144,8 @@ function testCreateField() {
     // Check that field name cannot be longer than 32 characters long.
     try {
       $field_definition = array(
-        'field_name' => '_12345678901234567890123456789012',
+        'name' => '_12345678901234567890123456789012',
+        'entity_type' => 'entity_test',
         'type' => 'test_field',
       );
       entity_create('field_entity', $field_definition)->save();
@@ -143,7 +160,8 @@ function testCreateField() {
     try {
       $field_definition = array(
         'type' => 'test_field',
-        'field_name' => 'id',
+        'name' => 'id',
+        'entity_type' => 'entity_test',
       );
       entity_create('field_entity', $field_definition)->save();
       $this->fail(t('Cannot create a field bearing the name of an entity key.'));
@@ -161,7 +179,8 @@ function testCreateField() {
    */
   function testCreateFieldWithExplicitSchema() {
     $field_definition = array(
-      'field_name' => 'field_2',
+      'name' => 'field_2',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
       'schema' => array(
         'dummy' => 'foobar'
@@ -172,68 +191,32 @@ function testCreateFieldWithExplicitSchema() {
   }
 
   /**
-   * Test failure to create a field.
-   */
-  function testCreateFieldFail() {
-    $field_name = 'duplicate';
-    $field_definition = array('field_name' => $field_name, 'type' => 'test_field', 'storage' => array('type' => 'field_test_storage_failure'));
-    $field = entity_load('field_entity', $field_name);
-
-    // The field does not exist.
-    $this->assertFalse($field, 'The field does not exist.');
-
-    // Try to create the field.
-    try {
-      entity_create('field_entity', $field_definition)->save();
-      $this->assertTrue(FALSE, 'Field creation (correctly) fails.');
-    }
-    catch (\Exception $e) {
-      $this->assertTrue(TRUE, 'Field creation (correctly) fails.');
-    }
-
-    // The field does not exist.
-    $field = entity_load('field_entity', $field_name);
-    $this->assertFalse($field, 'The field does not exist.');
-  }
-
-  /**
-   * Tests reading a single field definition.
-   */
-  function testReadField() {
-    $field_definition = array(
-      'field_name' => 'field_1',
-      'type' => 'test_field',
-    );
-    entity_create('field_entity', $field_definition)->save();
-
-    // Read the field back.
-    $field = field_read_field($field_definition['field_name']);
-    $this->assertTrue($field_definition < $field, 'The field was properly read.');
-  }
-
-  /**
    * Tests reading field definitions.
    */
   function testReadFields() {
     $field_definition = array(
-      'field_name' => 'field_1',
+      'name' => 'field_1',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     );
-    entity_create('field_entity', $field_definition)->save();
+    $field = entity_create('field_entity', $field_definition);
+    $field->save();
+    $id = $field->id();
+    field_cache_clear();
 
     // Check that 'single column' criteria works.
-    $fields = field_read_fields(array('field_name' => $field_definition['field_name']));
-    $this->assertTrue(count($fields) == 1 && isset($fields[$field_definition['field_name']]), 'The field was properly read.');
+    $fields = field_read_fields(array('id' => $id));
+    $this->assertTrue(count($fields) == 1 && isset($fields[$id]), 'The field was properly read.');
 
     // Check that 'multi column' criteria works.
-    $fields = field_read_fields(array('field_name' => $field_definition['field_name'], 'type' => $field_definition['type']));
-    $this->assertTrue(count($fields) == 1 && isset($fields[$field_definition['field_name']]), 'The field was properly read.');
-    $fields = field_read_fields(array('field_name' => $field_definition['field_name'], 'type' => 'foo'));
+    $fields = field_read_fields(array('id' => $id, 'type' => $field_definition['type']));
+    $this->assertTrue(count($fields) == 1 && isset($fields[$id]), 'The field was properly read.');
+    $fields = field_read_fields(array('name' => $field_definition['name'], 'type' => 'foo'));
     $this->assertTrue(empty($fields), 'No field was found.');
 
     // Create an instance of the field.
     $instance_definition = array(
-      'field_name' => $field_definition['field_name'],
+      'field_name' => $field_definition['name'],
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     );
@@ -246,11 +229,14 @@ function testReadFields() {
   function testFieldIndexes() {
     // Check that indexes specified by the field type are used by default.
     $field_definition = array(
-      'field_name' => 'field_1',
+      'name' => 'field_1',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     );
-    entity_create('field_entity', $field_definition)->save();
-    $field = field_read_field($field_definition['field_name']);
+    $field = entity_create('field_entity', $field_definition);
+    $field->save();
+    field_cache_clear();
+    $field = entity_load('field_entity', $field->id());
     $schema = $field->getSchema();
     $expected_indexes = array('value' => array('value'));
     $this->assertEqual($schema['indexes'], $expected_indexes, 'Field type indexes saved by default');
@@ -258,14 +244,17 @@ function testFieldIndexes() {
     // Check that indexes specified by the field definition override the field
     // type indexes.
     $field_definition = array(
-      'field_name' => 'field_2',
+      'name' => 'field_2',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
       'indexes' => array(
         'value' => array(),
       ),
     );
-    entity_create('field_entity', $field_definition)->save();
-    $field = field_read_field($field_definition['field_name']);
+    $field = entity_create('field_entity', $field_definition);
+    $field->save();
+    field_cache_clear();
+    $field = entity_load('field_entity', $field->id());
     $schema = $field->getSchema();
     $expected_indexes = array('value' => array());
     $this->assertEqual($schema['indexes'], $expected_indexes, 'Field definition indexes override field type indexes');
@@ -273,14 +262,18 @@ function testFieldIndexes() {
     // Check that indexes specified by the field definition add to the field
     // type indexes.
     $field_definition = array(
-      'field_name' => 'field_3',
+      'name' => 'field_3',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
       'indexes' => array(
         'value_2' => array('value'),
       ),
     );
-    entity_create('field_entity', $field_definition)->save();
-    $field = field_read_field($field_definition['field_name']);
+    $field = entity_create('field_entity', $field_definition);
+    $field->save();
+    $id = $field->id();
+    field_cache_clear();
+    $field = entity_load('field_entity', $id);
     $schema = $field->getSchema();
     $expected_indexes = array('value' => array('value'), 'value_2' => array('value'));
     $this->assertEqual($schema['indexes'], $expected_indexes, 'Field definition indexes are merged with field type indexes');
@@ -293,30 +286,38 @@ function testDeleteField() {
     // TODO: Also test deletion of the data stored in the field ?
 
     // Create two fields (so we can test that only one is deleted).
-    $this->field = array('field_name' => 'field_1', 'type' => 'test_field');
+    $this->field = array(
+      'name' => 'field_1',
+      'type' => 'test_field',
+      'entity_type' => 'entity_test',
+    );
     entity_create('field_entity', $this->field)->save();
-    $this->another_field = array('field_name' => 'field_2', 'type' => 'test_field');
+    $this->another_field = array(
+      'name' => 'field_2',
+      'type' => 'test_field',
+      'entity_type' => 'entity_test',
+    );
     entity_create('field_entity', $this->another_field)->save();
 
     // Create instances for each.
     $this->instance_definition = array(
-      'field_name' => $this->field['field_name'],
+      'field_name' => $this->field['name'],
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     );
     entity_create('field_instance', $this->instance_definition)->save();
     $another_instance_definition = $this->instance_definition;
-    $another_instance_definition['field_name'] = $this->another_field['field_name'];
+    $another_instance_definition['field_name'] = $this->another_field['name'];
     entity_create('field_instance', $another_instance_definition)->save();
 
     // Test that the first field is not deleted, and then delete it.
-    $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
+    $field = field_read_field('entity_test', $this->field['name'], array('include_deleted' => TRUE));
     $this->assertTrue(!empty($field) && empty($field['deleted']), 'A new field is not marked for deletion.');
-    field_info_field($this->field['field_name'])->delete();
+    field_info_field('entity_test', $this->field['name'])->delete();
 
     // Make sure that the field is marked as deleted when it is specifically
     // loaded.
-    $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE));
+    $field = field_read_field('entity_test', $this->field['name'], array('include_deleted' => TRUE));
     $this->assertTrue(!empty($field['deleted']), 'A deleted field is marked for deletion.');
 
     // Make sure that this field's instance is marked as deleted when it is
@@ -325,7 +326,7 @@ function testDeleteField() {
     $this->assertTrue(!empty($instance['deleted']), 'An instance for a deleted field is marked for deletion.');
 
     // Try to load the field normally and make sure it does not show up.
-    $field = field_read_field($this->field['field_name']);
+    $field = field_read_field('entity_test', $this->field['name']);
     $this->assertTrue(empty($field), 'A deleted field is not loaded by default.');
 
     // Try to load the instance normally and make sure it does not show up.
@@ -333,7 +334,7 @@ function testDeleteField() {
     $this->assertTrue(empty($instance), 'An instance for a deleted field is not loaded by default.');
 
     // Make sure the other field (and its field instance) are not deleted.
-    $another_field = field_read_field($this->another_field['field_name']);
+    $another_field = field_read_field('entity_test', $this->another_field['name']);
     $this->assertTrue(!empty($another_field) && empty($another_field['deleted']), 'A non-deleted field is not marked for deletion.');
     $another_instance = field_read_instance('entity_test', $another_instance_definition['field_name'], $another_instance_definition['bundle']);
     $this->assertTrue(!empty($another_instance) && empty($another_instance['deleted']), 'An instance of a non-deleted field is not marked for deletion.');
@@ -342,21 +343,18 @@ function testDeleteField() {
     // write data into it.
     entity_create('field_entity', $this->field)->save();
     entity_create('field_instance', $this->instance_definition)->save();
-    $field = field_read_field($this->field['field_name']);
+    $field = field_read_field('entity_test', $this->field['name']);
     $this->assertTrue(!empty($field) && empty($field['deleted']), 'A new field with a previously used name is created.');
     $instance = field_read_instance('entity_test', $this->instance_definition['field_name'], $this->instance_definition['bundle']);
     $this->assertTrue(!empty($instance) && empty($instance['deleted']), 'A new instance for a previously used field name is created.');
 
     // Save an entity with data for the field
-    $entity = entity_create('entity_test', array('id' => 0, 'revision_id' => 0));
-    $langcode = Language::LANGCODE_NOT_SPECIFIED;
+    $entity = entity_create('entity_test', array());
     $values[0]['value'] = mt_rand(1, 127);
     $entity->{$field['field_name']}->value = $values[0]['value'];
-    field_attach_insert($entity);
+    $entity = $this->entitySaveReload($entity);
 
     // Verify the field is present on load
-    $entity = entity_create('entity_test', array('id' => 0, 'revision_id' => 0));
-    field_attach_load('entity_test', array(0 => $entity));
     $this->assertIdentical(count($entity->{$field['field_name']}), count($values), "Data in previously deleted field saves and loads correctly");
     foreach ($values as $delta => $value) {
       $this->assertEqual($entity->{$field['field_name']}[$delta]->value, $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
@@ -364,7 +362,11 @@ function testDeleteField() {
   }
 
   function testUpdateFieldType() {
-    $field_definition = array('field_name' => 'field_type', 'type' => 'number_decimal');
+    $field_definition = array(
+      'name' => 'field_type',
+      'entity_type' => 'entity_test',
+      'type' => 'number_decimal',
+    );
     $field = entity_create('field_entity', $field_definition);
     $field->save();
 
@@ -387,7 +389,8 @@ function testUpdateField() {
     // systems, it makes a good test case.
     $cardinality = 4;
     $field = entity_create('field_entity', array(
-      'field_name' => 'field_update',
+      'name' => 'field_update',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
       'cardinality' => $cardinality,
     ));
@@ -400,22 +403,18 @@ function testUpdateField() {
     $instance->save();
 
     do {
-      // We need a unique ID for our entity. $cardinality will do.
-      $id = $cardinality;
-      $entity = entity_create('entity_test', array('id' => $id, 'revision_id' => $id));
+      $entity = entity_create('entity_test', array());
       // Fill in the entity with more values than $cardinality.
       for ($i = 0; $i < 20; $i++) {
-        $entity->field_update[$i]->value = $i;
+        // We can not use $i here because 0 values are filtered out.
+        $entity->field_update[$i]->value = $i + 1;
       }
-      // Save the entity.
-      field_attach_insert($entity);
       // Load back and assert there are $cardinality number of values.
-      $entity = entity_create('entity_test', array('id' => $id, 'revision_id' => $id));
-      field_attach_load('entity_test', array($id => $entity));
-      $this->assertEqual(count($entity->field_update), $field->cardinality, 'Cardinality is kept');
+      $entity = $this->entitySaveReload($entity);
+      $this->assertEqual(count($entity->field_update), $field->cardinality);
       // Now check the values themselves.
       for ($delta = 0; $delta < $cardinality; $delta++) {
-        $this->assertEqual($entity->field_update[$delta]->value, $delta, 'Value is kept');
+        $this->assertEqual($entity->field_update[$delta]->value, $delta + 1);
       }
       // Increase $cardinality and set the field cardinality to the new value.
       $field->cardinality = ++$cardinality;
@@ -428,7 +427,8 @@ function testUpdateField() {
    */
   function testUpdateFieldForbid() {
     $field = entity_create('field_entity', array(
-      'field_name' => 'forbidden',
+      'name' => 'forbidden',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
       'settings' => array(
         'changeable' => 0,
diff --git a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php
index e2fb2f9..3bfc7d2 100644
--- a/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/DisplayApiTest.php
@@ -70,7 +70,8 @@ function setUp() {
     $this->cardinality = 4;
 
     $field = array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
       'cardinality' => $this->cardinality,
     );
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php
index 16fabe5..eb115dc 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php
@@ -52,12 +52,13 @@ function setUp() {
     $content_type = $content_type_info->type;
 
     $field = array(
-      'field_name' => 'test_view_field',
+      'name' => 'test_view_field',
+      'entity_type' => 'node',
       'type' => 'text',
     );
     entity_create('field_entity', $field)->save();
     $instance = array(
-      'field_name' => $field['field_name'],
+      'field_name' => $field['name'],
       'entity_type' => 'node',
       'bundle' => $content_type,
     );
@@ -66,7 +67,7 @@ function setUp() {
     // Assign display properties for the 'default' and 'teaser' view modes.
     foreach (array('default', 'teaser') as $view_mode) {
       entity_get_display('node', $content_type, $view_mode)
-        ->setComponent($field['field_name'])
+        ->setComponent($field['name'])
         ->save();
     }
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
index 4739d5f..03490a5 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
@@ -236,7 +236,7 @@ function testFieldAttachPrepareViewMultiple() {
    */
   function testFieldAttachCache() {
     // Initialize random values and a test entity.
-    $entity_init = entity_create('entity_test', array('id' => 1, 'revision_id' => 1, 'type' => $this->instance['bundle']));
+    $entity_init = entity_create('entity_test', array('type' => $this->instance['bundle']));
     $langcode = Language::LANGCODE_NOT_SPECIFIED;
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
 
@@ -250,87 +250,72 @@ function testFieldAttachCache() {
     // Save, and check that no cache entry is present.
     $entity = clone($entity_init);
     $entity->{$this->field_name}->setValue($values);
-    field_attach_insert($entity);
-    $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on insert');
-
-    // Load, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
-    $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on load');
-
+    $entity = $this->entitySaveReload($entity);
+    $cid = "field:$entity_type:" . $entity->id();
+    $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on insert and load');
 
     // Cacheable entity type.
     $entity_type = 'entity_test_cache';
-    $cid = "field:$entity_type:" . $entity_init->id();
-    $instance_definition = $this->instance_definition;
-    $instance_definition['entity_type'] = $entity_type;
-    $instance_definition['bundle'] = $entity_type;
-    entity_create('field_instance', $instance_definition)->save();
-
+    $this->createFieldWithInstance('_2', 'entity_test_cache');
     entity_info_cache_clear();
 
     $entity_init = entity_create($entity_type, array(
-      'id' => 1,
-      'revision_id' => 1,
       'type' => $entity_type,
     ));
 
     // Check that no initial cache entry is present.
+    $cid = "field:$entity_type:" . $entity->id();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no initial cache entry');
 
     // Save, and check that no cache entry is present.
     $entity = clone($entity_init);
-    $entity->{$this->field_name} = $values;
-    field_attach_insert($entity);
+    $entity->{$this->field_name_2} = $values;
+    $entity->save();
+    $cid = "field:$entity_type:" . $entity->id();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on insert');
 
-    // Load a single field, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    $instance = field_info_instance($entity->entityType(), $this->field_name, $entity->bundle());
-    field_attach_load($entity_type, array($entity->id() => $entity), FIELD_LOAD_CURRENT, array('instance' => $instance));
-    $cache = cache('field')->get($cid);
-    $this->assertFalse($cache, 'Cached: no cache entry on loading a single field');
-
     // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+    $controller->resetCache();
+    $controller->load($entity->id());
     $cache = cache('field')->get($cid);
-    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+    $this->assertEqual($cache->data[$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Update with different values, and check that the cache entry is wiped.
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity = clone($entity_init);
-    $entity->{$this->field_name} = $values;
-    field_attach_update($entity);
+    $values = $this->_generateTestFieldValues($this->field_name_2['cardinality']);
+    $entity = entity_create($entity_type, array(
+      'type' => $entity_type,
+      'id' => $entity->id(),
+    ));
+    $entity->{$this->field_name_2} = $values;
+    $entity->save();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on update');
 
     // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $controller->resetCache();
+    $controller->load($entity->id());
     $cache = cache('field')->get($cid);
-    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+    $this->assertEqual($cache->data[$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Create a new revision, and check that the cache entry is wiped.
-    $entity_init = entity_create($entity_type, array(
-      'id' => 1,
-      'revision_id' => 2,
+    $entity = entity_create($entity_type, array(
       'type' => $entity_type,
+      'id' => $entity->id(),
     ));
-    $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity = clone($entity_init);
+    $values = $this->_generateTestFieldValues($this->field_name_2['cardinality']);
     $entity->{$this->field_name} = $values;
-    field_attach_update($entity);
-    $cache = cache('field')->get($cid);
+    $entity->setNewRevision();
+    $entity->save();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on new revision creation');
 
     // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $controller->resetCache();
+    $controller->load($entity->id());
     $cache = cache('field')->get($cid);
-    $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
+    $this->assertEqual($cache->data[$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Delete, and check that the cache entry is wiped.
-    field_attach_delete($entity);
+    $entity->delete();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry after delete');
   }
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
index e5f28d0..1bd585a 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
@@ -62,26 +62,22 @@ function testFieldAttachSaveLoad() {
     // TODO : test empty values filtering and "compression" (store consecutive deltas).
     // Preparation: create three revisions and store them in $revision array.
     $values = array();
+    $entity = entity_create($entity_type, array());
     for ($revision_id = 0; $revision_id < 3; $revision_id++) {
-      $revision[$revision_id] = entity_create($entity_type, array('id' => 0, 'revision_id' => $revision_id));
       // Note: we try to insert one extra value.
-      $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
-      $current_revision = $revision_id;
-      // If this is the first revision do an insert.
-      if (!$revision_id) {
-        $revision[$revision_id]->{$this->field_name}->setValue($values[$revision_id]);
-        field_attach_insert($revision[$revision_id]);
-      }
-      else {
-        // Otherwise do an update.
-        $revision[$revision_id]->{$this->field_name}->setValue($values[$revision_id]);
-        field_attach_update($revision[$revision_id]);
-      }
+      $current_values = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
+      $entity->{$this->field_name}->setValue($current_values);
+      $entity->setNewRevision();
+      $entity->save();
+      $entity_id = $entity->id();
+      $current_revision = $entity->getRevisionId();
+      $values[$current_revision] = $current_values;
     }
 
+    $storage_controller =  $this->container->get('plugin.manager.entity')->getStorageController($entity_type);
+    $storage_controller->resetCache();
+    $entity = $storage_controller->load($entity_id);
     // Confirm current revision loads the correct data.
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0));
-    field_attach_load($entity_type, array(0 => $entity));
     // Number of values per field loaded equals the field cardinality.
     $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], 'Current revision: expected number of values');
     for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
@@ -92,9 +88,8 @@ function testFieldAttachSaveLoad() {
     }
 
     // Confirm each revision loads the correct data.
-    foreach (array_keys($revision) as $revision_id) {
-      $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => $revision_id));
-      field_attach_load_revision($entity_type, array(0 => $entity));
+    foreach (array_keys($values) as $revision_id) {
+      $entity = $storage_controller->loadRevision($revision_id);
       // Number of values per field loaded equals the field cardinality.
       $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], format_string('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
       for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
@@ -130,7 +125,11 @@ function testFieldAttachLoadMultiple() {
     );
     for ($i = 1; $i <= 3; $i++) {
       $field_names[$i] = 'field_' . $i;
-      $field = entity_create('field_entity', array('field_name' => $field_names[$i], 'type' => 'test_field'));
+      $field = entity_create('field_entity', array(
+        'name' => $field_names[$i],
+        'entity_type' => $entity_type,
+        'type' => 'test_field',
+      ));
       $field->save();
       $field_ids[$i] = $field['uuid'];
       foreach ($field_bundles_map[$i] as $bundle) {
@@ -156,11 +155,14 @@ function testFieldAttachLoadMultiple() {
         $values[$index][$field_name] = mt_rand(1, 127);
         $entity->$field_name->setValue(array('value' => $values[$index][$field_name]));
       }
-      field_attach_insert($entity);
+      $entity->enforceIsnew();
+      $entity->save();
     }
 
     // Check that a single load correctly loads field values for both entities.
-    field_attach_load($entity_type, $entities);
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+    $controller->resetCache();
+    $entities = $controller->loadMultiple();
     foreach ($entities as $index => $entity) {
       $instances = field_info_instances($entity_type, $bundles[$index]);
       foreach ($instances as $field_name => $instance) {
@@ -170,165 +172,48 @@ function testFieldAttachLoadMultiple() {
         $this->assertEqual($entity->{$field_name}->additional_key, 'additional_value', format_string('Entity %index: extra information was found', array('%index' => $index)));
       }
     }
-
-    // Check that the single-field load option works.
-    $entity = entity_create($entity_type, array('id' => 1, 'revision_id' => 1, 'type' => $bundles[1]));
-    $instance = field_info_instance($entity->entityType(), $field_names[1], $entity->bundle());
-    field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('instance' => $instance));
-    $this->assertEqual($entity->{$field_names[1]}->value, $values[1][$field_names[1]], format_string('Entity %index: expected value was found.', array('%index' => 1)));
-    $this->assertEqual($entity->{$field_names[1]}->additional_key, 'additional_value', format_string('Entity %index: extra information was found', array('%index' => 1)));
-    $this->assert(empty($entity->{$field_names[2]}->value), format_string('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
-    $this->assert(!isset($entity->{$field_names[3]}), format_string('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
-  }
-
-  /**
-   * Test saving and loading fields using different storage backends.
-   */
-  function testFieldAttachSaveLoadDifferentStorage() {
-    $entity_type = 'entity_test';
-
-    // Create two fields using different storage backends, and their instances.
-    $fields = array(
-      array(
-        'field_name' => 'field_1',
-        'type' => 'test_field',
-        'cardinality' => 4,
-        'storage' => array('type' => 'field_sql_storage')
-      ),
-      array(
-        'field_name' => 'field_2',
-        'type' => 'test_field',
-        'cardinality' => 4,
-        'storage' => array('type' => 'field_test_storage')
-      ),
-    );
-    foreach ($fields as $field) {
-      entity_create('field_entity', $field)->save();
-      $instance = array(
-        'field_name' => $field['field_name'],
-        'entity_type' => $entity_type,
-        'bundle' => $entity_type,
-      );
-      entity_create('field_instance', $instance)->save();
-    }
-
-    $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
-
-    // Create entity and insert random values.
-    $entity = clone($entity_init);
-    $values = array();
-    foreach ($fields as $field) {
-      $values[$field['field_name']] = $this->_generateTestFieldValues($this->field['cardinality']);
-      $entity->{$field['field_name']} = $values[$field['field_name']];
-    }
-    field_attach_insert($entity);
-
-    // Check that values are loaded as expected.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
-    foreach ($fields as $field) {
-      $this->assertEqual($values[$field['field_name']], $entity->{$field['field_name']}->getValue(), format_string('%storage storage: expected values were found.', array('%storage' => $field['storage']['type'])));
-    }
-  }
-
-  /**
-   * Test storage details alteration.
-   *
-   * @see field_test_storage_details_alter()
-   */
-  function testFieldStorageDetailsAlter() {
-    $field_name = 'field_test_change_my_details';
-    $field = entity_create('field_entity', array(
-      'field_name' => $field_name,
-      'type' => 'test_field',
-      'cardinality' => 4,
-      'storage' => array('type' => 'field_test_storage'),
-    ));
-    $field->save();
-    $instance = entity_create('field_instance', array(
-      'field_name' => $field_name,
-      'entity_type' => 'entity_test',
-      'bundle' => 'entity_test',
-    ));
-    $instance->save();
-
-    // The storage details are indexed by a storage engine type.
-    $this->assertTrue(array_key_exists('drupal_variables', $field['storage_details']), 'The storage type is Drupal variables.');
-
-    $details = $field['storage_details']['drupal_variables'];
-
-    // The field_test storage details are indexed by variable name. The details
-    // are altered, so moon and mars are correct for this test.
-    $this->assertTrue(array_key_exists('moon', $details[FIELD_LOAD_CURRENT]), 'Moon is available in the instance array.');
-    $this->assertTrue(array_key_exists('mars', $details[FIELD_LOAD_REVISION]), 'Mars is available in the instance array.');
-
-    // Test current and revision storage details together because the columns
-    // are the same.
-    foreach ($field['columns'] as $column_name => $attributes) {
-      $this->assertEqual($details[FIELD_LOAD_CURRENT]['moon'][$column_name], $column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'moon[FIELD_LOAD_CURRENT]')));
-      $this->assertEqual($details[FIELD_LOAD_REVISION]['mars'][$column_name], $column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'mars[FIELD_LOAD_REVISION]')));
-    }
   }
 
   /**
    * Tests insert and update with empty or NULL fields.
    */
   function testFieldAttachSaveEmptyData() {
-    $entity_type = 'entity_test_rev';
+    $entity_type = 'entity_test';
     $this->createFieldWithInstance('', $entity_type);
 
-    $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
+    $entity_init = entity_create($entity_type, array('id' => 1));
 
     // Insert: Field is NULL.
-    field_cache_clear();
-    $entity = clone($entity_init);
+    $entity = clone $entity_init;
     $entity->{$this->field_name} = NULL;
-    field_attach_insert($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity->enforceIsNew();
+    $entity = $this->entitySaveReload($entity);
     $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
 
     // Add some real data.
-    field_cache_clear();
     $entity = clone($entity_init);
     $values = $this->_generateTestFieldValues(1);
     $entity->{$this->field_name} = $values;
-    field_attach_insert($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertEqual($entity->{$this->field_name}->getValue(), $values, 'Field data saved');
 
     // Update: Field is NULL. Data should be wiped.
-    field_cache_clear();
     $entity = clone($entity_init);
     $entity->{$this->field_name} = NULL;
-    field_attach_update($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Update: NULL field removes existing values');
 
     // Re-add some data.
-    field_cache_clear();
     $entity = clone($entity_init);
     $values = $this->_generateTestFieldValues(1);
     $entity->{$this->field_name} = $values;
-    field_attach_update($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertEqual($entity->{$this->field_name}->getValue(), $values, 'Field data saved');
 
     // Update: Field is empty array. Data should be wiped.
-    field_cache_clear();
     $entity = clone($entity_init);
     $entity->{$this->field_name} = array();
-    field_attach_update($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Update: empty array removes existing values');
   }
 
@@ -351,11 +236,8 @@ function testFieldAttachSaveEmptyDataDefaultValue() {
     // Insert: Field is NULL.
     $entity = clone($entity_init);
     $entity->getBCEntity()->{$this->field_name} = NULL;
-    field_attach_insert($entity);
-
-    $entity = clone($entity_init);
-    $entity->getBCEntity()->{$this->field_name} = array();
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity->enforceIsNew();
+    $entity = $this->entitySaveReload($entity);
     $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
 
     // Verify that prepopulated field values are not overwritten by defaults.
@@ -370,53 +252,55 @@ function testFieldAttachSaveEmptyDataDefaultValue() {
   function testFieldAttachDelete() {
     $entity_type = 'entity_test_rev';
     $this->createFieldWithInstance('', $entity_type);
-    $rev[0] = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+    $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
+    $vids = array();
 
     // Create revision 0
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $rev[0]->{$this->field_name} = $values;
-    field_attach_insert($rev[0]);
+    $entity->{$this->field_name} = $values;
+    $entity->save();
+    $vids[] = $entity->getRevisionId();
 
     // Create revision 1
-    $rev[1] = entity_create($entity_type, array('id' => 0, 'revision_id' => 1, 'type' => $this->instance['bundle']));
-    $rev[1]->{$this->field_name} = $values;
-    field_attach_update($rev[1]);
+    $entity->setNewRevision();
+    $entity->save();
+    $vids[] = $entity->getRevisionId();
 
     // Create revision 2
-    $rev[2] = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
-    $rev[2]->{$this->field_name} = $values;
-    field_attach_update($rev[2]);
+    $entity->setNewRevision();
+    $entity->save();
+    $vids[] = $entity->getRevisionId();
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+    $controller->resetCache();
 
     // Confirm each revision loads
-    foreach (array_keys($rev) as $vid) {
-      $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+    foreach ($vids as $vid) {
+      $revision = $controller->loadRevision($vid);
+      $this->assertEqual(count($revision->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
     }
 
     // Delete revision 1, confirm the other two still load.
-    field_attach_delete_revision($rev[1]);
-    foreach (array(0, 2) as $vid) {
-      $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+    $controller->deleteRevision($vids[1]);
+    $controller->resetCache();
+    foreach (array(0, 2) as $key) {
+      $vid = $vids[$key];
+      $revision = $controller->loadRevision($vid);
+      $this->assertEqual(count($revision->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
     }
 
     // Confirm the current revision still loads
-    $read = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $read));
-    $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
+    $controller->resetCache();
+    $current = $controller->load($entity->id());
+    $this->assertEqual(count($current->{$this->field_name}), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
 
     // Delete all field data, confirm nothing loads
-    field_attach_delete($rev[2]);
+    $entity->delete();
+    $controller->resetCache();
     foreach (array(0, 1, 2) as $vid) {
-      $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), "The test entity revision $vid is deleted.");
+      $revision = $controller->loadRevision($vid);
+      $this->assertFalse($revision);
     }
-    $read = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $read));
-    $this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), 'The test entity current revision is deleted.');
+    $this->assertFalse($controller->load($entity->id()));
   }
 
   /**
@@ -435,14 +319,12 @@ function testEntityCreateRenameBundle() {
     entity_create('field_instance', $this->instance_definition)->save();
 
     // Save an entity with data in the field.
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+    $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
     $entity->{$this->field_name} = $values;
-    field_attach_insert($entity);
 
     // Verify the field data is present on load.
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Data is retrieved for the new bundle");
 
     // Rename the bundle.
@@ -454,8 +336,9 @@ function testEntityCreateRenameBundle() {
     $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
 
     // Verify the field data is present on load.
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $entity));
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+    $controller->resetCache();
+    $entity = $controller->load($entity->id());
     $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Bundle name has been updated in the field storage");
   }
 
@@ -476,7 +359,12 @@ function testEntityDeleteBundle() {
 
     // Create a second field for the test bundle
     $field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 1);
+    $field = array(
+      'name' => $field_name,
+      'entity_type' => $entity_type,
+      'type' => 'test_field',
+      'cardinality' => 1,
+    );
     entity_create('field_entity', $field)->save();
     $instance = array(
       'field_name' => $field_name,
@@ -489,15 +377,13 @@ function testEntityDeleteBundle() {
     entity_create('field_instance', $instance)->save();
 
     // Save an entity with data for both fields
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+    $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
     $entity->{$this->field_name} = $values;
     $entity->{$field_name} = $this->_generateTestFieldValues(1);
-    field_attach_insert($entity);
+    $entity = $this->entitySaveReload($entity);
 
     // Verify the fields are present on load
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $entity));
     $this->assertEqual(count($entity->{$this->field_name}), 4, 'First field got loaded');
     $this->assertEqual(count($entity->{$field_name}), 1, 'Second field got loaded');
 
@@ -505,8 +391,10 @@ function testEntityDeleteBundle() {
     entity_test_delete_bundle($this->instance['bundle'], $entity_type);
 
     // Verify no data gets loaded
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $entity));
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+    $controller->resetCache();
+    $entity= $controller->load($entity->id());
+
     $this->assertTrue(empty($entity->{$this->field_name}), 'No data for first field');
     $this->assertTrue(empty($entity->{$field_name}), 'No data for second field');
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php
index b1a88fb..b6697e7 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php
@@ -26,11 +26,13 @@ public static function getInfo() {
    * Tests creating fields and instances during default config import.
    */
   function testImportCreateDefault() {
-    $field_id = 'field_test_import';
-    $instance_id = "entity_test.entity_test.$field_id";
-    $field_id_2 = 'field_test_import_2';
-    $instance_id_2a = "entity_test.entity_test.$field_id_2";
-    $instance_id_2b = "entity_test.entity_test.$field_id_2";
+    $field_name = 'field_test_import';
+    $field_id = "entity_test.$field_name";
+    $instance_id = "entity_test.entity_test.$field_name";
+    $field_name_2 = 'field_test_import_2';
+    $field_id_2 = "entity_test.$field_name_2";
+    $instance_id_2a = "entity_test.entity_test.$field_name_2";
+    $instance_id_2b = "entity_test.test_bundle.$field_name_2";
 
     // Check that the fields and instances do not exist yet.
     $this->assertFalse(entity_load('field_entity', $field_id));
@@ -72,15 +74,17 @@ function testImportCreateDefault() {
    */
   function testImportCreate() {
     // One field with one field instance.
-    $field_id = 'field_test_import_staging';
-    $instance_id = "entity_test.entity_test.$field_id";
+    $field_name = 'field_test_import_staging';
+    $field_id = "entity_test.$field_name";
+    $instance_id = "entity_test.entity_test.$field_name";
     $field_config_name = "field.field.$field_id";
     $instance_config_name = "field.instance.$instance_id";
 
     // One field with two field instances.
-    $field_id_2 = 'field_test_import_staging_2';
-    $instance_id_2a = "entity_test.test_bundle.$field_id_2";
-    $instance_id_2b = "entity_test.test_bundle_2.$field_id_2";
+    $field_name_2 = 'field_test_import_staging_2';
+    $field_id_2 = "entity_test.$field_name_2";
+    $instance_id_2a = "entity_test.test_bundle.$field_name_2";
+    $instance_id_2b = "entity_test.test_bundle_2.$field_name_2";
     $field_config_name_2 = "field.field.$field_id_2";
     $instance_config_name_2a = "field.instance.$instance_id_2a";
     $instance_config_name_2b = "field.instance.$instance_id_2b";
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php
index 216d36a..23ae199 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php
@@ -31,11 +31,13 @@ public static function getInfo() {
    * Tests deleting fields and instances as part of config import.
    */
   public function testImportDelete() {
-    $field_id = 'field_test_import';
-    $field_id_2 = 'field_test_import_2';
-    $instance_id = "entity_test.test_bundle.$field_id";
-    $instance_id_2a = "entity_test.test_bundle.$field_id_2";
-    $instance_id_2b = "entity_test.test_bundle_2.$field_id_2";
+    $field_name = 'field_test_import';
+    $field_id = "entity_test.$field_name";
+    $field_name_2 = 'field_test_import_2';
+    $field_id_2 = "entity_test.$field_name_2";
+    $instance_id = "entity_test.test_bundle.$field_name";
+    $instance_id_2a = "entity_test.test_bundle.$field_name_2";
+    $instance_id_2b = "entity_test.test_bundle_2.$field_name_2";
     $field_config_name = "field.field.$field_id";
     $field_config_name_2 = "field.field.$field_id_2";
     $instance_config_name = "field.instance.$instance_id";
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index 70f99f1..9c433aa 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -32,15 +32,6 @@ function testFieldInfo() {
       $this->assertEqual($info[$t_key]['provider'], 'field_test',  'Field type field_test module appears.');
     }
 
-    $storage_info = field_test_field_storage_info();
-    $info = field_info_storage_types();
-    foreach ($storage_info as $s_key => $storage) {
-      foreach ($storage as $key => $val) {
-        $this->assertEqual($info[$s_key][$key], $val, format_string('Storage type %s_key key %key is %value', array('%s_key' => $s_key, '%key' => $key, '%value' => print_r($val, TRUE))));
-      }
-      $this->assertEqual($info[$s_key]['module'], 'field_test',  'Storage type field_test module appears.');
-    }
-
     // Verify that no unexpected instances exist.
     $instances = field_info_instances('entity_test');
     $expected = array();
@@ -51,25 +42,26 @@ function testFieldInfo() {
     // Create a field, verify it shows up.
     $core_fields = field_info_fields();
     $field = entity_create('field_entity', array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'name' => drupal_strtolower($this->randomName()),
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     ));
     $field->save();
     $fields = field_info_fields();
     $this->assertEqual(count($fields), count($core_fields) + 1, 'One new field exists');
-    $this->assertEqual($fields[$field['field_name']]['field_name'], $field['field_name'], 'info fields contains field name');
-    $this->assertEqual($fields[$field['field_name']]['type'], $field['type'], 'info fields contains field type');
-    $this->assertEqual($fields[$field['field_name']]['module'], 'field_test', 'info fields contains field module');
+    $this->assertEqual($fields[$field['uuid']]['field_name'], $field['field_name'], 'info fields contains field name');
+    $this->assertEqual($fields[$field['uuid']]['type'], $field['type'], 'info fields contains field type');
+    $this->assertEqual($fields[$field['uuid']]['module'], 'field_test', 'info fields contains field module');
     $settings = array('test_field_setting' => 'dummy test string');
     foreach ($settings as $key => $val) {
-      $this->assertEqual($fields[$field['field_name']]['settings'][$key], $val, format_string('Field setting %key has correct default value %value', array('%key' => $key, '%value' => $val)));
+      $this->assertEqual($fields[$field['uuid']]['settings'][$key], $val, format_string('Field setting %key has correct default value %value', array('%key' => $key, '%value' => $val)));
     }
-    $this->assertEqual($fields[$field['field_name']]['cardinality'], 1, 'info fields contains cardinality 1');
-    $this->assertEqual($fields[$field['field_name']]['active'], TRUE, 'info fields contains active 1');
+    $this->assertEqual($fields[$field['uuid']]['cardinality'], 1, 'info fields contains cardinality 1');
+    $this->assertEqual($fields[$field['uuid']]['active'], TRUE, 'info fields contains active 1');
 
     // Create an instance, verify that it shows up
     $instance_definition = array(
-      'field_name' => $field['field_name'],
+      'field_name' => $field['name'],
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
       'label' => $this->randomName(),
@@ -121,7 +113,8 @@ function testFieldInfo() {
    */
   function testFieldPrepare() {
     $field_definition = array(
-      'field_name' => 'field',
+      'name' => 'field',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     );
     $field = entity_create('field_entity', $field_definition);
@@ -136,7 +129,7 @@ function testFieldPrepare() {
     field_info_cache_clear();
 
     // Read the field back.
-    $field = field_info_field($field_definition['field_name']);
+    $field = field_info_field('entity_test', $field_definition['name']);
 
     // Check that all expected settings are in place.
     $field_type = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($field_definition['type']);
@@ -148,12 +141,13 @@ function testFieldPrepare() {
    */
   function testInstancePrepare() {
     $field_definition = array(
-      'field_name' => 'field',
+      'name' => 'field',
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     );
     entity_create('field_entity', $field_definition)->save();
     $instance_definition = array(
-      'field_name' => $field_definition['field_name'],
+      'field_name' => $field_definition['name'],
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     );
@@ -182,8 +176,10 @@ function testInstancePrepare() {
   function testInstanceDisabledEntityType() {
     // For this test the field type and the entity type must be exposed by
     // different modules.
+    $this->enableModules(array('node', 'comment'));
     $field_definition = array(
-      'field_name' => 'field',
+      'name' => 'field',
+      'entity_type' => 'comment',
       'type' => 'test_field',
     );
     entity_create('field_entity', $field_definition)->save();
@@ -192,7 +188,7 @@ function testInstanceDisabledEntityType() {
       'entity_type' => 'comment',
       'bundle' => 'comment_node_article',
     );
-    entity_create('field_instance', $instance_definition)->save();
+    entity_create('field_instance', $instance_definition);
 
     // Disable coment module. This clears field_info cache.
     module_disable(array('comment'));
@@ -212,11 +208,18 @@ function testFieldMap() {
     // Create a couple fields.
     $fields  = array(
       array(
-        'field_name' => 'field_1',
+        'name' => 'field_1',
+        'entity_type' => 'entity_test',
         'type' => 'test_field',
       ),
       array(
-        'field_name' => 'field_2',
+        'name' => 'field_2',
+        'entity_type' => 'entity_test',
+        'type' => 'hidden_test_field',
+      ),
+      array(
+        'name' => 'field_2',
+        'entity_type' => 'entity_test_cache',
         'type' => 'hidden_test_field',
       ),
     );
@@ -252,17 +255,20 @@ function testFieldMap() {
     }
 
     $expected = array(
-      'field_1' => array(
-        'type' => 'test_field',
-        'bundles' => array(
-          'entity_test' => array('entity_test', 'test_bundle_2'),
+      'entity_test' => array(
+        'field_1' => array(
+          'type' => 'test_field',
+          'bundles' => array('entity_test', 'test_bundle_2'),
+        ),
+        'field_2' => array(
+          'type' => 'hidden_test_field',
+          'bundles' => array('entity_test'),
         ),
       ),
-      'field_2' => array(
-        'type' => 'hidden_test_field',
-        'bundles' => array(
-          'entity_test' => array('entity_test'),
-          'entity_test_cache' => array('entity_test'),
+      'entity_test_cache' => array(
+        'field_2' => array(
+          'type' => 'hidden_test_field',
+          'bundles' => array('entity_test')
         ),
       ),
     );
@@ -293,12 +299,13 @@ function testFieldInfoCache() {
     // field_info_fields().
     $field_name = drupal_strtolower($this->randomName());
     $field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     ));
     $field->save();
     $fields = field_info_fields();
-    $this->assertTrue(isset($fields[$field_name]), 'The test field is initially found in the array returned by field_info_fields().');
+    $this->assertTrue(isset($fields[$field->uuid]), 'The test field is initially found in the array returned by field_info_fields().');
 
     // Now rebuild the field info cache, and set a variable which will cause
     // the cache to be cleared while it's being rebuilt; see
@@ -307,7 +314,7 @@ function testFieldInfoCache() {
     field_info_cache_clear();
     \Drupal::state()->set('field_test.clear_info_cache_in_hook_entity_info', TRUE);
     $fields = field_info_fields();
-    $this->assertTrue(isset($fields[$field_name]), 'The test field is found in the array returned by field_info_fields() even if its cache is cleared while being rebuilt.');
+    $this->assertTrue(isset($fields[$field->uuid]), 'The test field is found in the array returned by field_info_fields() even if its cache is cleared while being rebuilt.');
   }
 
   /**
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
index cade864..3637788 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php
@@ -44,13 +44,14 @@ function setUp() {
     parent::setUp();
 
     $this->field_definition = array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'name' => drupal_strtolower($this->randomName()),
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     );
     $this->field = entity_create('field_entity', $this->field_definition);
     $this->field->save();
     $this->instance_definition = array(
-      'field_name' => $this->field['field_name'],
+      'field_name' => $this->field->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     );
@@ -103,40 +104,6 @@ function testCreateFieldInstance() {
       $this->pass(t('Cannot create an instance of a non-existing field.'));
     }
 
-    // Create a field restricted to a specific entity type.
-    $field_restricted_definition = array(
-      'field_name' => drupal_strtolower($this->randomName()),
-      'type' => 'test_field',
-      'entity_types' => array('entity_test_cache'),
-    );
-    $field_restricted = entity_create('field_entity', $field_restricted_definition);
-    $field_restricted->save();
-
-    // Check that an instance can be added to an entity type allowed
-    // by the field.
-    try {
-      $instance = $this->instance_definition;
-      $instance['field_name'] = $field_restricted_definition['field_name'];
-      $instance['entity_type'] = 'entity_test_cache';
-      entity_create('field_instance', $instance)->save();
-      $this->pass(t('Can create an instance on an entity type allowed by the field.'));
-    }
-    catch (FieldException $e) {
-      $this->fail(t('Can create an instance on an entity type allowed by the field.'));
-    }
-
-    // Check that an instance cannot be added to an entity type
-    // forbidden by the field.
-    try {
-      $instance = $this->instance_definition;
-      $instance['field_name'] = $field_restricted_definition['field_name'];
-      entity_create('field_instance', $instance)->save();
-      $this->fail(t('Cannot create an instance on an entity type forbidden by the field.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot create an instance on an entity type forbidden by the field.'));
-    }
-
     // TODO: test other failures.
   }
 
@@ -212,7 +179,7 @@ function testDeleteFieldInstance() {
     $another_instance->delete();
     $deleted_fields = \Drupal::state()->get('field.field.deleted');
     $this->assertTrue(isset($deleted_fields[$another_instance['field_id']]), 'A deleted field is marked for deletion.');
-    $field = field_read_field($another_instance['field_name']);
+    $field = field_read_field($another_instance['entity_type'], $another_instance['field_name']);
     $this->assertFalse($field, 'The field marked to be deleted is not found anymore in the configuration.');
   }
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
index 4580e7b..7ba1a18 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
@@ -20,7 +20,7 @@
    *
    * @var array
    */
-  public static $modules = array('user', 'entity', 'system', 'field', 'text', 'field_sql_storage', 'entity_test', 'field_test');
+  public static $modules = array('user', 'entity', 'system', 'field', 'text', 'entity_test', 'field_test');
 
   /**
    * A string for assert raw and text helper methods.
@@ -65,7 +65,12 @@ function createFieldWithInstance($suffix = '', $entity_type = 'entity_test', $bu
     $instance_definition = 'instance_definition' . $suffix;
 
     $this->$field_name = drupal_strtolower($this->randomName() . '_field_name' . $suffix);
-    $this->$field = entity_create('field_entity', array('field_name' => $this->$field_name, 'type' => 'test_field', 'cardinality' => 4));
+    $this->$field = entity_create('field_entity', array(
+      'name' => $this->$field_name,
+      'entity_type' => $entity_type,
+      'type' => 'test_field',
+      'cardinality' => 4,
+    ));
     $this->$field->save();
     $this->$field_id = $this->{$field}['uuid'];
     $this->$instance_definition = array(
@@ -92,6 +97,16 @@ function createFieldWithInstance($suffix = '', $entity_type = 'entity_test', $bu
   }
 
   /**
+   * Save and reload an entity.
+   */
+  protected function entitySaveReload(EntityInterface $entity) {
+    $entity->save();
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->entityType());
+    $controller->resetCache();
+    return $controller->load($entity->id());
+  }
+
+  /**
    * Generate random values for a field_test field.
    *
    * @param $cardinality
diff --git a/core/modules/field/lib/Drupal/field/Tests/FormTest.php b/core/modules/field/lib/Drupal/field/Tests/FormTest.php
index b263859..4ad739a 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FormTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FormTest.php
@@ -61,9 +61,23 @@ function setUp() {
     $web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
     $this->drupalLogin($web_user);
 
-    $this->field_single = array('field_name' => 'field_single', 'type' => 'test_field');
-    $this->field_multiple = array('field_name' => 'field_multiple', 'type' => 'test_field', 'cardinality' => 4);
-    $this->field_unlimited = array('field_name' => 'field_unlimited', 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
+    $this->field_single = array(
+      'name' => 'field_single',
+      'entity_type' => 'entity_test',
+      'type' => 'test_field',
+    );
+    $this->field_multiple = array(
+      'name' => 'field_multiple',
+      'entity_type' => 'entity_test',
+      'type' => 'test_field',
+      'cardinality' => 4,
+    );
+    $this->field_unlimited = array(
+      'name' => 'field_unlimited',
+      'entity_type' => 'entity_test',
+      'type' => 'test_field',
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    );
 
     $this->instance = array(
       'entity_type' => 'entity_test',
@@ -79,7 +93,7 @@ function setUp() {
 
   function testFieldFormSingle() {
     $field = $this->field_single;
-    $field_name = $field['field_name'];
+    $field_name = $field['name'];
     $this->instance['field_name'] = $field_name;
     entity_create('field_entity', $field)->save();
     entity_create('field_instance', $this->instance)->save();
@@ -162,7 +176,7 @@ function testFieldFormSingle() {
    */
   function testFieldFormDefaultValue() {
     $field = $this->field_single;
-    $field_name = $field['field_name'];
+    $field_name = $field['name'];
     $this->instance['field_name'] = $field_name;
     $default = rand(1, 127);
     $this->instance['default_value'] = array(array('value' => $default));
@@ -194,7 +208,7 @@ function testFieldFormDefaultValue() {
 
   function testFieldFormSingleRequired() {
     $field = $this->field_single;
-    $field_name = $field['field_name'];
+    $field_name = $field['name'];
     $this->instance['field_name'] = $field_name;
     $this->instance['required'] = TRUE;
     entity_create('field_entity', $field)->save();
@@ -244,7 +258,7 @@ function testFieldFormSingleRequired() {
 
   function testFieldFormUnlimited() {
     $field = $this->field_unlimited;
-    $field_name = $field['field_name'];
+    $field_name = $field['name'];
     $this->instance['field_name'] = $field_name;
     entity_create('field_entity', $field)->save();
     entity_create('field_instance', $this->instance)->save();
@@ -331,7 +345,7 @@ function testFieldFormUnlimited() {
   function testFieldFormMultivalueWithRequiredRadio() {
     // Create a multivalue test field.
     $field = $this->field_unlimited;
-    $field_name = $field['field_name'];
+    $field_name = $field['name'];
     $this->instance['field_name'] = $field_name;
     entity_create('field_entity', $field)->save();
     entity_create('field_instance', $this->instance)->save();
@@ -342,7 +356,8 @@ function testFieldFormMultivalueWithRequiredRadio() {
 
     // Add a required radio field.
     entity_create('field_entity', array(
-      'field_name' => 'required_radio_test',
+      'name' => 'required_radio_test',
+      'entity_type' => 'entity_test',
       'type' => 'list_text',
       'settings' => array(
         'allowed_values' => array('yes' => 'yes', 'no' => 'no'),
@@ -378,7 +393,7 @@ function testFieldFormMultivalueWithRequiredRadio() {
 
   function testFieldFormJSAddMore() {
     $field = $this->field_unlimited;
-    $field_name = $field['field_name'];
+    $field_name = $field['name'];
     $this->instance['field_name'] = $field_name;
     entity_create('field_entity', $field)->save();
     entity_create('field_instance', $this->instance)->save();
@@ -440,7 +455,7 @@ function testFieldFormMultipleWidget() {
     // Create a field with fixed cardinality and an instance using a multiple
     // widget.
     $field = $this->field_multiple;
-    $field_name = $field['field_name'];
+    $field_name = $field['name'];
     $this->instance['field_name'] = $field_name;
     entity_create('field_entity', $field)->save();
     entity_create('field_instance', $this->instance)->save();
@@ -485,10 +500,11 @@ function testFieldFormMultipleWidget() {
    * Tests fields with no 'edit' access.
    */
   function testFieldFormAccess() {
+    $entity_type = 'entity_test_rev';
     // Create a "regular" field.
     $field = $this->field_single;
-    $field_name = $field['field_name'];
-    $entity_type = 'entity_test_rev';
+    $field['entity_type'] = $entity_type;
+    $field_name = $field['name'];
     $instance = $this->instance;
     $instance['field_name'] = $field_name;
     $instance['entity_type'] = $entity_type;
@@ -501,10 +517,11 @@ function testFieldFormAccess() {
 
     // Create a field with no edit access - see field_test_field_access().
     $field_no_access = array(
-      'field_name' => 'field_no_edit_access',
+      'name' => 'field_no_edit_access',
+      'entity_type' => $entity_type,
       'type' => 'test_field',
     );
-    $field_name_no_access = $field_no_access['field_name'];
+    $field_name_no_access = $field_no_access['name'];
     $instance_no_access = array(
       'field_name' => $field_name_no_access,
       'entity_type' => $entity_type,
@@ -577,7 +594,8 @@ function testFieldFormAccess() {
   function testFieldFormHiddenWidget() {
     $entity_type = 'entity_test_rev';
     $field = $this->field_single;
-    $field_name = $field['field_name'];
+    $field['entity_type'] = $entity_type;
+    $field_name = $field['name'];
     $this->instance['field_name'] = $field_name;
     $this->instance['default_value'] = array(0 => array('value' => 99));
     $this->instance['entity_type'] = $entity_type;
diff --git a/core/modules/field/lib/Drupal/field/Tests/NestedFormTest.php b/core/modules/field/lib/Drupal/field/Tests/NestedFormTest.php
index 3d180f6..e812ff5 100644
--- a/core/modules/field/lib/Drupal/field/Tests/NestedFormTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/NestedFormTest.php
@@ -33,8 +33,17 @@ public function setUp() {
     $web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
     $this->drupalLogin($web_user);
 
-    $this->field_single = array('field_name' => 'field_single', 'type' => 'test_field');
-    $this->field_unlimited = array('field_name' => 'field_unlimited', 'type' => 'test_field', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
+    $this->field_single = array(
+      'name' => 'field_single',
+      'entity_type' => 'entity_test',
+      'type' => 'test_field',
+    );
+    $this->field_unlimited = array(
+      'name' => 'field_unlimited',
+      'entity_type' => 'entity_test',
+      'type' => 'test_field',
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    );
 
     $this->instance = array(
       'entity_type' => 'entity_test',
diff --git a/core/modules/field/lib/Drupal/field/Tests/ShapeItemTest.php b/core/modules/field/lib/Drupal/field/Tests/ShapeItemTest.php
index 6f7f3a0..b54174f 100644
--- a/core/modules/field/lib/Drupal/field/Tests/ShapeItemTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/ShapeItemTest.php
@@ -42,7 +42,8 @@ public function setUp() {
 
     // Create an field field and instance for validation.
     $field = array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'entity_test',
       'type' => 'shape',
     );
     entity_create('field_entity', $field)->save();
diff --git a/core/modules/field/lib/Drupal/field/Tests/TestItemTest.php b/core/modules/field/lib/Drupal/field/Tests/TestItemTest.php
index e8b85d1..b4c4aa4 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TestItemTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TestItemTest.php
@@ -42,7 +42,8 @@ public function setUp() {
 
     // Create an field field and instance for validation.
     $field = array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'entity_test',
       'type' => 'test_field',
     );
     entity_create('field_entity', $field)->save();
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
index ebf9722..7b6ce22 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
@@ -85,13 +85,14 @@ function setUp() {
     $this->entity_type = 'entity_test';
 
     $this->field_definition = array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => $this->entity_type,
       'type' => 'test_field',
       'cardinality' => 4,
       'translatable' => TRUE,
     );
     entity_create('field_entity', $this->field_definition)->save();
-    $this->field = field_read_field($this->field_name);
+    $this->field = field_read_field($this->entity_type, $this->field_name);
 
     $this->instance_definition = array(
       'field_name' => $this->field_name,
@@ -153,8 +154,7 @@ function testTranslatableFieldSaveLoad() {
     // Prepare the field translations.
     $entity_type = 'entity_test';
     field_test_entity_info_translatable($entity_type, TRUE);
-    $id = $revision_id = 1;
-    $entity = entity_create($entity_type, array('id' => $id, 'revision_id' => $revision_id, 'type' => $this->instance['bundle']));
+    $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
     $field_translations = array();
     $available_langcodes = field_available_languages($entity_type, $this->field);
     $this->assertTrue(count($available_langcodes) > 1, 'Field is translatable.');
@@ -165,10 +165,7 @@ function testTranslatableFieldSaveLoad() {
     }
 
     // Save and reload the field translations.
-    field_attach_insert($entity);
-    $entity = entity_create($entity_type, array('id' => $id, 'revision_id' => $revision_id, 'type' => $this->instance['bundle']));
-    $entity->langcode->value = reset($available_langcodes);
-    field_attach_load($entity_type, array($id => $entity));
+    $entity = $this->entitySaveReload($entity);
 
     // Check if the correct values were saved/loaded.
     foreach ($field_translations as $langcode => $items) {
@@ -182,7 +179,7 @@ function testTranslatableFieldSaveLoad() {
     // Test default values.
     $field_name_default = drupal_strtolower($this->randomName() . '_field_name');
     $field_definition = $this->field_definition;
-    $field_definition['field_name'] = $field_name_default;
+    $field_definition['name'] = $field_name_default;
     entity_create('field_entity', $field_definition)->save();
 
     $instance_definition = $this->instance_definition;
@@ -197,9 +194,7 @@ function testTranslatableFieldSaveLoad() {
     asort($translation_langcodes);
     $translation_langcodes = array_values($translation_langcodes);
 
-    $id++;
-    $revision_id++;
-    $values = array('id' => $id, 'revision_id' => $revision_id, 'type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
+    $values = array('type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
     $entity = entity_create($entity_type, $values);
     foreach ($translation_langcodes as $langcode) {
       $values[$this->field_name][$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
@@ -217,9 +212,7 @@ function testTranslatableFieldSaveLoad() {
 
     // Check that explicit empty values are not overridden with default values.
     foreach (array(NULL, array()) as $empty_items) {
-      $id++;
-      $revision_id++;
-      $values = array('id' => $id, 'revision_id' => $revision_id, 'type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
+      $values = array('type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
       $entity = entity_create($entity_type, $values);
       foreach ($translation_langcodes as $langcode) {
         $values[$this->field_name][$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
@@ -244,7 +237,8 @@ function testFieldDisplayLanguage() {
     // We need an additional field here to properly test display language
     // suggestions.
     $field = array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => $entity_type,
       'type' => 'test_field',
       'cardinality' => 2,
       'translatable' => TRUE,
@@ -252,7 +246,7 @@ function testFieldDisplayLanguage() {
     entity_create('field_entity', $field)->save();
 
     $instance = array(
-      'field_name' => $field['field_name'],
+      'field_name' => $field['name'],
       'entity_type' => $entity_type,
       'bundle' => 'entity_test',
     );
@@ -272,7 +266,7 @@ function testFieldDisplayLanguage() {
     // enabled.
     foreach ($instances as $instance) {
       $field_name = $instance['field_name'];
-      $field = field_info_field($field_name);
+      $field = $instance->getField();
       do {
         // Index 0 is reserved for the requested language, this way we ensure
         // that no field is actually populated with it.
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
index df93f12..a31153f 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
@@ -33,7 +33,7 @@ class TranslationWebTest extends FieldTestBase {
    *
    * @var string
    */
-  protected $entity_type = 'test_entity';
+  protected $entity_type = 'entity_test_rev';
 
   /**
    * The field to use in this test.
@@ -62,16 +62,15 @@ function setUp() {
 
     $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
 
-    $this->entity_type = 'entity_test_rev';
-
     $field = array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => $this->entity_type,
       'type' => 'test_field',
       'cardinality' => 4,
       'translatable' => TRUE,
     );
     entity_create('field_entity', $field)->save();
-    $this->field = field_read_field($this->field_name);
+    $this->field = field_read_field($this->entity_type, $this->field_name);
 
     $instance = array(
       'field_name' => $this->field_name,
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
index 4bf2625..1f60380 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\field\Tests\Views;
+use Drupal\Core\Entity\DatabaseStorageController;
 
 /**
  * Test the produced views_data.
@@ -28,9 +29,9 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
-    $field_names = $this->setUpFields();
+    $field_names = $this->setUpFields(1);
 
-    // The first one will be attached to nodes only.
+    // Attach the field to nodes only.
     $instance = array(
       'field_name' => $field_names[0],
       'entity_type' => 'node',
@@ -38,33 +39,10 @@ function setUp() {
     );
     entity_create('field_instance', $instance)->save();
 
-    // The second one will be attached to users only.
-    $instance = array(
-      'field_name' => $field_names[1],
-      'entity_type' => 'user',
-      'bundle' => 'user',
-    );
-    entity_create('field_instance', $instance)->save();
-
-    // The third will be attached to both nodes and users.
-    $instance = array(
-      'field_name' => $field_names[2],
-      'entity_type' => 'node',
-      'bundle' => 'page',
-    );
-    entity_create('field_instance', $instance)->save();
-    $instance = array(
-      'field_name' => $field_names[2],
-      'entity_type' => 'user',
-      'bundle' => 'user',
-    );
-    entity_create('field_instance', $instance)->save();
-
     // Now create some example nodes/users for the view result.
     for ($i = 0; $i < 5; $i++) {
       $edit = array(
-        'field_name_0' => array((array('value' => $this->randomName()))),
-        'field_name_2' => array((array('value' => $this->randomName()))),
+        $field_names[0] => array((array('value' => $this->randomName()))),
       );
       $nodes[] = $this->drupalCreateNode($edit);
     }
@@ -84,8 +62,8 @@ function testViewsData() {
     // Check the table and the joins of the first field.
     // Attached to node only.
     $field = $this->fields[0];
-    $current_table = _field_sql_storage_tablename($field);
-    $revision_table = _field_sql_storage_revision_tablename($field);
+    $current_table = DatabaseStorageController::_fieldTableName($field);
+    $revision_table = DatabaseStorageController::_fieldRevisionTableName($field);
     $data[$current_table] = $views_data->get($current_table);
     $data[$revision_table] = $views_data->get($revision_table);
 
@@ -99,7 +77,6 @@ function testViewsData() {
       'left_field' => 'nid',
       'field' => 'entity_id',
       'extra' => array(
-        array('field' => 'entity_type', 'value' => 'node'),
         array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
       ),
     );
@@ -108,54 +85,10 @@ function testViewsData() {
       'left_field' => 'vid',
       'field' => 'revision_id',
       'extra' => array(
-        array('field' => 'entity_type', 'value' => 'node'),
         array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
       ),
     );
     $this->assertEqual($expected_join, $data[$revision_table]['table']['join']['node_field_revision']);
-
-    // Check the table and the joins of the second field.
-    // Attached to both node and user.
-    $field_2 = $this->fields[2];
-    $current_table_2 = _field_sql_storage_tablename($field_2);
-    $revision_table_2 = _field_sql_storage_revision_tablename($field_2);
-    $data[$current_table_2] = $views_data->get($current_table_2);
-    $data[$revision_table_2] = $views_data->get($revision_table_2);
-
-    $this->assertTrue(isset($data[$current_table_2]));
-    $this->assertTrue(isset($data[$revision_table_2]));
-    // The second field should join against both node and users.
-    $this->assertTrue(isset($data[$current_table_2]['table']['join']['node']));
-    $this->assertTrue(isset($data[$revision_table_2]['table']['join']['node_field_revision']));
-    $this->assertTrue(isset($data[$current_table_2]['table']['join']['users']));
-
-    $expected_join = array(
-      'left_field' => 'nid',
-      'field' => 'entity_id',
-      'extra' => array(
-        array('field' => 'entity_type', 'value' => 'node'),
-        array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
-      )
-    );
-    $this->assertEqual($expected_join, $data[$current_table_2]['table']['join']['node']);
-    $expected_join = array(
-      'left_field' => 'vid',
-      'field' => 'revision_id',
-      'extra' => array(
-        array('field' => 'entity_type', 'value' => 'node'),
-        array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
-      )
-    );
-    $this->assertEqual($expected_join, $data[$revision_table_2]['table']['join']['node_field_revision']);
-    $expected_join = array(
-      'left_field' => 'uid',
-      'field' => 'entity_id',
-      'extra' => array(
-        array('field' => 'entity_type', 'value' => 'user'),
-        array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
-      )
-    );
-    $this->assertEqual($expected_join, $data[$current_table_2]['table']['join']['users']);
   }
 
 }
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php b/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
index 3cba44c..2f8e5a6 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
@@ -59,7 +59,11 @@ function setUpFields($amount = 3) {
     $field_names = array();
     for ($i = 0; $i < $amount; $i++) {
       $field_names[$i] = 'field_name_' . $i;
-      $field = array('field_name' => $field_names[$i], 'type' => 'text');
+      $field = array(
+        'name' => $field_names[$i],
+        'entity_type' => 'node',
+        'type' => 'text',
+      );
 
       $this->fields[$i] = $field = entity_create('field_entity', $field);
       $field->save();
@@ -70,7 +74,7 @@ function setUpFields($amount = 3) {
   function setUpInstances($bundle = 'page') {
     foreach ($this->fields as $key => $field) {
       $instance = array(
-        'field_name' => $field['field_name'],
+        'field_name' => $field['name'],
         'entity_type' => 'node',
         'bundle' => 'page',
       );
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
index 8658c52..2002c0f 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
@@ -47,10 +47,20 @@ protected function setUp() {
     $this->setUpFields(3);
 
     // Setup a field with cardinality > 1.
-    $this->fields[3] = $field = entity_create('field_entity', array('field_name' => 'field_name_3', 'type' => 'text', 'cardinality' => FIELD_CARDINALITY_UNLIMITED));
+    $this->fields[3] = $field = entity_create('field_entity', array(
+      'name' => 'field_name_3',
+      'entity_type' => 'node',
+      'type' => 'text',
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    ));
     $field->save();
     // Setup a field that will have no value.
-    $this->fields[4] = $field = entity_create('field_entity', array('field_name' => 'field_name_4', 'type' => 'text', 'cardinality' => FIELD_CARDINALITY_UNLIMITED));
+    $this->fields[4] = $field = entity_create('field_entity', array(
+      'name' => 'field_name_4',
+      'entity_type' => 'node',
+      'type' => 'text',
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    ));
     $field->save();
 
     $this->setUpInstances();
@@ -86,7 +96,7 @@ protected function prepareView(ViewExecutable $view) {
     $view->initDisplay();
     foreach ($this->fields as $key => $field) {
       $view->display_handler->options['fields'][$field['field_name']]['id'] = $field['field_name'];
-      $view->display_handler->options['fields'][$field['field_name']]['table'] = 'field_data_' . $field['field_name'];
+      $view->display_handler->options['fields'][$field['field_name']]['table'] = 'node__' . $field['field_name'];
       $view->display_handler->options['fields'][$field['field_name']]['field'] = $field['field_name'];
     }
   }
diff --git a/core/modules/field/lib/Drupal/field/Tests/reEnableModuleFieldTest.php b/core/modules/field/lib/Drupal/field/Tests/reEnableModuleFieldTest.php
index 6d4ed4a..0b5400d 100644
--- a/core/modules/field/lib/Drupal/field/Tests/reEnableModuleFieldTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/reEnableModuleFieldTest.php
@@ -21,7 +21,6 @@ class reEnableModuleFieldTest extends WebTestBase {
    */
   public static $modules = array(
     'field',
-    'field_sql_storage',
     'node',
     // We use telephone module instead of test_field because test_field is
     // hidden and does not display on the admin/modules page.
@@ -51,7 +50,8 @@ function testReEnabledField() {
 
     // Add a telephone field to the article content type.
     $field = entity_create('field_entity', array(
-      'field_name' => 'field_telephone',
+      'name' => 'field_telephone',
+      'entity_type' => 'node',
       'type' => 'telephone',
     ));
     $field->save();
diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module
index 800f4d4..3ab0c7e 100644
--- a/core/modules/field/tests/modules/field_test/field_test.module
+++ b/core/modules/field/tests/modules/field_test/field_test.module
@@ -18,7 +18,6 @@
 
 require_once __DIR__ . '/field_test.entity.inc';
 require_once __DIR__ . '/field_test.field.inc';
-require_once __DIR__ . '/field_test.storage.inc';
 
 /**
  * Implements hook_permission().
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 66c9911..3bbbae2 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
@@ -195,7 +195,7 @@ function field_test_field_storage_delete(EntityInterface $entity, $fields) {
   // does, is highly inefficient in our case...
   foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
     if (isset($fields[$instance['field_id']])) {
-      $field = field_info_field_by_id($instance['field_id']);
+      $field = $instance->getField();
       field_test_field_storage_purge($entity, $field, $instance);
     }
   }
@@ -403,7 +403,7 @@ function field_test_field_storage_delete_field($field) {
 function field_test_field_storage_delete_instance($instance) {
   $data = _field_test_storage_data();
 
-  $field = field_info_field($instance['field_name']);
+  $field = $instance->getField();
   $field_data = &$data[$field['uuid']];
   foreach (array('current', 'revisions') as $sub_table) {
     foreach ($field_data[$sub_table] as &$row) {
@@ -424,8 +424,8 @@ function field_test_entity_bundle_rename($entity_type, $bundle_old, $bundle_new)
 
   // We need to account for deleted or inactive fields and instances.
   $instances = field_read_instances(array('bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
-  foreach ($instances as $field_name => $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
+  foreach ($instances as $instance) {
+    $field = $instance->getField();
     if ($field && $field['storage']['type'] == 'field_test_storage') {
       $field_data = &$data[$field['uuid']];
       foreach (array('current', 'revisions') as $sub_table) {
diff --git a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml
similarity index 54%
rename from core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml
rename to core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml
index f472164..ae64a88 100644
--- a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml
+++ b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import.yml
@@ -1,16 +1,12 @@
-id: field_test_import
+id: entity_test.field_test_import
 langcode: und
+name: field_test_import
+entity_type: entity_test
 type: text
 settings:
   max_length: '255'
 module: text
 active: '1'
-entity_types: {  }
-storage:
-  type: field_sql_storage
-  settings: {  }
-  module: field_sql_storage
-  active: '1'
 locked: '0'
 cardinality: '1'
 translatable: false
diff --git a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import_2.yml b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml
similarity index 54%
rename from core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import_2.yml
rename to core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml
index 4d508a9..38c855d 100644
--- a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import_2.yml
+++ b/core/modules/field/tests/modules/field_test_config/config/field.field.entity_test.field_test_import_2.yml
@@ -1,16 +1,12 @@
-id: field_test_import_2
+id: entity_test.field_test_import_2
 langcode: und
+name: field_test_import_2
+entity_type: entity_test
 type: text
 settings:
   max_length: '255'
 module: text
 active: '1'
-entity_types: {  }
-storage:
-  type: field_sql_storage
-  settings: {  }
-  module: field_sql_storage
-  active: '1'
 locked: '0'
 cardinality: '1'
 translatable: false
diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml
similarity index 58%
rename from core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging.yml
rename to core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml
index 4226f72..265e180 100644
--- a/core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging.yml
+++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging.yml
@@ -1,17 +1,13 @@
-id: field_test_import_staging
+id: entity_test.field_test_import_staging
 uuid: 0bf654cc-f14a-4881-b94c-76959e47466b
 langcode: und
+name: field_test_import_staging
+entity_type: entity_test
 type: text
 settings:
   max_length: '255'
 module: text
 active: '1'
-entity_types: {  }
-storage:
-  type: field_sql_storage
-  settings: {  }
-  module: field_sql_storage
-  active: '1'
 locked: '0'
 cardinality: '1'
 translatable: '0'
diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging_2.yml b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml
similarity index 58%
rename from core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging_2.yml
rename to core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml
index a0eb94e..ba36c64 100644
--- a/core/modules/field/tests/modules/field_test_config/staging/field.field.field_test_import_staging_2.yml
+++ b/core/modules/field/tests/modules/field_test_config/staging/field.field.entity_test.field_test_import_staging_2.yml
@@ -1,17 +1,13 @@
-id: field_test_import_staging_2
+id: entity_test.field_test_import_staging_2
 uuid: 2165d9aa-9a0c-41a1-be02-2a49f3405c00
 langcode: und
+name: field_test_import_staging_2
+entity_type: entity_test
 type: text
 settings:
   max_length: '255'
 module: text
 active: '1'
-entity_types: {  }
-storage:
-  type: field_sql_storage
-  settings: {  }
-  module: field_sql_storage
-  active: '1'
 locked: '0'
 cardinality: '1'
 translatable: '0'
diff --git a/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_fieldapi.yml b/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_fieldapi.yml
index 940848f..b370ab1 100644
--- a/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_fieldapi.yml
+++ b/core/modules/field/tests/modules/field_test_views/test_views/views.view.test_view_fieldapi.yml
@@ -16,7 +16,7 @@ display:
           provider: node
         field_name_0:
           id: field_name_0
-          table: field_data_field_name_0
+          table: node__field_name_0
           field: field_name_0
           plugin_id: field
           provider: field
diff --git a/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml b/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml
deleted file mode 100644
index 9a68dd9..0000000
--- a/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-# Schema for configuration files of the Field SQL Storage module.
-
-field_storage.field_sql_storage.settings:
-  type: sequence
-  label: 'Settings'
-  sequence:
-    - type: string
diff --git a/core/modules/field_sql_storage/field_sql_storage.info.yml b/core/modules/field_sql_storage/field_sql_storage.info.yml
deleted file mode 100644
index e5172b5..0000000
--- a/core/modules/field_sql_storage/field_sql_storage.info.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-name: 'Field SQL Storage'
-type: module
-description: 'Stores field data in an SQL database.'
-package: Core
-version: VERSION
-core: 8.x
-dependencies:
-  - field
-required: true
diff --git a/core/modules/field_sql_storage/field_sql_storage.install b/core/modules/field_sql_storage/field_sql_storage.install
deleted file mode 100644
index 5c9eef2..0000000
--- a/core/modules/field_sql_storage/field_sql_storage.install
+++ /dev/null
@@ -1,153 +0,0 @@
-<?php
-
-/**
- * @file
- * Install, update, and uninstall functions for the Field SQL Storage module.
- */
-
-use Drupal\field\Plugin\Core\Entity\Field;
-
-/**
- * Writes field data directly to SQL storage.
- *
- * @ingroup update_api
- */
-function _update_8000_field_sql_storage_write($entity_type, $bundle, $entity_id, $revision_id, $field_name, $data) {
-  $table_name = "field_data_{$field_name}";
-  $revision_name = "field_revision_{$field_name}";
-
-  db_delete($table_name)
-    ->condition('entity_type', $entity_type)
-    ->condition('entity_id', $entity_id)
-    ->execute();
-  db_delete($revision_name)
-    ->condition('entity_type', $entity_type)
-    ->condition('entity_id', $entity_id)
-    ->condition('revision_id', $revision_id)
-    ->execute();
-
-  $columns = array();
-  foreach ($data as $langcode => $items) {
-    foreach ($items as $delta => $item) {
-      $record = array(
-        'entity_type' => $entity_type,
-        'entity_id' => $entity_id,
-        'revision_id' => $revision_id,
-        'bundle' => $bundle,
-        'delta' => $delta,
-        'langcode' => $langcode,
-      );
-      foreach ($item as $column => $value) {
-        $record[_field_sql_storage_columnname($field_name, $column)] = $value;
-      }
-
-      $records[] = $record;
-      // Record the columns used.
-      $columns += $record;
-    }
-  }
-
-  if ($columns) {
-    $query = db_insert($table_name)->fields(array_keys($columns));
-    $revision_query = db_insert($revision_name)->fields(array_keys($columns));
-    foreach ($records as $record) {
-      $query->values($record);
-      if ($revision_id) {
-        $revision_query->values($record);
-      }
-    }
-    $query->execute();
-    $revision_query->execute();
-  }
-}
-
-/**
- * Implements hook_update_dependencies().
- */
-function field_sql_storage_update_dependencies() {
-  // Convert storage tables after field definitions have moved to
-  // ConfigEntities.
-  $dependencies['field_sql_storage'][8000] = array(
-    'field' => 8003,
-  );
-  return $dependencies;
-}
-
-/**
- * Renames the 'language' column to 'langcode' in field data tables.
- */
-function field_sql_storage_update_8000(&$sandbox) {
-  // Get field definitions from config, and deleted fields from state system.
-  $config_names = config_get_storage_names_with_prefix('field.field');
-  $deleted_fields = Drupal::state()->get('field.field.deleted') ?: array();
-  // Ditch UUID keys, we will iterate through deleted fields using a numeric
-  // index.
-  $deleted_fields = array_values($deleted_fields);
-
-  if (empty($config_names) && empty($deleted_fields)) {
-    return;
-  }
-
-  if (!isset($sandbox['index'])) {
-    $sandbox['index'] = 0;
-    $sandbox['max'] = count($config_names) + count($deleted_fields);
-  }
-
-  // Retrieve the next field definition. When the index exceeds the number of
-  // 'configuration' fields, use it to iterate on deleted fields.
-  if (isset($config_names[$sandbox['index']])) {
-    $field_config = Drupal::config($config_names[$sandbox['index']])->get();
-  }
-  else {
-    $field_config = $deleted_fields[$sandbox['index'] - count($config_names)];
-  }
-
-  if ($field_config['storage']['type'] == 'field_sql_storage') {
-    $field = new Field($field_config);
-
-    // Prepare updated schema data structures.
-    $primary_key_data = array(
-      'entity_type',
-      'entity_id',
-      'deleted',
-      'delta',
-      'langcode',
-    );
-    $primary_key_revision = array(
-      'entity_type',
-      'entity_id',
-      'revision_id',
-      'deleted',
-      'delta',
-      'langcode',
-    );
-    $langcode_index = array(
-      'langcode',
-    );
-    $field_langcode = array(
-      'type' => 'varchar',
-      'length' => 32,
-      'not null' => TRUE,
-      'default' => '',
-    );
-
-    $table_info = array(
-      _field_sql_storage_tablename($field) => $primary_key_data,
-      _field_sql_storage_revision_tablename($field) => $primary_key_revision,
-    );
-    foreach ($table_info as $table => $primary_key) {
-      // Do not update tables which already have the langcode column,
-      // created during the upgrade before this update function.
-      if (db_field_exists($table, 'language')) {
-        db_drop_primary_key($table);
-        db_drop_index($table, 'language');
-        db_change_field($table, 'language', 'langcode', $field_langcode);
-        db_add_primary_key($table, $primary_key);
-        db_add_index($table, 'langcode', $langcode_index);
-      }
-    }
-  }
-
-  $sandbox['index']++;
-  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['index'] / $sandbox['max']);
-}
diff --git a/core/modules/field_sql_storage/field_sql_storage.module b/core/modules/field_sql_storage/field_sql_storage.module
deleted file mode 100644
index 5e3e0dd..0000000
--- a/core/modules/field_sql_storage/field_sql_storage.module
+++ /dev/null
@@ -1,652 +0,0 @@
-<?php
-
-/**
- * @file
- * Default implementation of the field storage API.
- */
-
-use Drupal\Core\Database\Database;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\field\FieldUpdateForbiddenException;
-use Drupal\field\Plugin\Core\Entity\Field;
-
-/**
- * Implements hook_help().
- */
-function field_sql_storage_help($path, $arg) {
-  switch ($path) {
-    case 'admin/help#field_sql_storage':
-      $output = '';
-      $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The Field SQL Storage module stores field data in the database. It is the default field storage module; other field storage mechanisms may be available as contributed modules. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
-      return $output;
-  }
-}
-
-/**
- * Implements hook_field_storage_info().
- */
-function field_sql_storage_field_storage_info() {
-  return array(
-    'field_sql_storage' => array(
-      'label' => t('Default SQL storage'),
-      'description' => t('Stores fields in the local SQL database, using per-field tables.'),
-    ),
-  );
-}
-
-/**
- * Generates a table name for a field data table.
- *
- * When a field is a deleted, the table is renamed to
- * {field_deleted_data_FIELD_UUID}. To make sure we don't end up with table
- * names longer than 64 characters, we hash the uuid and return the first 10
- * characters so we end up with a short unique ID.
- *
- * @param $field
- *   The field structure.
- *
- * @return
- *   A string containing the generated name for the database table.
- */
-function _field_sql_storage_tablename($field) {
-  if ($field['deleted']) {
-    return "field_deleted_data_" . substr(hash('sha256', $field['uuid']), 0, 10);
-  }
-  else {
-    return "field_data_{$field['field_name']}";
-  }
-}
-
-/**
- * Generates a table name for a field revision archive table.
- *
- * When a field is a deleted, the table is renamed to
- * {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with table
- * names longer than 64 characters, we hash the uuid and return the first
- * 10 characters so we end up with a short unique ID.
- *
- * @param $name
- *   The field structure.
- *
- * @return
- *   A string containing the generated name for the database table.
- */
-function _field_sql_storage_revision_tablename($field) {
-  if ($field['deleted']) {
-    return "field_deleted_revision_" . substr(hash('sha256', $field['uuid']), 0, 10);
-  }
-  else {
-    return "field_revision_{$field['field_name']}";
-  }
-}
-
-/**
- * Generates a column name for a field data table.
- *
- * @param $name
- *   The name of the field.
- * @param $column
- *   The name of the column.
- *
- * @return
- *   A string containing a generated column name for a field data table that is
- *   unique among all other fields.
- */
-function _field_sql_storage_columnname($name, $column) {
-  return in_array($column, Field::getReservedColumns()) ? $column : $name . '_' . $column;
-}
-
-/**
- * Generates an index name for a field data table.
- *
- * @param $name
- *   The name of the field.
- * @param $column
- *   The name of the index.
- *
- * @return
- *   A string containing a generated index name for a field data table that is
- *   unique among all other fields.
- */
-function _field_sql_storage_indexname($name, $index) {
-  return $name . '_' . $index;
-}
-
-/**
- * Returns the database schema for a field.
- *
- * This may contain one or more tables. Each table will contain the columns
- * relevant for the specified field. Leave the $field's 'columns' and 'indexes'
- * keys empty to get only the base schema.
- *
- * @param $field
- *   The field structure for which to generate a database schema.
- * @return
- *   One or more tables representing the schema for the field.
- */
-function _field_sql_storage_schema($field) {
-  $deleted = $field['deleted'] ? 'deleted ' : '';
-  $current = array(
-    'description' => "Data storage for {$deleted}field {$field['id']} ({$field['field_name']})",
-    'fields' => array(
-      'entity_type' => array(
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'The entity type this data is attached to',
-      ),
-      'bundle' => array(
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
-      ),
-      'deleted' => array(
-        'type' => 'int',
-        'size' => 'tiny',
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => 'A boolean indicating whether this data item has been deleted'
-      ),
-      'entity_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The entity id this data is attached to',
-      ),
-      'revision_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
-      ),
-      // @todo Consider storing langcode as integer.
-      'langcode' => array(
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'The language code for this data item.',
-      ),
-      'delta' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The sequence number for this data item, used for multi-value fields',
-      ),
-    ),
-    'primary key' => array('entity_type', 'entity_id', 'deleted', 'delta', 'langcode'),
-    'indexes' => array(
-      'entity_type' => array('entity_type'),
-      'bundle' => array('bundle'),
-      'deleted' => array('deleted'),
-      'entity_id' => array('entity_id'),
-      'revision_id' => array('revision_id'),
-      'langcode' => array('langcode'),
-    ),
-  );
-
-  $schema = $field->getSchema();
-
-  // Add field columns.
-  foreach ($schema['columns'] as $column_name => $attributes) {
-    $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
-    $current['fields'][$real_name] = $attributes;
-  }
-
-  // Add indexes.
-  foreach ($schema['indexes'] as $index_name => $columns) {
-    $real_name = _field_sql_storage_indexname($field['field_name'], $index_name);
-    foreach ($columns as $column_name) {
-      // Indexes can be specified as either a column name or an array with
-      // column name and length. Allow for either case.
-      if (is_array($column_name)) {
-        $current['indexes'][$real_name][] = array(
-          _field_sql_storage_columnname($field['field_name'], $column_name[0]),
-          $column_name[1],
-        );
-      }
-      else {
-        $current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name);
-      }
-    }
-  }
-
-  // Add foreign keys.
-  foreach ($schema['foreign keys'] as $specifier => $specification) {
-    $real_name = _field_sql_storage_indexname($field['field_name'], $specifier);
-    $current['foreign keys'][$real_name]['table'] = $specification['table'];
-    foreach ($specification['columns'] as $column_name => $referenced) {
-      $sql_storage_column = _field_sql_storage_columnname($field['field_name'], $column_name);
-      $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
-    }
-  }
-
-  // Construct the revision table.
-  $revision = $current;
-  $revision['description'] = "Revision archive storage for {$deleted}field {$field['id']} ({$field['field_name']})";
-  $revision['primary key'] = array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'langcode');
-  $revision['fields']['revision_id']['not null'] = TRUE;
-  $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
-
-  return array(
-    _field_sql_storage_tablename($field) => $current,
-    _field_sql_storage_revision_tablename($field) => $revision,
-  );
-}
-
-/**
- * Implements hook_field_storage_create_field().
- */
-function field_sql_storage_field_storage_create_field($field) {
-  $schema = _field_sql_storage_schema($field);
-  foreach ($schema as $name => $table) {
-    db_create_table($name, $table);
-  }
-  // Do not rebuild the schema right now, since the field definition has not
-  // been saved yet. This will be done in hook_field_create_field().
-}
-
-/**
- * Implements hook_field_update_forbid().
- *
- * Forbids any field update that changes column definitions if there is any
- * data.
- */
-function field_sql_storage_field_update_forbid($field, $prior_field) {
-  if ($field->hasData() && $field['columns'] != $prior_field['columns']) {
-    throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data.");
-  }
-}
-
-/**
- * Implements hook_field_storage_update_field().
- */
-function field_sql_storage_field_storage_update_field($field, $prior_field) {
-  if (!$field->hasData()) {
-    // There is no data. Re-create the tables completely.
-
-    if (Database::getConnection()->supportsTransactionalDDL()) {
-      // If the database supports transactional DDL, we can go ahead and rely
-      // on it. If not, we will have to rollback manually if something fails.
-      $transaction = db_transaction();
-    }
-
-    try {
-      $prior_schema = _field_sql_storage_schema($prior_field);
-      foreach ($prior_schema as $name => $table) {
-        db_drop_table($name, $table);
-      }
-      $schema = _field_sql_storage_schema($field);
-      foreach ($schema as $name => $table) {
-        db_create_table($name, $table);
-      }
-    }
-    catch (Exception $e) {
-      if (Database::getConnection()->supportsTransactionalDDL()) {
-        $transaction->rollback();
-      }
-      else {
-        // Recreate tables.
-        $prior_schema = _field_sql_storage_schema($prior_field);
-        foreach ($prior_schema as $name => $table) {
-          if (!db_table_exists($name)) {
-            db_create_table($name, $table);
-          }
-        }
-      }
-      throw $e;
-    }
-  }
-  else {
-    // There is data, so there are no column changes. Drop all the
-    // prior indexes and create all the new ones, except for all the
-    // priors that exist unchanged.
-    $table = _field_sql_storage_tablename($prior_field);
-    $revision_table = _field_sql_storage_revision_tablename($prior_field);
-
-    $schema = $field->getSchema();
-    $prior_schema = $prior_field->getSchema();
-
-    foreach ($prior_schema['indexes'] as $name => $columns) {
-      if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) {
-        $real_name = _field_sql_storage_indexname($field['field_name'], $name);
-        db_drop_index($table, $real_name);
-        db_drop_index($revision_table, $real_name);
-      }
-    }
-    $table = _field_sql_storage_tablename($field);
-    $revision_table = _field_sql_storage_revision_tablename($field);
-    foreach ($schema['indexes'] as $name => $columns) {
-      if (!isset($prior_schema['indexes'][$name]) || $columns != $prior_schema['indexes'][$name]) {
-        $real_name = _field_sql_storage_indexname($field['field_name'], $name);
-        $real_columns = array();
-        foreach ($columns as $column_name) {
-          // Indexes can be specified as either a column name or an array with
-          // column name and length. Allow for either case.
-          if (is_array($column_name)) {
-            $real_columns[] = array(
-              _field_sql_storage_columnname($field['field_name'], $column_name[0]),
-              $column_name[1],
-            );
-          }
-          else {
-            $real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
-          }
-        }
-        db_add_index($table, $real_name, $real_columns);
-        db_add_index($revision_table, $real_name, $real_columns);
-      }
-    }
-  }
-  drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Implements hook_field_storage_delete_field().
- */
-function field_sql_storage_field_storage_delete_field($field) {
-  // Mark all data associated with the field for deletion.
-  $field['deleted'] = FALSE;
-  $table = _field_sql_storage_tablename($field);
-  $revision_table = _field_sql_storage_revision_tablename($field);
-  db_update($table)
-    ->fields(array('deleted' => 1))
-    ->execute();
-
-  // Move the table to a unique name while the table contents are being deleted.
-  $field['deleted'] = TRUE;
-  $new_table = _field_sql_storage_tablename($field);
-  $revision_new_table = _field_sql_storage_revision_tablename($field);
-  db_rename_table($table, $new_table);
-  db_rename_table($revision_table, $revision_new_table);
-  drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Implements hook_field_storage_load().
- */
-function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fields, $options) {
-  $load_current = $age == FIELD_LOAD_CURRENT;
-
-  foreach ($fields as $field_id => $ids) {
-    // By the time this hook runs, the relevant field definitions have been
-    // populated and cached in FieldInfo, so calling field_info_field_by_id()
-    // on each field individually is more efficient than loading all fields in
-    // memory upfront with field_info_field_by_ids().
-    $field = field_info_field_by_id($field_id);
-    $field_name = $field['field_name'];
-    $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
-
-    $query = db_select($table, 't')
-      ->fields('t')
-      ->condition('entity_type', $entity_type)
-      ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
-      ->condition('langcode', field_available_languages($entity_type, $field), 'IN')
-      ->orderBy('delta');
-
-    if (empty($options['deleted'])) {
-      $query->condition('deleted', 0);
-    }
-
-    $results = $query->execute();
-
-    $delta_count = array();
-    foreach ($results as $row) {
-      if (!isset($delta_count[$row->entity_id][$row->langcode])) {
-        $delta_count[$row->entity_id][$row->langcode] = 0;
-      }
-
-      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
-        $item = array();
-        // For each column declared by the field, populate the item
-        // from the prefixed database column.
-        foreach ($field['columns'] as $column => $attributes) {
-          $column_name = _field_sql_storage_columnname($field_name, $column);
-          // Unserialize the value if specified in the column schema.
-          $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name;
-        }
-
-        // Add the item to the field values for the entity.
-        $entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
-        $delta_count[$row->entity_id][$row->langcode]++;
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_field_storage_write().
- */
-function field_sql_storage_field_storage_write(EntityInterface $entity, $op, $fields) {
-  $vid = $entity->getRevisionId();
-  $id = $entity->id();
-  $bundle = $entity->bundle();
-  $entity_type = $entity->entityType();
-  if (!isset($vid)) {
-    $vid = $id;
-  }
-
-  foreach ($fields as $field_id) {
-    $field = field_info_field_by_id($field_id);
-    $field_name = $field['field_name'];
-    $table_name = _field_sql_storage_tablename($field);
-    $revision_name = _field_sql_storage_revision_tablename($field);
-
-    $all_langcodes = field_available_languages($entity_type, $field);
-    $field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
-
-    // Delete and insert, rather than update, in case a value was added.
-    if ($op == FIELD_STORAGE_UPDATE) {
-      // Delete language codes present in the incoming $entity->$field_name.
-      // Delete all language codes if $entity->$field_name is empty.
-      $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
-      if ($langcodes) {
-        // Only overwrite the field's base table if saving the default revision
-        // of an entity.
-        if ($entity->isDefaultRevision()) {
-          db_delete($table_name)
-            ->condition('entity_type', $entity_type)
-            ->condition('entity_id', $id)
-            ->condition('langcode', $langcodes, 'IN')
-            ->execute();
-        }
-        db_delete($revision_name)
-          ->condition('entity_type', $entity_type)
-          ->condition('entity_id', $id)
-          ->condition('revision_id', $vid)
-          ->condition('langcode', $langcodes, 'IN')
-          ->execute();
-      }
-    }
-
-    // Prepare the multi-insert query.
-    $do_insert = FALSE;
-    $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
-    foreach ($field['columns'] as $column => $attributes) {
-      $columns[] = _field_sql_storage_columnname($field_name, $column);
-    }
-    $query = db_insert($table_name)->fields($columns);
-    $revision_query = db_insert($revision_name)->fields($columns);
-
-    foreach ($field_langcodes as $langcode) {
-      $items = (array) $entity->{$field_name}[$langcode];
-      $delta_count = 0;
-      foreach ($items as $delta => $item) {
-        // We now know we have someting to insert.
-        $do_insert = TRUE;
-        $record = array(
-          'entity_type' => $entity_type,
-          'entity_id' => $id,
-          'revision_id' => $vid,
-          'bundle' => $bundle,
-          'delta' => $delta,
-          'langcode' => $langcode,
-        );
-        foreach ($field['columns'] as $column => $attributes) {
-          $column_name = _field_sql_storage_columnname($field_name, $column);
-          $value = isset($item[$column]) ? $item[$column] : NULL;
-          // Serialize the value if specified in the column schema.
-          $record[$column_name] = (!empty($attributes['serialize'])) ? serialize($value) : $value;
-        }
-        $query->values($record);
-        if (isset($vid)) {
-          $revision_query->values($record);
-        }
-
-        if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
-          break;
-        }
-      }
-    }
-
-    // Execute the query if we have values to insert.
-    if ($do_insert) {
-      // Only overwrite the field's base table if saving the default revision
-      // of an entity.
-      if ($entity->isDefaultRevision()) {
-        $query->execute();
-      }
-      $revision_query->execute();
-    }
-  }
-}
-
-/**
- * Implements hook_field_storage_delete().
- *
- * This function deletes data for all fields for an entity from the database.
- */
-function field_sql_storage_field_storage_delete(EntityInterface $entity, $fields) {
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    if (isset($fields[$instance['field_id']])) {
-      $field = field_info_field_by_id($instance['field_id']);
-      field_sql_storage_field_storage_purge($entity, $field, $instance);
-    }
-  }
-}
-
-/**
- * Implements hook_field_storage_purge().
- *
- * This function deletes data from the database for a single field on
- * an entity.
- */
-function field_sql_storage_field_storage_purge(EntityInterface $entity, $field, $instance) {
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_delete($table_name)
-    ->condition('entity_type', $entity->entityType())
-    ->condition('entity_id', $entity->id())
-    ->execute();
-  db_delete($revision_name)
-    ->condition('entity_type', $entity->entityType())
-    ->condition('entity_id', $entity->id())
-    ->execute();
-}
-
-/**
- * Implements hook_field_storage_delete_revision().
- *
- * This function actually deletes the data from the database.
- */
-function field_sql_storage_field_storage_delete_revision(EntityInterface $entity, $fields) {
-  $vid = $entity->getRevisionId();
-  if (isset($vid)) {
-    foreach ($fields as $field_id) {
-      $field = field_info_field_by_id($field_id);
-      $revision_name = _field_sql_storage_revision_tablename($field);
-      db_delete($revision_name)
-        ->condition('entity_type', $entity->entityType())
-        ->condition('entity_id', $entity->id())
-        ->condition('revision_id', $vid)
-        ->execute();
-    }
-  }
-}
-
-/**
- * Implements hook_field_storage_delete_instance().
- *
- * This function simply marks for deletion all data associated with the field.
- */
-function field_sql_storage_field_storage_delete_instance($instance) {
-  $field = field_info_field($instance['field_name']);
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_update($table_name)
-    ->fields(array('deleted' => 1))
-    ->condition('entity_type', $instance['entity_type'])
-    ->condition('bundle', $instance['bundle'])
-    ->execute();
-  db_update($revision_name)
-    ->fields(array('deleted' => 1))
-    ->condition('entity_type', $instance['entity_type'])
-    ->condition('bundle', $instance['bundle'])
-    ->execute();
-}
-
-/**
- * Implements hook_entity_bundle_rename().
- */
-function field_sql_storage_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
-  // We need to account for deleted or inactive fields and instances.
-  $instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
-  foreach ($instances as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    if ($field['storage']['type'] == 'field_sql_storage') {
-      $table_name = _field_sql_storage_tablename($field);
-      $revision_name = _field_sql_storage_revision_tablename($field);
-      db_update($table_name)
-        ->fields(array('bundle' => $bundle_new))
-        ->condition('entity_type', $entity_type)
-        ->condition('bundle', $bundle_old)
-        ->execute();
-      db_update($revision_name)
-        ->fields(array('bundle' => $bundle_new))
-        ->condition('entity_type', $entity_type)
-        ->condition('bundle', $bundle_old)
-        ->execute();
-    }
-  }
-}
-
-/**
- * Implements hook_field_storage_purge_field().
- *
- * All field data items and instances have already been purged, so all that is
- * left is to delete the table.
- */
-function field_sql_storage_field_storage_purge_field($field) {
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_drop_table($table_name);
-  db_drop_table($revision_name);
-}
-
-/**
- * Implements hook_field_storage_details().
- */
-function field_sql_storage_field_storage_details($field) {
-  $details = array();
-  if (!empty($field['columns'])) {
-     // Add field columns.
-    foreach ($field['columns'] as $column_name => $attributes) {
-      $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
-      $columns[$column_name] = $real_name;
-    }
-    return array(
-      'sql' => array(
-        FIELD_LOAD_CURRENT => array(
-          _field_sql_storage_tablename($field) => $columns,
-        ),
-        FIELD_LOAD_REVISION => array(
-          _field_sql_storage_revision_tablename($field) => $columns,
-        ),
-      ),
-    );
-  }
-}
diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Tests/FieldSqlStorageTest.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Tests/FieldSqlStorageTest.php
deleted file mode 100644
index fce0a52..0000000
--- a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Tests/FieldSqlStorageTest.php
+++ /dev/null
@@ -1,539 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\field_sql_storage\FieldSqlStorageTest.
- */
-
-namespace Drupal\field_sql_storage\Tests;
-
-use Drupal\Core\Database\Database;
-use Drupal\Core\Language\Language;
-use Drupal\field\FieldException;
-use Drupal\system\Tests\Entity\EntityUnitTestBase;
-use PDO;
-
-/**
- * Tests field storage.
- *
- * Field_sql_storage.module implements the default back-end storage plugin
- * for the Field Strage API.
- */
-class FieldSqlStorageTest extends EntityUnitTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('field_sql_storage', 'field', 'field_test', 'text', 'number', 'entity_test');
-
-  /**
-   * The name of the created field.
-   *
-   * @var string
-   */
-  protected $field_name;
-
-  /**
-   * A field to use in this class.
-   *
-   * @var \Drupal\field\Plugin\Core\Entity\Field
-   */
-  protected $field;
-
-  /**
-   * A field instance to use in this test class.
-   *
-   * @var \Drupal\field\Plugin\Core\Entity\FieldInstance
-   */
-  protected $instance;
-
-  /**
-   * Name of the revision table of the field.
-   *
-   * @var string
-   */
-  protected $revision_table;
-
-  public static function getInfo() {
-    return array(
-      'name'  => 'Field SQL Storage tests',
-      'description'  => "Test Field SQL Storage module.",
-      'group' => 'Field API'
-    );
-  }
-
-  function setUp() {
-    parent::setUp();
-    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
-    $entity_type = 'entity_test_rev';
-
-    $this->field_name = strtolower($this->randomName());
-    $this->field = entity_create('field_entity', array(
-      'field_name' => $this->field_name,
-      'type' => 'test_field',
-      'cardinality' => 4,
-    ));
-    $this->field->save();
-    $this->instance = entity_create('field_instance', array(
-      'field_name' => $this->field_name,
-      'entity_type' => $entity_type,
-      'bundle' => $entity_type
-    ));
-    $this->instance->save();
-    $this->table = _field_sql_storage_tablename($this->field);
-    $this->revision_table = _field_sql_storage_revision_tablename($this->field);
-  }
-
-  /**
-   * Uses the mysql tables and records to verify
-   * field_load_revision works correctly.
-   */
-  function testFieldAttachLoad() {
-    $entity_type = 'entity_test_rev';
-    $eid = 0;
-    $langcode = Language::LANGCODE_NOT_SPECIFIED;
-
-    $columns = array('entity_type', 'entity_id', 'revision_id', 'delta', 'langcode', $this->field_name . '_value');
-
-    // Insert data for four revisions to the field revisions table
-    $query = db_insert($this->revision_table)->fields($columns);
-    for ($evid = 0; $evid < 4; ++$evid) {
-      $values[$evid] = array();
-      // Note: we insert one extra value ('<=' instead of '<').
-      for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
-        $value = mt_rand(1, 127);
-        $values[$evid][] = $value;
-        $query->values(array($entity_type, $eid, $evid, $delta, $langcode, $value));
-      }
-    }
-    $query->execute();
-
-    // Insert data for the "most current revision" into the field table
-    $query = db_insert($this->table)->fields($columns);
-    foreach ($values[0] as $delta => $value) {
-      $query->values(array($entity_type, $eid, 0, $delta, $langcode, $value));
-    }
-    $query->execute();
-
-    // Load the "most current revision"
-    $entity = entity_create($entity_type, array(
-      'id' => 0,
-      'revision_id' => 0,
-    ));
-    field_attach_load($entity_type, array($eid => $entity));
-    foreach ($values[0] as $delta => $value) {
-      if ($delta < $this->field['cardinality']) {
-        $this->assertEqual($entity->{$this->field_name}[$delta]->value, $value, "Value $delta is loaded correctly for current revision");
-      }
-      else {
-        $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}), "No extraneous value gets loaded for current revision.");
-      }
-    }
-
-    // Load every revision
-    for ($evid = 0; $evid < 4; ++$evid) {
-      $entity = entity_create($entity_type, array(
-        'id' => $eid,
-        'revision_id' => $evid,
-      ));
-      field_attach_load_revision($entity_type, array($eid => $entity));
-      foreach ($values[$evid] as $delta => $value) {
-        if ($delta < $this->field['cardinality']) {
-          $this->assertEqual($entity->{$this->field_name}[$delta]->value, $value, "Value $delta for revision $evid is loaded correctly");
-        }
-        else {
-          $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}), "No extraneous value gets loaded for revision $evid.");
-        }
-      }
-    }
-
-    // Add a translation in an unavailable language code and verify it is not
-    // loaded.
-    $eid = $evid = 1;
-    $unavailable_langcode = 'xx';
-    $entity = entity_create($entity_type, array(
-      'id' => $eid,
-      'revision_id' => $evid,
-    ));
-    $values = array($entity_type, $eid, $evid, 0, $unavailable_langcode, mt_rand(1, 127));
-    db_insert($this->table)->fields($columns)->values($values)->execute();
-    db_insert($this->revision_table)->fields($columns)->values($values)->execute();
-    field_attach_load($entity_type, array($eid => $entity));
-    $this->assertFalse(array_key_exists($unavailable_langcode, $entity->{$this->field_name}), 'Field translation in an unavailable language ignored');
-  }
-
-  /**
-   * Reads mysql to verify correct data is
-   * written when using insert and update.
-   */
-  function testFieldAttachInsertAndUpdate() {
-    $entity_type = 'entity_test_rev';
-    $entity = entity_create($entity_type, array(
-      'id' => 0,
-      'revision_id' => 0,
-    ));
-    $langcode = Language::LANGCODE_NOT_SPECIFIED;
-
-    // Test insert.
-    $values = array();
-    // Note: we try to insert one extra value ('<=' instead of '<').
-    // TODO : test empty values filtering and "compression" (store consecutive deltas).
-    for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
-      $values[$delta]['value'] = mt_rand(1, 127);
-    }
-    $entity->{$this->field_name} = $rev_values[0] = $values;
-    field_attach_insert($entity);
-
-    $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
-    foreach ($values as $delta => $value) {
-      if ($delta < $this->field['cardinality']) {
-        $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Value $delta is inserted correctly"));
-      }
-      else {
-        $this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets inserted.");
-      }
-    }
-
-    // Test update.
-    $entity = entity_create($entity_type, array(
-      'id' => 0,
-      'revision_id' => 1,
-    ));
-    $values = array();
-    // Note: we try to update one extra value ('<=' instead of '<').
-    for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
-      $values[$delta]['value'] = mt_rand(1, 127);
-    }
-    $rev_values[1] = $values;
-    $entity->{$this->field_name}->setValue($values);
-    field_attach_update($entity);
-    $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
-    foreach ($values as $delta => $value) {
-      if ($delta < $this->field['cardinality']) {
-        $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Value $delta is updated correctly"));
-      }
-      else {
-        $this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets updated.");
-      }
-    }
-
-    // Check that data for both revisions are in the revision table.
-    // We make sure each value is stored correctly, then unset it.
-    // When an entire revision's values are unset (remembering that we
-    // put one extra value in $values per revision), unset the entire
-    // revision. Then, if $rev_values is empty at the end, all
-    // revision data was found.
-    $results = db_select($this->revision_table, 't')->fields('t')->execute();
-    foreach ($results as $row) {
-      $this->assertEqual($row->{$this->field_name . '_value'}, $rev_values[$row->revision_id][$row->delta]['value'], "Value {$row->delta} for revision {$row->revision_id} stored correctly");
-      unset($rev_values[$row->revision_id][$row->delta]);
-      if (count($rev_values[$row->revision_id]) == 1) {
-        unset($rev_values[$row->revision_id]);
-      }
-    }
-    $this->assertTrue(empty($rev_values), "All values for all revisions are stored in revision table {$this->revision_table}");
-
-    // Check that update leaves the field data untouched if
-    // $entity->{$field_name} is absent.
-    unset($entity->{$this->field_name});
-    field_attach_update($entity);
-    $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
-    foreach ($values as $delta => $value) {
-      if ($delta < $this->field->cardinality) {
-        $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], t("Update with no field_name entry leaves value $delta untouched"));
-      }
-    }
-
-    // Check that update with an empty $entity->$field_name empties the field.
-    $entity->getBCEntity()->{$this->field_name} = NULL;
-    field_attach_update($entity);
-    $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
-    $this->assertEqual(count($rows), 0, t("Update with an empty field_name entry empties the field."));
-  }
-
-  /**
-   * Tests insert and update with empty and NULL fields.
-   */
-  function testFieldAttachSaveMissingData() {
-    $entity_type = 'entity_test_rev';
-    $entity = entity_create($entity_type, array(
-      'id' => 0,
-      'revision_id' => 0,
-    ));
-
-    // Insert: Field is missing
-    field_attach_insert($entity);
-    $count = db_select($this->table)
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertEqual($count, 0, 'Missing field results in no inserts');
-
-    // Insert: Field is NULL
-    $entity->{$this->field_name} = NULL;
-    field_attach_insert($entity);
-    $count = db_select($this->table)
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertEqual($count, 0, 'NULL field results in no inserts');
-
-    // Add some real data
-    $entity->{$this->field_name}->value = 1;
-    field_attach_insert($entity);
-    $count = db_select($this->table)
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertEqual($count, 1, 'Field data saved');
-
-    // Update: Field is NULL. Data should be wiped.
-    $entity->getBCEntity()->{$this->field_name} = NULL;
-    field_attach_update($entity);
-    $count = db_select($this->table)
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertEqual($count, 0, 'NULL field leaves no data in table');
-
-    // Add a translation in an unavailable language.
-    $unavailable_langcode = 'xx';
-    db_insert($this->table)
-      ->fields(array('entity_type', 'bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode'))
-      ->values(array($entity_type, $this->instance->bundle, 0, 0, 0, 0, $unavailable_langcode))
-      ->execute();
-    $count = db_select($this->table)
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertEqual($count, 1, 'Field translation in an unavailable language saved.');
-
-    // Again add some real data.
-    $entity->{$this->field_name}->value = 1;
-    field_attach_insert($entity);
-    $count = db_select($this->table)
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertEqual($count, 2, 'Field data saved.');
-
-    // Update: Field translation is missing but field is not empty. Translation
-    // data should survive.
-    unset($entity->{$this->field_name});
-    field_attach_update($entity);
-    $count = db_select($this->table)
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertEqual($count, 2, 'Missing field translation leaves data in table.');
-
-    // Update: Field translation is NULL but field is not empty. Translation
-    // data should be wiped.
-    $entity->getBCEntity()->{$this->field_name}[Language::LANGCODE_NOT_SPECIFIED] = NULL;
-    field_attach_update($entity);
-    $count = db_select($this->table)
-      ->countQuery()
-      ->execute()
-      ->fetchField();
-    $this->assertEqual($count, 1, 'NULL field translation is wiped.');
-  }
-
-  /**
-   * Test trying to update a field with data.
-   */
-  function testUpdateFieldSchemaWithData() {
-    $entity_type = 'entity_test_rev';
-    // Create a decimal 5.2 field and add some data.
-    $field = entity_create('field_entity', array(
-      'field_name' => 'decimal52',
-      'type' => 'number_decimal',
-      'settings' => array('precision' => 5, 'scale' => 2),
-    ));
-    $field->save();
-    $instance = entity_create('field_instance', array(
-      'field_name' => 'decimal52',
-      'entity_type' => $entity_type,
-      'bundle' => $entity_type,
-    ));
-    $instance->save();
-    $entity = entity_create($entity_type, array(
-      'id' => 0,
-      'revision_id' => 0,
-    ));
-    $entity->decimal52->value = '1.235';
-    $entity->save();
-
-    // Attempt to update the field in a way that would work without data.
-    $field->settings['scale'] = 3;
-    try {
-      $field->save();
-      $this->fail(t('Cannot update field schema with data.'));
-    }
-    catch (FieldException $e) {
-      $this->pass(t('Cannot update field schema with data.'));
-    }
-  }
-
-  /**
-   * Test that failure to create fields is handled gracefully.
-   */
-  function testFieldUpdateFailure() {
-    // Create a text field.
-    $field = entity_create('field_entity', array(
-      'field_name' => 'test_text',
-      'type' => 'text',
-      'settings' => array('max_length' => 255),
-    ));
-    $field->save();
-
-    // Attempt to update the field in a way that would break the storage.
-    $prior_field = $field;
-    $field->settings['max_length'] = -1;
-    try {
-      $field->save();
-      $this->fail(t('Update succeeded.'));
-    }
-    catch (\Exception $e) {
-      $this->pass(t('Update properly failed.'));
-    }
-
-    // Ensure that the field tables are still there.
-    foreach (_field_sql_storage_schema($prior_field) as $table_name => $table_info) {
-      $this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name)));
-    }
-  }
-
-  /**
-   * Test adding and removing indexes while data is present.
-   */
-  function testFieldUpdateIndexesWithData() {
-    // Create a decimal field.
-    $field_name = 'testfield';
-    $entity_type = 'entity_test_rev';
-    $field = entity_create('field_entity', array(
-      'field_name' => $field_name,
-      'type' => 'text'));
-    $field->save();
-    $instance = entity_create('field_instance', array(
-      'field_name' => $field_name,
-      'entity_type' => $entity_type,
-      'bundle' => $entity_type,
-    ));
-    $instance->save();
-    $tables = array(_field_sql_storage_tablename($field), _field_sql_storage_revision_tablename($field));
-
-    // Verify the indexes we will create do not exist yet.
-    foreach ($tables as $table) {
-      $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), t("No index named value exists in $table"));
-      $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), t("No index named value_format exists in $table"));
-    }
-
-    // Add data so the table cannot be dropped.
-    $entity = entity_create($entity_type, array(
-      'id' => 1,
-      'revision_id' => 1,
-    ));
-    $entity->$field_name->value = 'field data';
-    $entity->enforceIsNew();
-    $entity->save();
-
-    // Add an index.
-    $field->indexes = array('value' => array(array('value', 255)));
-    $field->save();
-    foreach ($tables as $table) {
-      $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value created in $table"));
-    }
-
-    // Add a different index, removing the existing custom one.
-    $field->indexes = array('value_format' => array(array('value', 127), array('format', 127)));
-    $field->save();
-    foreach ($tables as $table) {
-      $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), t("Index on value_format created in $table"));
-      $this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value removed in $table"));
-    }
-
-    // Verify that the tables were not dropped.
-    $entity = entity_create($entity_type, array(
-      'id' => 1,
-      'revision_id' => 1,
-    ));
-    field_attach_load($entity_type, array(1 => $entity));
-    $this->assertEqual($entity->$field_name->value, 'field data', t("Index changes performed without dropping the tables"));
-  }
-
-  /**
-   * Test the storage details.
-   */
-  function testFieldStorageDetails() {
-    $current = _field_sql_storage_tablename($this->field);
-    $revision = _field_sql_storage_revision_tablename($this->field);
-
-    // Retrieve the field and instance with field_info so the storage details are attached.
-    $field = field_info_field($this->field_name);
-    $instance = field_info_instance($this->instance->entity_type, $field->id(), $this->instance->bundle);
-
-    // The storage details are indexed by a storage engine type.
-    $storage_details = $field->getStorageDetails();
-    $this->assertTrue(array_key_exists('sql', $storage_details), 'The storage type is SQL.');
-
-    // The SQL details are indexed by table name.
-    $details = $storage_details['sql'];
-    $this->assertTrue(array_key_exists($current, $details[FIELD_LOAD_CURRENT]), 'Table name is available in the instance array.');
-    $this->assertTrue(array_key_exists($revision, $details[FIELD_LOAD_REVISION]), 'Revision table name is available in the instance array.');
-
-    // Test current and revision storage details together because the columns
-    // are the same.
-    $schema = $this->field->getSchema();
-    foreach ($schema['columns'] as $column_name => $attributes) {
-      $storage_column_name = _field_sql_storage_columnname($this->field_name, $column_name);
-      $this->assertEqual($details[FIELD_LOAD_CURRENT][$current][$column_name], $storage_column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $current)));
-      $this->assertEqual($details[FIELD_LOAD_REVISION][$revision][$column_name], $storage_column_name, t('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $revision)));
-    }
-  }
-
-  /**
-   * Test foreign key support.
-   */
-  function testFieldSqlStorageForeignKeys() {
-    // Create a 'shape' field, with a configurable foreign key (see
-    // field_test_field_schema()).
-    $field_name = 'testfield';
-    $foreign_key_name = 'shape';
-    $field = entity_create('field_entity', array(
-      'field_name' => $field_name,
-      'type' => 'shape',
-      'settings' => array('foreign_key_name' => $foreign_key_name),
-    ));
-    $field->save();
-    // Get the field schema.
-    $schema = $field->getSchema();
-
-    // Retrieve the field definition and check that the foreign key is in place.
-    $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name preserved through CRUD');
-    $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name preserved through CRUD');
-
-    // Update the field settings, it should update the foreign key definition too.
-    $foreign_key_name = 'color';
-    $field->settings['foreign_key_name'] = $foreign_key_name;
-    $field->save();
-    // Reload the field schema after the update.
-    $schema = $field->getSchema();
-
-    // Retrieve the field definition and check that the foreign key is in place.
-    $field = field_info_field($field_name);
-    $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update');
-    $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update');
-
-    // Verify the SQL schema.
-    $schemas = _field_sql_storage_schema($field);
-    $schema = $schemas[_field_sql_storage_tablename($field)];
-    $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema');
-    $foreign_key = reset($schema['foreign keys']);
-    $foreign_key_column = _field_sql_storage_columnname($field['field_name'], $foreign_key_name);
-    $this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema');
-    $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema');
-  }
-
-}
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index 8a8674c..187bf46 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -216,7 +216,7 @@ function field_ui_instance_load($field_name, $entity_type, $bundle_name, $bundle
     $bundle_name = field_extract_bundle($entity_type, $bundle);
   }
   // Check whether the field exists at all.
-  if ($field = field_info_field($field_name)) {
+  if ($field = field_info_field($entity_type, $field_name)) {
     // Only return the field if a field instance exists for the given entity
     // type and bundle.
     if ($instance = field_info_instance($entity_type, $field_name, $bundle_name)) {
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php
index 514e966..075ffe8 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldListController.php
@@ -12,7 +12,6 @@
 use Drupal\Core\Entity\EntityManager;
 use Drupal\Core\Entity\Field\FieldTypePluginManager;
 use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\field\FieldInfo;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -28,13 +27,6 @@ class FieldListController extends ConfigEntityListController {
   protected $fieldTypes;
 
   /**
-   * An array of field data.
-   *
-   * @var array
-   */
-  protected $fieldInfo;
-
-  /**
    * The entity manager.
    *
    * @var \Drupal\Core\Entity\EntityManager
@@ -71,10 +63,9 @@ class FieldListController extends ConfigEntityListController {
    * @param \Drupal\Core\Entity\Field\FieldTypePluginManager $field_type_manager
    *   The 'field type' plugin manager.
    */
-  public function __construct($entity_type, array $entity_info, EntityManager $entity_manager, ModuleHandlerInterface $module_handler, FieldInfo $field_info, FieldTypePluginManager $field_type_manager) {
+  public function __construct($entity_type, array $entity_info, EntityManager $entity_manager, ModuleHandlerInterface $module_handler, FieldTypePluginManager $field_type_manager) {
     parent::__construct($entity_type, $entity_info, $entity_manager->getStorageController($entity_type), $module_handler);
 
-    $this->fieldInfo = $field_info->getFieldMap();
     $this->entityManager = $entity_manager;
     $this->bundles = entity_get_bundles();
     $this->fieldTypeManager = $field_type_manager;
@@ -90,7 +81,6 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_info,
       $container->get('plugin.manager.entity'),
       $container->get('module_handler'),
-      $container->get('field.info'),
       $container->get('plugin.manager.entity.field.field_type')
     );
   }
@@ -111,24 +101,22 @@ public function buildHeader() {
   /**
    * {@inheritdoc}
    */
-  public function buildRow(EntityInterface $entity) {
-    if ($entity->locked) {
+  public function buildRow(EntityInterface $field) {
+    if ($field->locked) {
       $row['class'] = array('menu-disabled');
-      $row['data']['id'] =  t('@field_name (Locked)', array('@field_name' => $entity->id()));
+      $row['data']['id'] =  t('@field_name (Locked)', array('@field_name' => $field->name));
     }
     else {
-      $row['data']['id'] = $entity->id();
+      $row['data']['id'] = $field->name;
     }
 
-    $field_type = $this->fieldTypes[$entity->getFieldType()];
+    $field_type = $this->fieldTypes[$field->type];
     $row['data']['type'] = t('@type (module: @module)', array('@type' => $field_type['label'], '@module' => $field_type['provider']));
 
     $usage = array();
-    foreach($this->fieldInfo[$entity->id()]['bundles'] as $entity_type => $field_bundles) {
-      foreach($field_bundles as $bundle) {
-        $admin_path = $this->entityManager->getAdminPath($entity_type, $bundle);
-        $usage[] = $admin_path ? l($this->bundles[$entity_type][$bundle]['label'], $admin_path . '/fields') : $this->bundles[$entity_type][$bundle]['label'];
-      }
+    foreach ($field->getBundles() as $bundle) {
+      $admin_path = $this->entityManager->getAdminPath($field->entity_type, $bundle);
+      $usage[] = $admin_path ? l($this->bundles[$field->entity_type][$bundle]['label'], $admin_path . '/fields') : $this->bundles[$field->entity_type][$bundle]['label'];
     }
     $row['data']['usage'] = implode(', ', $usage);
     return $row;
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
index d2208a1..7f0b003 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
@@ -118,7 +118,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL,
 
     // Fields.
     foreach ($instances as $name => $instance) {
-      $field = field_info_field($instance['field_name']);
+      $field = $instance->getField();
       $admin_field_path = $this->adminPath . '/fields/' . $instance->id();
       $table[$name] = array(
         '#attributes' => array(
@@ -202,7 +202,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL,
           '#description' => t('A unique machine-readable name containing letters, numbers, and underscores.'),
           // Calculate characters depending on the length of the field prefix
           // setting. Maximum length is 32.
-          '#maxlength' => Field::ID_MAX_LENGTH - strlen($field_prefix),
+          '#maxlength' => Field::NAME_MAX_LENGTH - strlen($field_prefix),
           '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
           '#machine_name' => array(
             'source' => array('fields', $name, 'label'),
@@ -382,12 +382,13 @@ public function submitForm(array &$form, array &$form_state) {
       $values = $form_values['_add_new_field'];
 
       $field = array(
-        'field_name' => $values['field_name'],
+        'name' => $values['field_name'],
+        'entity_type' => $this->entity_type,
         'type' => $values['type'],
         'translatable' => $values['translatable'],
       );
       $instance = array(
-        'field_name' => $field['field_name'],
+        'field_name' => $values['field_name'],
         'entity_type' => $this->entity_type,
         'bundle' => $this->bundle,
         'label' => $values['label'],
@@ -403,14 +404,14 @@ public function submitForm(array &$form, array &$form_state) {
         // default widget and settings). It stays hidden for other form modes
         // until it is explicitly configured.
         entity_get_form_display($this->entity_type, $this->bundle, 'default')
-          ->setComponent($field['field_name'])
+          ->setComponent($values['field_name'])
           ->save();
 
         // Make sure the field is displayed in the 'default' view mode (using
         // default formatter and settings). It stays hidden for other view
         // modes until it is explicitly configured.
         entity_get_display($this->entity_type, $this->bundle, 'default')
-          ->setComponent($field['field_name'])
+          ->setComponent($values['field_name'])
           ->save();
 
         // Always show the field settings step, as the cardinality needs to be
@@ -419,7 +420,7 @@ public function submitForm(array &$form, array &$form_state) {
         $destinations[] = $this->adminPath . '/fields/' . $new_instance->id();
 
         // Store new field information for any additional submit handlers.
-        $form_state['fields_added']['_add_new_field'] = $field['field_name'];
+        $form_state['fields_added']['_add_new_field'] = $values['field_name'];
       }
       catch (\Exception $e) {
         drupal_set_message(t('There was a problem creating field %label: !message', array('%label' => $instance['label'], '!message' => $e->getMessage())), 'error');
@@ -429,7 +430,7 @@ public function submitForm(array &$form, array &$form_state) {
     // Re-use existing field.
     if (!empty($form_values['_add_existing_field']['field_name'])) {
       $values = $form_values['_add_existing_field'];
-      $field = field_info_field($values['field_name']);
+      $field = field_info_field($this->entity_type, $values['field_name']);
       if (!empty($field['locked'])) {
         drupal_set_message(t('The field %label cannot be added because it is locked.', array('%label' => $values['label'])), 'error');
       }
@@ -490,37 +491,42 @@ public function submitForm(array &$form, array &$form_state) {
    *   An array of existing fields keyed by field name.
    */
   protected function getExistingFieldOptions() {
-    $info = array();
-    $field_types = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions();
-
-    foreach (field_info_instances() as $existing_entity_type => $bundles) {
-      foreach ($bundles as $existing_bundle => $instances) {
-        // No need to look in the current bundle.
-        if (!($existing_bundle == $this->bundle && $existing_entity_type == $this->entity_type)) {
-          foreach ($instances as $instance) {
-            $field = field_info_field($instance['field_name']);
-            // Don't show
-            // - locked fields,
-            // - fields already in the current bundle,
-            // - fields that cannot be added to the entity type,
-            // - fields that should not be added via user interface.
-
-            if (empty($field['locked'])
-              && !field_info_instance($this->entity_type, $field['field_name'], $this->bundle)
-              && (empty($field['entity_types']) || in_array($this->entity_type, $field['entity_types']))
-              && empty($field_types[$field['type']]['no_ui'])) {
-              $info[$instance['field_name']] = array(
-                'type' => $field['type'],
-                'type_label' => $field_types[$field['type']]['label'],
-                'field' => $field['field_name'],
-                'label' => $instance['label'],
-              );
-            }
-          }
+    $options = array();
+
+    // Collect candidate field instances: all instances of fields for this
+    // entity type that are not already present in the current bundle.
+    $field_map = field_info_field_map();
+    $instance_ids = array();
+    if (!empty($field_map[$this->entity_type])) {
+      foreach ($field_map[$this->entity_type] as $field_name => $data) {
+        if (!in_array($this->bundle, $data['bundles'])) {
+          $bundle = reset($data['bundles']);
+          $instance_ids[] = $this->entity_type . '.' . $bundle . '.' . $field_name;
+        }
+      }
+    }
+
+    // Load the instances and build the list of options.
+    if ($instance_ids) {
+      $field_types = $this->fieldTypeManager->getDefinitions();
+      $instances = $this->entityManager->getStorageController('field_instance')->loadMultiple($instance_ids);
+      foreach ($instances as $instance) {
+        $field = $instance->getField();
+        // Do not show:
+        // - locked fields,
+        // - fields that should not be added via user interface.
+        if (empty($field['locked']) && empty($field_types[$field['type']]['no_ui'])) {
+          $options[$field->name] = array(
+            'type' => $field->type,
+            'type_label' => $field_types[$field->type]['label'],
+            'field' => $field->name,
+            'label' => $instance->label,
+          );
         }
       }
     }
-    return $info;
+
+    return $options;
   }
 
   /**
@@ -538,7 +544,7 @@ public function fieldNameExists($value) {
 
     // We need to check inactive fields as well, so we can't use
     // field_info_fields().
-    return (bool) field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE));
+    return (bool) field_read_fields(array('entity_type' => $this->entity_type, 'name' => $field_name), array('include_inactive' => TRUE));
   }
 
 }
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php
index f40e32a..f880cce 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php
@@ -174,7 +174,7 @@ public function submitForm(array &$form, array &$form_state) {
     unset($field_values['container']);
 
     // Merge incoming form values into the existing field.
-    $field = Field::fieldInfo()->getField($field_values['field_name']);
+    $field = $this->instance->getField();
     foreach ($field_values as $key => $value) {
       $field[$key] = $value;
     }
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
index 046e012..743d779 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php
@@ -198,7 +198,7 @@ public function validateForm(array &$form, array &$form_state) {
 
       // Extract the 'default value'.
       $items = $entity->getNGEntity()->{$field_name};
-      $entity_form_display->getRenderer($this->instance->getField()->id)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
+      $entity_form_display->getRenderer($this->instance->getField()->name)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
       $violations = $items->validate();
 
       // Report errors.
@@ -209,7 +209,7 @@ public function validateForm(array &$form, array &$form_state) {
         field_form_set_state($element['#parents'], $field_name, Language::LANGCODE_NOT_SPECIFIED, $form_state, $field_state);
 
         // Assign reported errors to the correct form element.
-        $entity_form_display->getRenderer($this->instance->getField()->id)->flagErrors($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
+        $entity_form_display->getRenderer($this->instance->getField()->name)->flagErrors($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
       }
     }
   }
@@ -228,7 +228,7 @@ public function submitForm(array &$form, array &$form_state) {
 
       // Extract field values.
       $items = $entity->getNGEntity()->{$field_name};
-      $entity_form_display->getRenderer($this->instance->getField()->id)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
+      $entity_form_display->getRenderer($this->instance->getField()->name)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
 
       $this->instance['default_value'] = $items->getValue() ?: NULL;
     }
@@ -296,11 +296,11 @@ protected function getDefaultValueWidget($field, array &$form, &$form_state) {
 
     // Insert the widget. Since we do not use the "official" instance definition,
     // the whole flow cannot use field_invoke_method().
-    $items = $entity->getNGEntity()->{$this->instance->getField()->id};
+    $items = $entity->getNGEntity()->{$this->instance->getField()->name};
     if (!empty($this->instance['default_value'])) {
       $items->setValue((array) $this->instance['default_value']);
     }
-    $element += $entity_form_display->getRenderer($this->instance->getField()->id)->form($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
+    $element += $entity_form_display->getRenderer($this->instance->getField()->name)->form($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
 
     return $element;
   }
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageFieldsTest.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageFieldsTest.php
index 69be9ec..9d957c5 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageFieldsTest.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/ManageFieldsTest.php
@@ -42,7 +42,8 @@ function setUp() {
     $vocabulary->save();
 
     $field = array(
-      'field_name' => 'field_' . $vocabulary->id(),
+      'name' => 'field_' . $vocabulary->id(),
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
     );
     entity_create('field_entity', $field)->save();
@@ -116,13 +117,6 @@ function createField() {
       'fields[_add_new_field][field_name]' => $this->field_name_input,
     );
     $this->fieldUIAddNewField('admin/structure/types/manage/' . $this->type, $edit);
-
-    // Assert the field appears in the "re-use existing field" section for
-    // different entity types; e.g. if a field was added in a node entity, it
-    // should also appear in the 'taxonomy term' entity.
-    $vocabulary = entity_load('taxonomy_vocabulary', 'tags');
-    $this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/fields');
-    $this->assertTrue($this->xpath('//select[@name="fields[_add_existing_field][field_name]"]//option[@value="' . $this->field_name . '"]'), 'Existing field was found in taxonomy term fields.');
   }
 
   /**
@@ -162,9 +156,8 @@ function addExistingField() {
     $this->drupalGet('admin/structure/types/manage/page/fields');
     $this->assertRaw(t('Re-use existing field'), '"Re-use existing field" was found.');
 
-    // Check that the list of options respects entity type restrictions on
-    // fields. The 'comment' field is restricted to the 'comment' entity type
-    // and should not appear in the list.
+    // Check that fields of other entity types (here, the 'comment_body' field)
+    // do not show up in the "Re-use existing field" list.
     $this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value="comment"]'), 'The list of options respects entity type restrictions.');
 
     // Add a new field based on an existing field.
@@ -231,7 +224,7 @@ function assertFieldSettings($bundle, $field_name, $string = 'dummy test string'
     // Reset the fields info.
     field_info_cache_clear();
     // Assert field settings.
-    $field = field_info_field($field_name);
+    $field = field_info_field($entity_type, $field_name);
     $this->assertTrue($field['settings']['test_field_setting'] == $string, 'Field settings were found.');
 
     // Assert instance settings.
@@ -276,7 +269,8 @@ function testDefaultValue() {
     // Create a test field and instance.
     $field_name = 'test';
     entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'node',
       'type' => 'test_field'
     ))->save();
     $instance = entity_create('field_instance', array(
@@ -366,7 +360,7 @@ function testDeleteField() {
     // Check that the field instance was deleted.
     $this->assertNull(field_info_instance('node', $this->field_name, $this->type), 'Field instance was deleted.');
     // Check that the field was not deleted
-    $this->assertNotNull(field_info_field($this->field_name), 'Field was not deleted.');
+    $this->assertNotNull(field_info_field('node', $this->field_name), 'Field was not deleted.');
 
     // Delete the second instance.
     $this->fieldUIDeleteField($bundle_path2, "node.$type_name2.$this->field_name", $this->field_label, $type_name2);
@@ -376,7 +370,7 @@ function testDeleteField() {
     // Check that the field instance was deleted.
     $this->assertNull(field_info_instance('node', $this->field_name, $type_name2), 'Field instance was deleted.');
     // Check that the field was deleted too.
-    $this->assertNull(field_info_field($this->field_name), 'Field was deleted.');
+    $this->assertNull(field_info_field('node', $this->field_name), 'Field was deleted.');
   }
 
   /**
@@ -386,7 +380,8 @@ function testLockedField() {
     // Create a locked field and attach it to a bundle. We need to do this
     // programatically as there's no way to create a locked field through UI.
     $field = entity_create('field_entity', array(
-      'field_name' => strtolower($this->randomName(8)),
+      'name' => strtolower($this->randomName(8)),
+      'entity_type' => 'node',
       'type' => 'test_field',
       'cardinality' => 1,
       'locked' => TRUE
@@ -405,11 +400,11 @@ function testLockedField() {
 
     // Check that the links for edit and delete are not present.
     $this->drupalGet('admin/structure/types/manage/' . $this->type . '/fields');
-    $locked = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->id()));
+    $locked = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->name));
     $this->assertTrue(in_array('Locked', $locked), 'Field is marked as Locked in the UI');
-    $edit_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->id()));
+    $edit_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->name));
     $this->assertFalse(in_array('edit', $edit_link), 'Edit option for locked field is not present the UI');
-    $delete_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->id()));
+    $delete_link = $this->xpath('//tr[@id=:field_name]/td[4]', array(':field_name' => $field->name));
     $this->assertFalse(in_array('delete', $delete_link), 'Delete option for locked field is not present the UI');
   }
 
@@ -425,7 +420,11 @@ function testHiddenFields() {
 
     // Create a field and an instance programmatically.
     $field_name = 'hidden_test_field';
-    entity_create('field_entity', array('field_name' => $field_name, 'type' => $field_name))->save();
+    entity_create('field_entity', array(
+      'name' => $field_name,
+      'entity_type' => 'node',
+      'type' => $field_name,
+    ))->save();
     $instance = array(
       'field_name' => $field_name,
       'bundle' => $this->type,
@@ -501,7 +500,7 @@ function testDeleteTaxonomyField() {
     // Check that the field instance was deleted.
     $this->assertNull(field_info_instance('taxonomy_term', $this->field_name, 'tags'), 'Field instance was deleted.');
     // Check that the field was deleted too.
-    $this->assertNull(field_info_field($this->field_name), 'Field was deleted.');
+    $this->assertNull(field_info_field('taxonomy_term', $this->field_name), 'Field was deleted.');
   }
 
   /**
@@ -510,7 +509,8 @@ function testDeleteTaxonomyField() {
   function testHelpDescriptions() {
     // Create an image field
     entity_create('field_entity', array(
-      'field_name' => 'field_image',
+      'name' => 'field_image',
+      'entity_type' => 'node',
       'type' => 'image',
     ))->save();
 
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index 3e3806e..369b8c0 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -355,7 +355,7 @@ function file_field_widget_multiple_count_validate($element, &$form_state, $form
   array_pop($parents);
   $current = count(element_children(NestedArray::getValue($form, $parents))) - 1;
 
-  $field = field_info_field($element['#field_name']);
+  $field = field_info_field($element['#entity_type'], $element['#field_name']);
   $uploaded = count($values['fids']);
   $count = $uploaded + $current;
   if ($count > $field['cardinality']) {
diff --git a/core/modules/file/file.install b/core/modules/file/file.install
index 37b89ae..f229cad 100644
--- a/core/modules/file/file.install
+++ b/core/modules/file/file.install
@@ -5,6 +5,7 @@
  * Install, update and uninstall functions for File module.
  */
 
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
@@ -247,10 +248,10 @@ function file_update_dependencies() {
     'file' => 8001,
   );
 
-  // Convert the 'fid' column of file fields to 'target_id' after fields and
-  // instances have been moved to the config system.
+  // Convert the 'fid' column of file fields to 'target_id' after the field
+  // tables have been reorganized.
   $dependencies['file'][8003] = array(
-    'field' => 8003,
+    'field' => 8006,
   );
   return $dependencies;
 }
@@ -305,21 +306,21 @@ function file_update_8003() {
   foreach (config_get_storage_names_with_prefix('field.field.') as $config_name) {
     $field_config = Drupal::config($config_name);
     // Only update file fields that use the default SQL storage.
-    if (in_array($field_config->get('type'), array('file', 'image')) && $field_config->get('storage.type') == 'field_sql_storage') {
+    if (in_array($field_config->get('type'), array('file', 'image'))) {
       $field = new Field($field_config->get());
 
       $tables = array(
-        _field_sql_storage_tablename($field),
-        _field_sql_storage_revision_tablename($field),
+        DatabaseStorageController::_fieldTableName($field),
+        DatabaseStorageController::_fieldRevisionTableName($field),
       );
 
       foreach ($tables as $table_name) {
         // Skip fields which were created during the upgrade process.
-        if (!db_field_exists($table_name, $field->id() . '_fid')) {
+        if (!db_field_exists($table_name, $field->name . '_fid')) {
           continue 2;
         }
 
-        db_change_field($table_name, $field->id() . '_fid', $field->id() . '_target_id', array(
+        db_change_field($table_name, $field->name . '_fid', $field->name . '_target_id', array(
           'description' => 'The ID of the target entity.',
           'type' => 'int',
           'unsigned' => TRUE,
@@ -327,8 +328,8 @@ function file_update_8003() {
         ));
 
         // Change the index.
-        db_drop_index($table_name, $field->id() . '_fid');
-        db_add_index($table_name, $field->id() . '_target_id', array($field->id() . '_target_id'));
+        db_drop_index($table_name, $field->name . '_fid');
+        db_add_index($table_name, $field->name . '_target_id', array($field->name . '_target_id'));
       }
 
       // Update the indexes in field config as well.
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 018eef6..f32462d 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -659,7 +659,7 @@ function file_file_download($uri, $field_type = 'file') {
   foreach ($references as $field_name => $field_references) {
     foreach ($field_references as $entity_type => $entities) {
       foreach ($entities as $entity) {
-        $field = field_info_field($field_name);
+        $field = field_info_field($entity_type, $field_name);
         // Check if access to this field is not disallowed.
         if (!field_access('view', $field, $entity_type, $entity)) {
           $denied = TRUE;
@@ -1878,7 +1878,7 @@ function file_get_file_references(File $file, $field = NULL, $age = FIELD_LOAD_R
           // This contains the possible field names.
           $instances = field_info_instances($entity_type, $bundle);
           foreach ($instances as $field_name => $instance) {
-            $current_field = field_info_field($field_name);
+            $current_field = $instance->getField();
             // If this is the first time this field type is seen, check
             // whether it references files.
             if (!isset($field_columns[$current_field['type']])) {
@@ -1916,9 +1916,11 @@ function file_get_file_references(File $file, $field = NULL, $age = FIELD_LOAD_R
   // cache is very small so this will be very fast.
   if ($field || $field_type) {
     foreach ($return as $field_name => $data) {
-      $current_field = field_info_field($field_name);
-      if (($field_type && $current_field['type'] != $field_type) || ($field && $field['id'] != $current_field['id'])) {
-        unset($return[$field_name]);
+      foreach (array_keys($data) as $entity_type) {
+        $current_field = field_info_field($entity_type, $field_name);
+        if (($field_type && $current_field['type'] != $field_type) || ($field && $field['id'] != $current_field['id'])) {
+          unset($return[$field_name][$entity_type]);
+        }
       }
     }
   }
diff --git a/core/modules/file/file.views.inc b/core/modules/file/file.views.inc
index db18c46..501ce7c 100644
--- a/core/modules/file/file.views.inc
+++ b/core/modules/file/file.views.inc
@@ -7,6 +7,9 @@
  * @ingroup views_module_handlers
  */
 
+use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\field\FieldInterface;
+
 /**
  * Implements hook_views_data().
  */
@@ -460,7 +463,7 @@ function file_views_data() {
  *
  * @see field_views_field_default_views_data()
  */
-function file_field_views_data($field) {
+function file_field_views_data(FieldInterface $field) {
   $data = field_views_field_default_views_data($field);
   foreach ($data as $table_name => $table_data) {
     // Add the relationship only on the fid field.
@@ -481,38 +484,31 @@ function file_field_views_data($field) {
  *
  * Views integration to provide reverse relationships on file fields.
  */
-function file_field_views_data_views_data_alter(&$data, $field) {
-  foreach ($field['bundles'] as $entity_type => $bundles) {
-    $entity_info = entity_get_info($entity_type);
-    $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type;
+function file_field_views_data_views_data_alter(array &$data, FieldInterface $field) {
+  $entity_type = $field['entity_type'];
+  $entity_info = entity_get_info($entity_type);
+  $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type;
 
-    list($label, $all_labels) = field_views_field_label($field['field_name']);
-    $entity = $entity_info['label'];
-    if ($entity == t('Node')) {
-      $entity = t('Content');
-    }
+  list($label,) = field_views_field_label($entity_type, $field['field_name']);
+  $entity_label = ($entity_info['label'] == t('Node')) ? t('Content') : $entity_info['label'];
 
-    $data['file_managed'][$pseudo_field_name]['relationship'] = array(
-      'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)),
-      'help' => t('Relate each @entity with a @field set to the file.', array('@entity' => $entity, '@field' => $label)),
-      'id' => 'entity_reverse',
-      'field_name' => $field['field_name'],
-      'field table' => _field_sql_storage_tablename($field),
-      'field field' => $field['field_name'] . '_target_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(
-          'field' => 'entity_type',
-          'value' => $entity_type,
-        ),
-        1 => array(
-          'field' => 'deleted',
-          'value' => 0,
-          'numeric' => TRUE,
-        ),
+  $data['file_managed'][$pseudo_field_name]['relationship'] = array(
+    'title' => t('@entity using @field', array('@entity' => $entity_label, '@field' => $label)),
+    'help' => t('Relate each @entity with a @field set to the file.', array('@entity' => $entity_label, '@field' => $label)),
+    'id' => 'entity_reverse',
+    'field_name' => $field['field_name'],
+    'entity_type' => $field['entity_type'],
+    'field table' => DatabaseStorageController::_fieldTableName($field),
+    'field field' => $field['field_name'] . '_target_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(
+        'field' => 'deleted',
+        'value' => 0,
+        'numeric' => TRUE,
       ),
-    );
-  }
+    ),
+  );
 }
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php
index 4ef654d..0ed2f7f 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php
@@ -36,7 +36,7 @@ function testNodeDisplay() {
       'description_field' => '1',
     );
     $widget_settings = array();
-    $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings);
+    $this->createFileField($field_name, 'node', $type_name, $field_settings, $instance_settings, $widget_settings);
 
     // Create a new node *without* the file field set, and check that the field
     // is not shown for each node display.
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php
index f2c45b6..3fa22c4 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php
@@ -27,7 +27,7 @@ public static function getInfo() {
   function testUploadPath() {
     $field_name = strtolower($this->randomName());
     $type_name = 'article';
-    $this->createFileField($field_name, $type_name);
+    $this->createFileField($field_name, 'node', $type_name);
     $test_file = $this->getTestFile('text');
 
     // Create a new node.
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php
index d031ffe..e46f736 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldRSSContentTest.php
@@ -43,7 +43,7 @@ function testFileFieldRSSContent() {
       'description_field' => '1',
     );
     $widget_settings = array();
-    $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings);
+    $this->createFileField($field_name, 'node', $type_name, $field_settings, $instance_settings, $widget_settings);
 
     // RSS display must be added manually.
     $this->drupalGet("admin/structure/types/manage/$type_name/display");
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php
index d9604cc..fa55141 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php
@@ -35,10 +35,9 @@ public static function getInfo() {
   function testRevisions() {
     $type_name = 'article';
     $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
-
-    // Attach the same fields to users.
-    $this->attachFileField($field_name, 'user', 'user');
+    $this->createFileField($field_name, 'node', $type_name);
+    // Create the same fields for users.
+    $this->createFileField($field_name, 'user', 'user');
 
     $test_file = $this->getTestFile('text');
 
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php
index 3582893..f91ce20 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php
@@ -57,8 +57,10 @@ function getLastFileId() {
    *
    * @param $name
    *   The name of the new field (all lowercase), exclude the "field_" prefix.
-   * @param $type_name
-   *   The node type that this field will be added to.
+   * @param $entity_type
+   *   The entity type.
+   * @param $bundle
+   *   The bundle that this field will be added to.
    * @param $field_settings
    *   A list of field settings that will be added to the defaults.
    * @param $instance_settings
@@ -66,9 +68,10 @@ function getLastFileId() {
    * @param $widget_settings
    *   A list of widget settings that will be added to the widget defaults.
    */
-  function createFileField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
+  function createFileField($name, $entity_type, $bundle, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
     $field_definition = array(
-      'field_name' => $name,
+      'entity_type' => $entity_type,
+      'name' => $name,
       'type' => 'file',
       'settings' => array(),
       'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1,
@@ -77,7 +80,7 @@ function createFileField($name, $type_name, $field_settings = array(), $instance
     $field = entity_create('field_entity', $field_definition);
     $field->save();
 
-    $this->attachFileField($name, 'node', $type_name, $instance_settings, $widget_settings);
+    $this->attachFileField($name, $entity_type, $bundle, $instance_settings, $widget_settings);
     return $field;
   }
 
@@ -159,7 +162,7 @@ function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE,
     }
 
     // Attach a file to the node.
-    $field = field_info_field($field_name);
+    $field = field_info_field('node', $field_name);
     $name = 'files[' . $field_name . '_' . $langcode . '_0]';
     if ($field['cardinality'] != 1) {
       $name .= '[]';
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
index a7cf1e5..670ce91 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
@@ -30,8 +30,8 @@ public static function getInfo() {
   function testRequired() {
     $type_name = 'article';
     $field_name = strtolower($this->randomName());
-    $field = $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
-    $instance = field_info_instance('node', $field_name, $type_name);
+    $field = $this->createFileField($field_name, 'node', $type_name, array(), array('required' => '1'));
+    $instance = field_info_instance($field_name, 'node', $type_name);
 
     $test_file = $this->getTestFile('text');
 
@@ -53,7 +53,7 @@ function testRequired() {
 
     // Try again with a multiple value field.
     $field->delete();
-    $this->createFileField($field_name, $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1'));
+    $this->createFileField($field_name, 'node', $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1'));
 
     // Try to post a new node without uploading a file in the multivalue field.
     $edit = array('title' => $this->randomName());
@@ -74,7 +74,7 @@ function testRequired() {
   function testFileMaxSize() {
     $type_name = 'article';
     $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
+    $this->createFileField($field_name, 'node', $type_name, array(), array('required' => '1'));
 
     $small_file = $this->getTestFile('text', 131072); // 128KB.
     $large_file = $this->getTestFile('text', 1310720); // 1.2MB
@@ -120,7 +120,7 @@ function testFileMaxSize() {
   function testFileExtension() {
     $type_name = 'article';
     $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
+    $this->createFileField($field_name, 'node', $type_name);
 
     $test_file = $this->getTestFile('image');
     list(, $test_file_extension) = explode('.', $test_file->getFilename());
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
index e481384..1f8e9d6 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
@@ -35,7 +35,7 @@ public static function getInfo() {
   function testSingleValuedWidget() {
     $type_name = 'article';
     $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
+    $this->createFileField($field_name, 'node', $type_name);
 
     $test_file = $this->getTestFile('text');
 
@@ -96,8 +96,8 @@ function testMultiValuedWidget() {
     // names).
     $field_name = 'test_file_field_1';
     $field_name2 = 'test_file_field_2';
-    $this->createFileField($field_name, $type_name, array('cardinality' => 3));
-    $this->createFileField($field_name2, $type_name, array('cardinality' => 3));
+    $this->createFileField($field_name, 'node', $type_name, array('cardinality' => 3));
+    $this->createFileField($field_name2, 'node', $type_name, array('cardinality' => 3));
 
     $test_file = $this->getTestFile('text');
 
@@ -206,7 +206,7 @@ function testMultiValuedWidget() {
   function testPrivateFileSetting() {
     $type_name = 'article';
     $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
+    $this->createFileField($field_name, 'node', $type_name);
     $instance = field_info_instance('node', $field_name, $type_name);
 
     $test_file = $this->getTestFile('text');
@@ -316,7 +316,7 @@ function testPrivateFileComment() {
   function testWidgetValidation() {
     $type_name = 'article';
     $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
+    $this->createFileField($field_name, 'node', $type_name);
     $this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt'));
 
     foreach (array('nojs', 'js') as $type) {
@@ -326,7 +326,7 @@ function testWidgetValidation() {
       $this->drupalGet("node/$nid/edit");
       $test_file_text = $this->getTestFile('text');
       $test_file_image = $this->getTestFile('image');
-      $field = field_info_field($field_name);
+      $field = field_info_field('ndde', $field_name);
       $name = 'files[' . $field_name . '_' . Language::LANGCODE_NOT_SPECIFIED . '_0]';
 
       // Upload file with incorrect extension, check for validation error.
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php
index fd00c11..f44e48d 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php
@@ -45,7 +45,8 @@ public function setUp() {
     $this->installSchema('file', 'file_usage');
 
     entity_create('field_entity', array(
-      'field_name' => 'file_test',
+      'name' => 'file_test',
+      'entity_type' => 'entity_test',
       'type' => 'file',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
     ))->save();
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php b/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php
index e8a2f5d..cebde18 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileListingTest.php
@@ -34,7 +34,7 @@ function setUp() {
 
     $this->admin_user = $this->drupalCreateUser(array('access files overview', 'bypass node access'));
     $this->base_user = $this->drupalCreateUser();
-    $this->createFileField('file', 'article', array(), array('file_extensions' => 'txt png'));
+    $this->createFileField('file', 'node', 'article', array(), array('file_extensions' => 'txt png'));
   }
 
   /**
diff --git a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
index 1b8a82f..bf338e0 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
@@ -41,11 +41,11 @@ public function setUp() {
   function testPrivateFile() {
     $type_name = 'article';
     $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name, array('uri_scheme' => 'private'));
+    $this->createFileField($field_name, 'node', $type_name, array('uri_scheme' => 'private'));
 
     // Create a field with no view access - see field_test_field_access().
     $no_access_field_name = 'field_no_view_access';
-    $this->createFileField($no_access_field_name, $type_name, array('uri_scheme' => 'private'));
+    $this->createFileField($no_access_field_name, 'node', $type_name, array('uri_scheme' => 'private'));
 
     $test_file = $this->getTestFile('text');
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => TRUE));
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php b/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php
index 36d323d..b9ba145 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php
@@ -35,7 +35,7 @@ function testFileTokenReplacement() {
     // Create file field.
     $type_name = 'article';
     $field_name = 'field_' . strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
+    $this->createFileField($field_name, 'node', $type_name);
 
     $test_file = $this->getTestFile('text');
     // Coping a file to test uploads with non-latin filenames.
diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install
index 60efc30..adc8ddb 100644
--- a/core/modules/forum/forum.install
+++ b/core/modules/forum/forum.install
@@ -51,9 +51,10 @@ function forum_enable() {
   // Create the 'taxonomy_forums' field if it doesn't already exist. If forum
   // is being enabled at the same time as taxonomy after both modules have been
   // enabled, the field might exist but still be marked inactive.
-  if (!field_read_field('taxonomy_forums', array('include_inactive' => TRUE))) {
+  if (!field_read_field('node', 'taxonomy_forums', array('include_inactive' => TRUE))) {
     entity_create('field_entity', array(
-      'field_name' => 'taxonomy_forums',
+      'name' => 'taxonomy_forums',
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'settings' => array(
         'allowed_values' => array(
@@ -114,7 +115,7 @@ function forum_uninstall() {
   // Load the dependent Taxonomy module, in case it has been disabled.
   drupal_load('module', 'taxonomy');
 
-  if ($field = field_info_field('taxonomy_forums')) {
+  if ($field = field_info_field('node', 'taxonomy_forums')) {
     $field->delete();
   }
 
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 46921af..c1436e0 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -171,8 +171,8 @@ function forum_menu_local_tasks(&$data, $router_item, $root_path) {
     if ($forum_term) {
       $links = array();
       // Loop through all bundles for forum taxonomy vocabulary field.
-      $field = field_info_field('taxonomy_forums');
-      foreach ($field['bundles']['node'] as $type_name) {
+      $field = field_info_field('node', 'taxonomy_forums');
+      foreach ($field['bundles'] as $type_name) {
         if (($type = entity_load('node_type', $type_name)) && node_access('create', $type_name)) {
           $links[$type_name] = array(
             '#theme' => 'menu_local_action',
@@ -377,6 +377,33 @@ function forum_node_update(EntityInterface $node) {
         ))
         ->execute();
      }
+
+    // If the node is published, update the forum index.
+    if ($node->status) {
+      db_delete('forum_index')->condition('nid', $node->id())->execute();
+      $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
+      foreach ($node->taxonomy_forums as $language) {
+        foreach ($language as $item) {
+          $query->values(array(
+            'nid' => $node->id(),
+            'title' => $node->title,
+            'tid' => $item['target_id'],
+            'sticky' => $node->sticky,
+            'created' => $node->created,
+            'comment_count' => 0,
+            'last_comment_timestamp' => $node->created,
+          ));
+        }
+      }
+      $query->execute();
+      // The logic for determining last_comment_count is fairly complex, so
+      // call _forum_update_forum_index() too.
+      _forum_update_forum_index($node->id());
+    }
+    // When a forum node is unpublished, remove it from the forum_index table.
+    else {
+      db_delete('forum_index')->condition('nid', $node->id())->execute();
+    }
   }
 }
 
@@ -394,6 +421,23 @@ function forum_node_insert(EntityInterface $node) {
         ))
         ->execute();
     }
+
+    if ($node->status) {
+      $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
+      foreach ($node->getTranslationLanguages() as $langcode => $language) {
+        $translation = $node->getTranslation($langcode);
+        $query->values(array(
+          'nid' => $node->id(),
+          'title' => $translation->title->value,
+          'tid' => $translation->taxonomy_forums->target_id,
+          'sticky' => $node->sticky,
+          'created' => $node->created,
+          'comment_count' => 0,
+          'last_comment_timestamp' => $node->created,
+        ));
+      }
+      $query->execute();
+    }
   }
 }
 
@@ -498,74 +542,6 @@ function forum_comment_delete($comment) {
 }
 
 /**
- * Implements hook_field_storage_pre_insert().
- */
-function forum_field_storage_pre_insert(EntityInterface $entity, &$skip_fields) {
-  if ($entity->entityType() == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
-    $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
-    foreach ($entity->getTranslationLanguages() as $langcode => $language) {
-      $translation = $entity->getTranslation($langcode);
-      $query->values(array(
-        'nid' => $entity->id(),
-        'title' => $translation->title->value,
-        'tid' => $translation->taxonomy_forums->target_id,
-        'sticky' => $entity->sticky,
-        'created' => $entity->created,
-        'comment_count' => 0,
-        'last_comment_timestamp' => $entity->created,
-      ));
-    }
-    $query->execute();
-  }
-}
-
-/**
- * Implements hook_field_storage_pre_update().
- */
-function forum_field_storage_pre_update(EntityInterface $entity, &$skip_fields) {
-  $first_call = &drupal_static(__FUNCTION__, array());
-
-  if ($entity->entityType() == 'node' && _forum_node_check_node_type($entity)) {
-
-    // If the node is published, update the forum index.
-    if ($entity->status) {
-
-      // We don't maintain data for old revisions, so clear all previous values
-      // from the table. Since this hook runs once per field, per object, make
-      // sure we only wipe values once.
-      if (!isset($first_call[$entity->nid])) {
-        $first_call[$entity->nid] = FALSE;
-        db_delete('forum_index')->condition('nid', $entity->id())->execute();
-      }
-      $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
-      foreach ($entity->taxonomy_forums as $language) {
-        foreach ($language as $item) {
-          $query->values(array(
-            'nid' => $entity->nid,
-            'title' => $entity->title,
-            'tid' => $item['target_id'],
-            'sticky' => $entity->sticky,
-            'created' => $entity->created,
-            'comment_count' => 0,
-            'last_comment_timestamp' => $entity->created,
-          ));
-        }
-      }
-      $query->execute();
-      // The logic for determining last_comment_count is fairly complex, so
-      // call _forum_update_forum_index() too.
-      _forum_update_forum_index($entity->id());
-    }
-
-    // When a forum node is unpublished, remove it from the forum_index table.
-    else {
-      db_delete('forum_index')->condition('nid', $entity->id())->execute();
-    }
-
-  }
-}
-
-/**
  * Implements hook_form_BASE_FORM_ID_alter().
  */
 function forum_form_taxonomy_vocabulary_form_alter(&$form, &$form_state, $form_id) {
diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumUninstallTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumUninstallTest.php
index 0fe25e3..cce8db0 100644
--- a/core/modules/forum/lib/Drupal/forum/Tests/ForumUninstallTest.php
+++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumUninstallTest.php
@@ -34,7 +34,7 @@ public static function getInfo() {
    */
   function testForumUninstallWithField() {
     // Ensure that the field exists before uninstallation.
-    $field = field_info_field('taxonomy_forums');
+    $field = field_info_field('node', 'taxonomy_forums');
     $this->assertNotNull($field, 'The taxonomy_forums field exists.');
 
     // Uninstall the forum module which should trigger field deletion.
@@ -42,7 +42,7 @@ function testForumUninstallWithField() {
     $this->container->get('module_handler')->uninstall(array('forum'));
 
     // Check that the field is now deleted.
-    $field = field_info_field('taxonomy_forums');
+    $field = field_info_field('node', 'taxonomy_forums');
     $this->assertNull($field, 'The taxonomy_forums field has been deleted.');
   }
 
@@ -52,12 +52,12 @@ function testForumUninstallWithField() {
    */
   function testForumUninstallWithoutField() {
     // Manually delete the taxonomy_forums field before module uninstallation.
-    $field = field_info_field('taxonomy_forums');
+    $field = field_info_field('node', 'taxonomy_forums');
     $this->assertNotNull($field, 'The taxonomy_forums field exists.');
     $field->delete();
 
     // Check that the field is now deleted.
-    $field = field_info_field('taxonomy_forums');
+    $field = field_info_field('node', 'taxonomy_forums');
     $this->assertNull($field, 'The taxonomy_forums field has been deleted.');
 
     // Ensure that uninstallation succeeds even if the field has already been
diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
index ea55fd9..a99b1aa 100644
--- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
+++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
@@ -78,7 +78,8 @@ function setUp() {
 
     // Create the test text field.
     entity_create('field_entity', array(
-      'field_name' => 'field_test_text',
+      'name' => 'field_test_text',
+      'entity_type' => 'entity_test',
       'type' => 'text',
       'translatable' => FALSE,
     ))->save();
@@ -90,7 +91,8 @@ function setUp() {
 
     // Create the test translatable field.
     entity_create('field_entity', array(
-      'field_name' => 'field_test_translatable_text',
+      'name' => 'field_test_translatable_text',
+      'entity_type' => 'entity_test',
       'type' => 'text',
       'translatable' => TRUE,
     ))->save();
@@ -102,12 +104,13 @@ function setUp() {
 
     // Create the test entity reference field.
     entity_create('field_entity', array(
+      'name' => 'field_test_entity_reference',
+      'entity_type' => 'entity_test',
+      'type' => 'entity_reference',
       'translatable' => TRUE,
       'settings' => array(
         'target_type' => 'entity_test',
       ),
-      'field_name' => 'field_test_entity_reference',
-      'type' => 'entity_reference',
     ))->save();
     entity_create('field_instance', array(
       'entity_type' => 'entity_test',
diff --git a/core/modules/image/image.views.inc b/core/modules/image/image.views.inc
index 2e70c08..356f140 100644
--- a/core/modules/image/image.views.inc
+++ b/core/modules/image/image.views.inc
@@ -7,6 +7,9 @@
  * @ingroup views_module_handlers
  */
 
+use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\field\FieldInterface;
+
 /**
  * Implements hook_field_views_data().
  *
@@ -15,7 +18,7 @@
  *
  * @see field_views_field_default_views_data()
  */
-function image_field_views_data($field) {
+function image_field_views_data(FieldInterface $field) {
   $data = field_views_field_default_views_data($field);
   foreach ($data as $table_name => $table_data) {
     // Add the relationship only on the target_id field.
@@ -35,38 +38,31 @@ function image_field_views_data($field) {
  *
  * Views integration to provide reverse relationships on image fields.
  */
-function image_field_views_data_views_data_alter(&$data, $field) {
-  foreach ($field['bundles'] as $entity_type => $bundles) {
-    $entity_info = entity_get_info($entity_type);
-    $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type;
+function image_field_views_data_views_data_alter(array &$data, FieldInterface $field) {
+  $entity_type = $field['entity_type'];
+  $entity_info = entity_get_info($entity_type);
+  $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type;
 
-    list($label, $all_labels) = field_views_field_label($field['field_name']);
-    $entity = $entity_info['label'];
-    if ($entity == t('Node')) {
-      $entity = t('Content');
-    }
+  list($label,) = field_views_field_label($entity_type, $field['field_name']);
+  $entity_label = ($entity_info['label'] == t('Node')) ? t('Content') : $entity_info['label'];
 
-    $data['file_managed'][$pseudo_field_name]['relationship'] = array(
-      'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)),
-      'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity, '@field' => $label)),
-      'id' => 'entity_reverse',
-      'field_name' => $field['field_name'],
-      'field table' => _field_sql_storage_tablename($field),
-      'field field' => $field['field_name'] . '_target_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(
-          'field' => 'entity_type',
-          'value' => $entity_type,
-        ),
-        1 => array(
-          'field' => 'deleted',
-          'value' => 0,
-          'numeric' => TRUE,
-        ),
+  $data['file_managed'][$pseudo_field_name]['relationship'] = array(
+    'title' => t('@entity using @field', array('@entity' => $entity_label, '@field' => $label)),
+    'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity_label, '@field' => $label)),
+    'id' => 'entity_reverse',
+    'field_name' => $field['field_name'],
+    'entity_type' => $field['entity_type'],
+    'field table' => DatabaseStorageController::_fieldTableName($field),
+    'field field' => $field['field_name'] . '_target_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(
+        'field' => 'deleted',
+        'value' => 0,
+        'numeric' => TRUE,
       ),
-    );
-  }
+    ),
+  );
 }
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
index a0a746f..0608723 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
@@ -60,7 +60,7 @@ public function testDefaultImages() {
     $instance_field_settings = $instance->getFieldSettings();
     $this->assertEqual($instance_field_settings['default_image'], $default_images['instance']->id());
 
-    $field = entity_load('field_entity', $field_name);
+    $field = $instance->getField();
 
     // The field default image id should be 1.
     $this->assertEqual($field->getFieldSetting('default_image'), $default_images['field']->id());
@@ -71,7 +71,7 @@ public function testDefaultImages() {
 
     // Add another instance with another default image to the page content type.
     $instance2 = entity_create('field_instance', array(
-      'field_name' => $field->id(),
+      'field_name' => $field->name,
       'entity_type' => 'node',
       'bundle' => 'page',
       'label' => $instance->label(),
@@ -82,12 +82,12 @@ public function testDefaultImages() {
     ));
     $instance2->save();
 
-    $widget_settings = entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default')->getComponent($field['field_name']);
+    $widget_settings = entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default')->getComponent($field_name);
     entity_get_form_display('node', 'page', 'default')
-      ->setComponent($field->id(), $widget_settings)
+      ->setComponent($field_name, $widget_settings)
       ->save();
     entity_get_display('node', 'page', 'default')
-      ->setComponent($field->id())
+      ->setComponent($field_name)
       ->save();
 
     // Confirm the defaults are present on the article field settings form.
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
index 8ec6727..38394a6 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDisplayTest.php
@@ -231,7 +231,7 @@ function testImageFieldDefaultImage() {
     $this->drupalPost("admin/structure/types/manage/article/fields/node.article.$field_name/field", $edit, t('Save field settings'));
     // Clear field info cache so the new default image is detected.
     field_info_cache_clear();
-    $field = field_info_field($field_name);
+    $field = field_info_field('node', $field_name);
     $image = file_load($field['settings']['default_image']);
     $this->assertTrue($image->isPermanent(), 'The default image status is permanent.');
     $default_output = theme('image', array('uri' => $image->getFileUri()));
@@ -259,7 +259,7 @@ function testImageFieldDefaultImage() {
     $this->drupalPost("admin/structure/types/manage/article/fields/node.article.$field_name/field", $edit, t('Save field settings'));
     // Clear field info cache so the new default image is detected.
     field_info_cache_clear();
-    $field = field_info_field($field_name);
+    $field = field_info_field('node', $field_name);
     $this->assertFalse($field['settings']['default_image'], 'Default image removed from field.');
     // Create an image field that uses the private:// scheme and test that the
     // default image works as expected.
@@ -273,7 +273,7 @@ function testImageFieldDefaultImage() {
     // Clear field info cache so the new default image is detected.
     field_info_cache_clear();
 
-    $private_field = field_info_field($private_field_name);
+    $private_field = field_info_field('node', $private_field_name);
     $image = file_load($private_field['settings']['default_image']);
     $this->assertEqual('private', file_uri_scheme($image->getFileUri()), 'Default image uses private:// scheme.');
     $this->assertTrue($image->isPermanent(), 'The default image status is permanent.');
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php
index 7d0f054..5dc38f3 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldTestBase.php
@@ -66,7 +66,8 @@ function setUp() {
    */
   function createImageField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
     $field = array(
-      'field_name' => $name,
+      'name' => $name,
+      'entity_type' => 'node',
       'type' => 'image',
       'settings' => array(),
       'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1,
@@ -75,9 +76,9 @@ function createImageField($name, $type_name, $field_settings = array(), $instanc
     entity_create('field_entity', $field)->save();
 
     $instance = array(
-      'field_name' => $field['field_name'],
-      'entity_type' => 'node',
+      'field_name' => $field['name'],
       'label' => $name,
+      'entity_type' => 'node',
       'bundle' => $type_name,
       'required' => !empty($instance_settings['required']),
       'description' => !empty($instance_settings['description']) ? $instance_settings['description'] : '',
@@ -88,14 +89,14 @@ function createImageField($name, $type_name, $field_settings = array(), $instanc
     $field_instance->save();
 
     entity_get_form_display('node', $type_name, 'default')
-      ->setComponent($field['field_name'], array(
+      ->setComponent($field['name'], array(
         'type' => 'image_image',
         'settings' => $widget_settings,
       ))
       ->save();
 
     entity_get_display('node', $type_name, 'default')
-      ->setComponent($field['field_name'])
+      ->setComponent($field['name'])
       ->save();
 
     return $field_instance;
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php
index fdd3686..39f4ba2 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php
@@ -49,7 +49,8 @@ public function setUp() {
     $this->installSchema('file', array('file_managed', 'file_usage'));
 
     entity_create('field_entity', array(
-      'field_name' => 'image_test',
+      'name' => 'image_test',
+      'entity_type' => 'entity_test',
       'type' => 'image',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
     ))->save();
diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
index b4033ff..d48f587 100644
--- a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
+++ b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
@@ -68,7 +68,8 @@ function testURLValidation() {
     $field_name = drupal_strtolower($this->randomName());
     // Create a field with settings to validate.
     $this->field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'link',
     ));
     $this->field->save();
@@ -142,7 +143,8 @@ function testLinkTitle() {
     $field_name = drupal_strtolower($this->randomName());
     // Create a field with settings to validate.
     $this->field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'link',
     ));
     $this->field->save();
@@ -261,7 +263,8 @@ function testLinkFormatter() {
     $field_name = drupal_strtolower($this->randomName());
     // Create a field with settings to validate.
     $this->field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'link',
       'cardinality' => 2,
     ));
@@ -404,7 +407,8 @@ function testLinkSeparateFormatter() {
     $field_name = drupal_strtolower($this->randomName());
     // Create a field with settings to validate.
     $this->field = entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
       'type' => 'link',
       'cardinality' => 2,
     ));
diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php
index 2cd88bc..a42d6a2 100644
--- a/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php
+++ b/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php
@@ -36,7 +36,8 @@ public function setUp() {
 
     // Create an link field and instance for validation.
     entity_create('field_entity', array(
-      'field_name' => 'field_test',
+      'name' => 'field_test',
+      'entity_type' => 'entity_test',
       'type' => 'link',
     ))->save();
     entity_create('field_instance', array(
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
index 43d54ce..7242cbe 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageException;
 use Drupal\Core\Database\Connection;
+use Drupal\field\FieldInfo;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Cmf\Component\Routing\RouteProviderInterface;
 
@@ -52,11 +53,13 @@ class MenuLinkStorageController extends DatabaseStorageController implements Men
    *   An array of entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
+   * @param \Drupal\field\FieldInfo $field_info
+   *   The field info service.
    * @param \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider
    *   The route provider service.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database, RouteProviderInterface $route_provider) {
-    parent::__construct($entity_type, $entity_info, $database);
+  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info, RouteProviderInterface $route_provider) {
+    parent::__construct($entity_type, $entity_info, $database, $field_info);
 
     $this->routeProvider = $route_provider;
 
@@ -85,6 +88,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_type,
       $entity_info,
       $container->get('database'),
+      $container->get('field.info'),
       $container->get('router.route_provider')
     );
   }
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 1a874fe..3318acb 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\DatabaseStorageControllerNG;
 use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\DependencyInjection\Container;
 
 /**
  * Controller class for nodes.
@@ -46,10 +47,10 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
     }
 
     if ($load_revision) {
-      field_attach_load_revision($this->entityType, $queried_entities);
+      $this->fieldLoad($queried_entities, FIELD_LOAD_REVISION);
     }
     else {
-      field_attach_load($this->entityType, $queried_entities);
+      $this->fieldLoad($queried_entities, FIELD_LOAD_CURRENT);
     }
 
     // Besides the list of nodes, pass one additional argument to
@@ -80,14 +81,9 @@ protected function invokeHook($hook, EntityInterface $node) {
     // Inline parent::invokeHook() to pass on BC-entities to node-specific
     // hooks.
 
-    $function = 'field_attach_' . $hook;
-    // @todo: field_attach_delete_revision() is named the wrong way round,
-    // consider renaming it.
-    if ($function == 'field_attach_revision_delete') {
-      $function = 'field_attach_delete_revision';
-    }
-    if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
-      $function($node);
+    $method = Container::camelize('field_' . $hook);
+    if (!empty($this->entityInfo['fieldable']) && method_exists($this, $method)) {
+      $this->$method($node);
     }
 
     // Invoke the hook.
diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php b/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php
index acba6b6..2d55c6e 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php
@@ -306,9 +306,10 @@ protected function buildFilters(&$form, &$form_state) {
       $form['displays']['show']['tagged_with'] = array(
         '#type' => 'textfield',
         '#title' => t('tagged with'),
-        '#autocomplete_path' => 'taxonomy/autocomplete/' . $tag_field_name,
+        '#autocomplete_path' => 'taxonomy/autocomplete/' . $this->entity_type . '/' . $tag_field_name,
         '#size' => 30,
         '#maxlength' => 1024,
+        '#entity_type' => $this->entity_type,
         '#field_name' => $tag_field_name,
         '#element_validate' => array('views_ui_taxonomy_autocomplete_validate'),
       );
diff --git a/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php b/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php
index 65e3aec..4fc3f7b 100644
--- a/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php
@@ -41,7 +41,8 @@ function testMultiStepNodeFormBasicOptions() {
     // Create an unlimited cardinality field.
     $this->field_name = drupal_strtolower($this->randomName());
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'node',
       'type' => 'text',
       'cardinality' => -1,
     ))->save();
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php
index 8b4bf7a..944aa4f 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessFieldTest.php
@@ -62,7 +62,8 @@ public function setUp() {
     // Add a custom field to the page content type.
     $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'node',
       'type' => 'text'
     ))->save();
     entity_create('field_instance', array(
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
index 6df2698..6b451a0 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
@@ -59,7 +59,7 @@ function setUp() {
     $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), 'Basic page content type has been updated.');
 
     // Make node body translatable.
-    $field = field_info_field('body');
+    $field = field_info_field('node', 'body');
     $field->translatable = TRUE;
     $field->save();
   }
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
index dac3575..7c852d9 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
@@ -169,7 +169,7 @@ function testFieldTranslationForm() {
     $this->assertRaw('Not translated');
 
     // Delete the only translatable field.
-    field_info_field('field_test_et_ui_test')->delete();
+    field_info_field($this->entityType, 'field_test_et_ui_test')->delete();
 
     // Visit translation page.
     $this->drupalGet('node/' . $article->id() . '/translations');
diff --git a/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php b/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php
index 0538e00..f9e3670 100644
--- a/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php
@@ -68,7 +68,8 @@ function setUp() {
     // Set up a field and instance.
     $this->field_name = drupal_strtolower($this->randomName());
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'settings' => array(
         'allowed_values' => array(
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 200c6f6..185e792 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -395,13 +395,13 @@ function node_type_load($name) {
  */
 function node_add_body_field(NodeTypeInterface $type, $label = 'Body') {
    // Add or remove the body field, as needed.
-  $field = field_info_field('body');
+  $field = field_info_field('node', 'body');
   $instance = field_info_instance('node', 'body', $type->id());
   if (empty($field)) {
     $field = entity_create('field_entity', array(
-      'field_name' => 'body',
+      'name' => 'body',
+      'entity_type' => 'node',
       'type' => 'text_with_summary',
-      'entity_types' => array('node'),
     ));
     $field->save();
   }
@@ -417,20 +417,20 @@ function node_add_body_field(NodeTypeInterface $type, $label = 'Body') {
 
     // Assign widget settings for the 'default' form mode.
     entity_get_form_display('node', $type->type, 'default')
-      ->setComponent($field->id(), array(
+      ->setComponent('body', array(
         'type' => 'text_textarea_with_summary',
       ))
       ->save();
 
     // Assign display settings for the 'default' and 'teaser' view modes.
     entity_get_display('node', $type->type, 'default')
-      ->setComponent($field->id(), array(
+      ->setComponent('body', array(
         'label' => 'hidden',
         'type' => 'text_default',
       ))
       ->save();
     entity_get_display('node', $type->type, 'teaser')
-      ->setComponent($field->id(), array(
+      ->setComponent('body', array(
         'label' => 'hidden',
         'type' => 'text_summary_or_trimmed',
       ))
diff --git a/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module
index 1baa48e..5b54e13 100644
--- a/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module
+++ b/core/modules/node/tests/modules/node_access_test_language/node_access_test_language.module
@@ -50,7 +50,8 @@ function node_access_test_language_node_access_records(EntityInterface $node) {
  */
 function node_access_test_language_enable() {
   $field_private = entity_create('field_entity', array(
-    'field_name' => 'field_private',
+    'name' => 'field_private',
+    'entity_type' => 'node',
     'type' => 'list_boolean',
     'cardinality' => 1,
     'translatable'  => TRUE,
@@ -61,7 +62,7 @@ function node_access_test_language_enable() {
   $field_private->save();
 
   entity_create('field_instance', array(
-    'field_name' => $field_private->id(),
+    'field_name' => $field_private->name,
     'entity_type' => 'node',
     'bundle' => 'page',
     'widget' => array(
diff --git a/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php b/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php
index ad947e4..3839866 100644
--- a/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php
+++ b/core/modules/number/lib/Drupal/number/Tests/NumberFieldTest.php
@@ -64,7 +64,8 @@ function setUp() {
   function testNumberDecimalField() {
     // Create a field with settings to validate.
     $this->field = entity_create('field_entity', array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'name' => drupal_strtolower($this->randomName()),
+      'entity_type' => 'entity_test',
       'type' => 'number_decimal',
       'settings' => array(
         'precision' => 8, 'scale' => 4, 'decimal_separator' => '.',
@@ -72,13 +73,13 @@ function testNumberDecimalField() {
     ));
     $this->field->save();
     entity_create('field_instance', array(
-      'field_name' => $this->field->id(),
+      'field_name' => $this->field->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     ))->save();
 
     entity_get_form_display('entity_test', 'entity_test', 'default')
-      ->setComponent($this->field->id(), array(
+      ->setComponent($this->field->name, array(
         'type' => 'number',
         'settings' => array(
           'placeholder' => '0.00'
@@ -86,7 +87,7 @@ function testNumberDecimalField() {
       ))
       ->save();
     entity_get_display('entity_test', 'entity_test', 'default')
-      ->setComponent($this->field->id(), array(
+      ->setComponent($this->field->name, array(
         'type' => 'number_decimal',
       ))
       ->save();
diff --git a/core/modules/number/lib/Drupal/number/Tests/NumberItemTest.php b/core/modules/number/lib/Drupal/number/Tests/NumberItemTest.php
index a0cc904..636c55d 100644
--- a/core/modules/number/lib/Drupal/number/Tests/NumberItemTest.php
+++ b/core/modules/number/lib/Drupal/number/Tests/NumberItemTest.php
@@ -37,7 +37,8 @@ public function setUp() {
     // Create number fields and instances for validation.
     foreach (array('integer', 'float', 'decimal') as $type) {
       entity_create('field_entity', array(
-        'field_name' => 'field_' . $type,
+        'name' => 'field_' . $type,
+        'entity_type' => 'entity_test',
         'type' => 'number_' . $type,
       ))->save();
       entity_create('field_instance', array(
diff --git a/core/modules/options/lib/Drupal/options/Tests/OptionsDynamicValuesTest.php b/core/modules/options/lib/Drupal/options/Tests/OptionsDynamicValuesTest.php
index c270e4a..6607332 100644
--- a/core/modules/options/lib/Drupal/options/Tests/OptionsDynamicValuesTest.php
+++ b/core/modules/options/lib/Drupal/options/Tests/OptionsDynamicValuesTest.php
@@ -33,7 +33,8 @@ function setUp() {
 
     $this->field_name = 'test_options';
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'entity_test_rev',
       'type' => 'list_text',
       'cardinality' => 1,
       'settings' => array(
diff --git a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUITest.php b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUITest.php
index 2b45acd..34f0a87 100644
--- a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUITest.php
+++ b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUITest.php
@@ -231,7 +231,7 @@ function testOptionsAllowedValuesBoolean() {
     $this->drupalGet($this->admin_path);
     $this->assertFieldByName('on', $on, t("The 'On' value is stored correctly."));
     $this->assertFieldByName('off', $off, t("The 'Off' value is stored correctly."));
-    $field = field_info_field($this->field_name);
+    $field = field_info_field('node', $this->field_name);
     $this->assertEqual($field['settings']['allowed_values'], $allowed_values, 'The allowed value is correct');
     $this->assertFalse(isset($field['settings']['on']), 'The on value is not saved into settings');
     $this->assertFalse(isset($field['settings']['off']), 'The off value is not saved into settings');
@@ -259,7 +259,8 @@ function testOptionsTrimmedValuesText() {
   protected function createOptionsField($type) {
     // Create a test field and instance.
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'node',
       'type' => $type,
     ))->save();
     entity_create('field_instance', array(
@@ -294,7 +295,7 @@ function assertAllowedValuesInput($input_string, $result, $message) {
     }
     else {
       field_info_cache_clear();
-      $field = field_info_field($this->field_name);
+      $field = field_info_field('node', $this->field_name);
       $this->assertIdentical($field['settings']['allowed_values'], $result, $message);
     }
   }
diff --git a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUnitTestBase.php b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUnitTestBase.php
index 748ee6e..dc987d4 100644
--- a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUnitTestBase.php
+++ b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldUnitTestBase.php
@@ -58,7 +58,8 @@ public function setUp() {
     $this->installSchema('system', 'menu_router');
 
     $this->fieldDefinition = array(
-      'field_name' => $this->fieldName,
+      'name' => $this->fieldName,
+      'entity_type' => 'entity_test',
       'type' => 'list_integer',
       'cardinality' => 1,
       'settings' => array(
diff --git a/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php b/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php
index dbc1876..afa8a94 100644
--- a/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php
+++ b/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php
@@ -64,7 +64,8 @@ function setUp() {
 
     // Field with cardinality 1.
     $this->card_1 = entity_create('field_entity', array(
-      'field_name' => 'card_1',
+      'name' => 'card_1',
+      'entity_type' => 'entity_test',
       'type' => 'list_integer',
       'cardinality' => 1,
       'settings' => array(
@@ -76,7 +77,8 @@ function setUp() {
 
     // Field with cardinality 2.
     $this->card_2 = entity_create('field_entity', array(
-      'field_name' => 'card_2',
+      'name' => 'card_2',
+      'entity_type' => 'entity_test',
       'type' => 'list_integer',
       'cardinality' => 2,
       'settings' => array(
@@ -88,7 +90,8 @@ function setUp() {
 
     // Boolean field.
     $this->bool = entity_create('field_entity', array(
-      'field_name' => 'bool',
+      'name' => 'bool',
+      'entity_type' => 'entity_test',
       'type' => 'list_boolean',
       'cardinality' => 1,
       'settings' => array(
@@ -108,13 +111,13 @@ function setUp() {
   function testRadioButtons() {
     // Create an instance of the 'single value' field.
     $instance = entity_create('field_instance', array(
-      'field_name' => $this->card_1->id(),
+      'field_name' => $this->card_1->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     ));
     $instance->save();
     entity_get_form_display('entity_test', 'entity_test', 'default')
-      ->setComponent($this->card_1->id(), array(
+      ->setComponent($this->card_1->name, array(
         'type' => 'options_buttons',
       ))
       ->save();
@@ -167,13 +170,13 @@ function testRadioButtons() {
   function testCheckBoxes() {
     // Create an instance of the 'multiple values' field.
     $instance = entity_create('field_instance', array(
-      'field_name' => $this->card_2->id(),
+      'field_name' => $this->card_2->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     ));
     $instance->save();
     entity_get_form_display('entity_test', 'entity_test', 'default')
-      ->setComponent($this->card_2->id(), array(
+      ->setComponent($this->card_2->name, array(
         'type' => 'options_buttons',
       ))
       ->save();
@@ -259,14 +262,14 @@ function testCheckBoxes() {
   function testSelectListSingle() {
     // Create an instance of the 'single value' field.
     $instance = entity_create('field_instance', array(
-      'field_name' => $this->card_1->id(),
+      'field_name' => $this->card_1->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
       'required' => TRUE,
     ));
     $instance->save();
     entity_get_form_display('entity_test', 'entity_test', 'default')
-      ->setComponent($this->card_1->id(), array(
+      ->setComponent($this->card_1->name, array(
         'type' => 'options_select',
       ))
       ->save();
@@ -361,13 +364,13 @@ function testSelectListSingle() {
   function testSelectListMultiple() {
     // Create an instance of the 'multiple values' field.
     $instance = entity_create('field_instance', array(
-      'field_name' => $this->card_2->id(),
+      'field_name' => $this->card_2->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     ));
     $instance->save();
     entity_get_form_display('entity_test', 'entity_test', 'default')
-      ->setComponent($this->card_2->id(), array(
+      ->setComponent($this->card_2->name, array(
         'type' => 'options_select',
       ))
       ->save();
@@ -483,12 +486,12 @@ function testSelectListMultiple() {
   function testOnOffCheckbox() {
     // Create an instance of the 'boolean' field.
     entity_create('field_instance', array(
-      'field_name' => $this->bool->id(),
+      'field_name' => $this->bool->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     ))->save();
     entity_get_form_display('entity_test', 'entity_test', 'default')
-      ->setComponent($this->bool->id(), array(
+      ->setComponent($this->bool->name, array(
         'type' => 'options_onoff',
       ))
       ->save();
@@ -525,7 +528,12 @@ function testOnOffCheckbox() {
     // Display form: with 'off' value, option is unchecked.
     $this->drupalGet('entity_test/manage/' . $entity->id() . '/edit');
     $this->assertNoFieldChecked("edit-bool-$langcode");
+  }
 
+  /**
+   * Tests that the 'options_onoff' widget has a 'display_label' setting.
+   */
+  function testOnOffCheckboxLabelSetting() {
     // Create Basic page node type.
     $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
 
@@ -534,11 +542,17 @@ function testOnOffCheckbox() {
     $this->drupalLogin($admin_user);
 
     // Create a test field instance.
-    $fieldUpdate = $this->bool;
-    $fieldUpdate['settings']['allowed_values'] = array(0 => 0, 1 => 'MyOnValue');
-    $fieldUpdate->save();
+    entity_create('field_entity', array(
+      'name' => 'bool',
+      'entity_type' => 'node',
+      'type' => 'list_boolean',
+      'cardinality' => 1,
+      'settings' => array(
+        'allowed_values' => array(0 => 'Zero', 1 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
+      ),
+    ))->save();
     entity_create('field_instance', array(
-      'field_name' => $this->bool['field_name'],
+      'field_name' => 'bool',
       'entity_type' => 'node',
       'bundle' => 'page',
     ))->save();
@@ -584,4 +598,5 @@ function testOnOffCheckbox() {
       t('Display label changes label of the checkbox')
     );
   }
+
 }
diff --git a/core/modules/options/options.module b/core/modules/options/options.module
index e6ebdbe..6dedc11 100644
--- a/core/modules/options/options.module
+++ b/core/modules/options/options.module
@@ -384,16 +384,14 @@ function _options_values_in_use($field, $values) {
   if ($values) {
     $field = field_info_field_by_id($field['uuid']);
     $factory = Drupal::service('entity.query');
-    foreach ($field['bundles'] as $entity_type => $bundle) {
-      $result = $factory->get($entity_type)
-        ->condition($field['field_name'] . '.value', $values)
-        ->count()
-        ->accessCheck(FALSE)
-        ->range(0, 1)
-        ->execute();
-      if ($result) {
-        return TRUE;
-      }
+    $result = $factory->get($field->entity_type)
+      ->condition($field['field_name'] . '.value', $values)
+      ->count()
+      ->accessCheck(FALSE)
+      ->range(0, 1)
+      ->execute();
+    if ($result) {
+      return TRUE;
     }
   }
 
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php
index 11176dc..46119d5 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php
@@ -56,7 +56,7 @@ public function setUp() {
     $this->fieldName = strtolower($this->randomName());
 
     $type_name = 'article';
-    $this->createFileField($this->fieldName, $type_name);
+    $this->createFileField($this->fieldName, 'node', $type_name);
 
     // Set the teaser display to show this field.
     entity_get_display('node', 'article', 'teaser')
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php
index 70bdda0..d082abe 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php
@@ -154,7 +154,8 @@ function testNodeTeaser() {
    */
   protected function createTaxonomyTermReferenceField($field_name, $vocabulary) {
     entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
diff --git a/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php
index 0e3fd91..9c7815c 100644
--- a/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php
+++ b/core/modules/rest/lib/Drupal/rest/LinkManager/RelationLinkManager.php
@@ -74,16 +74,14 @@ public function getRelations() {
   protected function writeCache() {
     $data = array();
 
-    foreach (field_info_fields() as $field_info) {
-      foreach ($field_info['bundles'] as $entity_type => $bundles) {
-        foreach ($bundles as $bundle) {
-          $relation_uri = $this->getRelationUri($entity_type, $bundle, $field_info['field_name']);
-          $data[$relation_uri] = array(
-            'entity_type' => $entity_type,
-            'bundle' => $bundle,
-            'field_name' => $field_info['field_name'],
-          );
-        }
+    foreach (field_info_fields() as $field) {
+      foreach ($field['bundles'] as $bundle) {
+        $relation_uri = $this->getRelationUri($field->entity_type, $bundle, $field->name);
+        $data[$relation_uri] = array(
+          'entity_type' => $field->entity_type,
+          'bundle' => $bundle,
+          'field_name' => $field->name,
+        );
       }
     }
     // These URIs only change when field info changes, so cache it permanently
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
index e1021b4..dcb5bb2 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
@@ -48,7 +48,7 @@ function setUp() {
 
     // Make the body field translatable.
     // The parent class has already created the article and page content types.
-    $field = field_info_field('body');
+    $field = field_info_field('node', 'body');
     $field->translatable = TRUE;
     $field->save();
 
diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php b/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php
index 48d0293..5d9dac4 100644
--- a/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php
+++ b/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php
@@ -38,7 +38,8 @@ protected function setUp() {
       'settings' => array(
         'target_type' => 'entity_test_mulrev',
       ),
-      'field_name' => 'field_test_entity_reference',
+      'name' => 'field_test_entity_reference',
+      'entity_type' => 'entity_test_mulrev',
       'type' => 'entity_reference',
     ))->save();
 
diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
index 74fa8d9..8bb36f2 100644
--- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
+++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
@@ -16,7 +16,7 @@
    *
    * @var array
    */
-  public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'field_sql_storage');
+  public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text');
 
   protected function setUp() {
     parent::setUp();
@@ -27,7 +27,8 @@ protected function setUp() {
 
     // Auto-create a field for testing.
     entity_create('field_entity', array(
-      'field_name' => 'field_test_text',
+      'name' => 'field_test_text',
+      'entity_type' => 'entity_test_mulrev',
       'type' => 'text',
       'cardinality' => 1,
       'translatable' => FALSE,
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
index a2788b1..505ff33 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
@@ -232,12 +232,13 @@ function testEnableModulesFixedList() {
       'mode' => 'default',
     ));
     $field = entity_create('field_entity', array(
-      'field_name' => 'test_field',
+      'name' => 'test_field',
+      'entity_type' => 'entity_test',
       'type' => 'test_field'
     ));
     $field->save();
     entity_create('field_instance', array(
-      'field_name' => $field->id(),
+      'field_name' => $field->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     ))->save();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php
index f15f382..5051384 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/MultiFormTest.php
@@ -35,7 +35,8 @@ function setUp() {
     // Create a multi-valued field for 'page' nodes to use for Ajax testing.
     $field_name = 'field_ajax_test';
     entity_create('field_entity', array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'node',
       'type' => 'text',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
     ))->save();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregateTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregateTest.php
index faa7090..7939fb0 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregateTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryAggregateTest.php
@@ -62,7 +62,8 @@ public function setUp() {
     for ($i = 1; $i <= 2; $i++) {
       $field_name = 'field_test_' . $i;
       entity_create('field_entity', array(
-        'field_name' => $field_name,
+        'name' => $field_name,
+        'entity_type' => 'entity_test',
         'type' => 'number_integer',
         'cardinality' => 2,
       ))->save();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php
index ae99e9d..ee498c4 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php
@@ -81,7 +81,8 @@ public function setUp() {
     // Second, create the field.
     $this->fieldName = strtolower($this->randomName());
     $field = array(
-      'field_name' => $this->fieldName,
+      'name' => $this->fieldName,
+      'entity_type' => 'entity_test',
       'type' => 'taxonomy_term_reference',
     );
     $field['settings']['allowed_values']['vocabulary'] = $vocabulary->id();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
index 284526f..e1b0ea7 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
@@ -63,7 +63,8 @@ function setUp() {
     $greetings = drupal_strtolower($this->randomName());
     foreach (array($figures => 'shape', $greetings => 'text') as $field_name => $field_type) {
       $field = entity_create('field_entity', array(
-        'field_name' => $field_name,
+        'name' => $field_name,
+        'entity_type' => 'entity_test_mulrev',
         'type' => $field_type,
         'cardinality' => 2,
         'translatable' => TRUE,
@@ -81,7 +82,7 @@ function setUp() {
       entity_test_create_bundle($bundle);
       foreach ($fields as $field) {
         entity_create('field_instance', array(
-          'field_name' => $field->id(),
+          'field_name' => $field->name,
           'entity_type' => 'entity_test_mulrev',
           'bundle' => $bundle,
         ))->save();
@@ -404,12 +405,19 @@ protected function testTableSort() {
   }
 
   /**
-   * Test entity count query.
+   * Test that count queries are separated across entity types.
    */
   protected function testCount() {
-    // Attach the existing 'figures' field to a second entity type so that we
-    // can test whether cross entity type fields produce the correct query.
+    // Create a field with the same name in a different entity type.
     $field_name = $this->figures;
+    $field = entity_create('field_entity', array(
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
+      'type' => 'shape',
+      'cardinality' => 2,
+      'translatable' => TRUE,
+    ));
+    $field->save();
     $bundle = $this->randomName();
     entity_create('field_instance', array(
       'field_name' => $field_name,
@@ -424,6 +432,7 @@ protected function testCount() {
     $entity->enforceIsNew();
     $entity->setNewRevision();
     $entity->save();
+
     // As the single entity of this type we just saved does not have a value
     // in the color field, the result should be 0.
     $count = $this->factory->get('entity_test')
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php
index 80e8a6b..e152a68 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php
@@ -104,10 +104,10 @@ function testEntityFormLanguage() {
     $this->assertTrue($node, 'Node found in database.');
 
     // Make body translatable.
-    $field = field_info_field('body');
+    $field = field_info_field('node', 'body');
     $field->translatable = TRUE;
     $field->save();
-    $field = field_info_field('body');
+    $field = field_info_field('node', 'body');
     $this->assertTrue($field['translatable'], 'Field body is translatable.');
 
     // Create a body translation and check the form language.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
index 40a7e44..7adfda3 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
@@ -51,16 +51,16 @@ function setUp() {
 
     // Create a translatable test field.
     $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
-    entity_create('field_entity', array(
-      'field_name' => $this->field_name,
-      'type' => 'text',
-      'cardinality' => 4,
-      'translatable' => TRUE,
-    ))->save();
-    $this->field = field_read_field($this->field_name);
 
     // Create instance in all entity variations.
     foreach (entity_test_entity_types() as $entity_type) {
+      entity_create('field_entity', array(
+        'name' => $this->field_name,
+        'entity_type' => $entity_type,
+        'type' => 'text',
+        'cardinality' => 4,
+        'translatable' => TRUE,
+      ))->save();
       entity_create('field_instance', array(
         'field_name' => $this->field_name,
         'entity_type' => $entity_type,
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
index 34fb4cc..d848bcf 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
@@ -20,7 +20,7 @@
    *
    * @var array
    */
-  public static $modules = array('entity', 'user', 'system', 'field', 'text', 'field_sql_storage', 'entity_test');
+  public static $modules = array('entity', 'user', 'system', 'field', 'text', 'entity_test');
 
   /**
    * The entity manager service.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
new file mode 100644
index 0000000..a3ab7dc
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
@@ -0,0 +1,462 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Entity\FieldSqlStorageTest.
+ */
+
+namespace Drupal\system\Tests\Entity;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\field\FieldException;
+use Drupal\field\Plugin\Core\Entity\Field;
+use Drupal\system\Tests\Entity\EntityUnitTestBase;
+use PDO;
+
+/**
+ * Tests field storage.
+ *
+ * Field_sql_storage.module implements the default back-end storage plugin
+ * for the Field Storage API.
+ */
+class FieldSqlStorageTest extends EntityUnitTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('field', 'field_test', 'text', 'number', 'entity_test');
+
+  /**
+   * The name of the created field.
+   *
+   * @var string
+   */
+  protected $field_name;
+
+  /**
+   * A field to use in this class.
+   *
+   * @var \Drupal\field\Plugin\Core\Entity\Field
+   */
+  protected $field;
+
+  /**
+   * A field instance to use in this test class.
+   *
+   * @var \Drupal\field\Plugin\Core\Entity\FieldInstance
+   */
+  protected $instance;
+
+  /**
+   * Name of the revision table of the field.
+   *
+   * @var string
+   */
+  protected $revision_table;
+
+  public static function getInfo() {
+    return array(
+      'name'  => 'Field SQL storage tests',
+      'description'  => "Test Field SQL Storage .",
+      'group' => 'Entity API'
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
+    $entity_type = 'entity_test_rev';
+
+    $this->field_name = strtolower($this->randomName());
+    $this->field = entity_create('field_entity', array(
+      'name' => $this->field_name,
+      'entity_type' => $entity_type,
+      'type' => 'test_field',
+      'cardinality' => 4,
+    ));
+    $this->field->save();
+    $this->instance = entity_create('field_instance', array(
+      'field_name' => $this->field_name,
+      'entity_type' => $entity_type,
+      'bundle' => $entity_type
+    ));
+    $this->instance->save();
+
+    $this->table = DatabaseStorageController::_fieldTableName($this->field);
+    $this->revision_table = DatabaseStorageController::_fieldRevisionTableName($this->field);
+  }
+
+  /**
+   * Tests field loading works correctly by inserting directly in the tables.
+   */
+  function testFieldLoad() {
+    $entity_type = $bundle = 'entity_test_rev';
+    $storage_controller = $this->container->get('plugin.manager.entity')->getStorageController($entity_type);
+
+    $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', DatabaseStorageController::_fieldColumnName($this->field, 'value'));
+
+    // Create an entity with four revisions.
+    $revision_ids = array();
+    $entity = entity_create($entity_type, array());
+    $entity->save();
+    $revision_ids[] = $entity->getRevisionId();
+    for ($i = 0; $i < 4; $i++) {
+      $entity->setNewRevision();
+      $entity->save();
+      $revision_ids[] = $entity->getRevisionId();
+    }
+
+    // Generate values and insert them directly in the storage tables.
+    $values = array();
+    $query = db_insert($this->revision_table)->fields($columns);
+    foreach ($revision_ids as $revision_id) {
+      // Put one value too many.
+      for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
+        $value = mt_rand(1, 127);
+        $values[$revision_id][] = $value;
+        $query->values(array($bundle, 0, $entity->id(), $revision_id, $delta, $entity->language()->id, $value));
+      }
+      $query->execute();
+    }
+    $query = db_insert($this->table)->fields($columns);
+    foreach ($values[$revision_id] as $delta => $value) {
+      $query->values(array($bundle, 0, $entity->id(), $revision_id, $delta, $entity->language()->id, $value));
+    }
+    $query->execute();
+
+    // Load every revision and check the values.
+    foreach ($revision_ids as $revision_id) {
+      $entity = $storage_controller->loadRevision($revision_id);
+      foreach ($values[$revision_id] as $delta => $value) {
+        if ($delta < $this->field['cardinality']) {
+          $this->assertEqual($entity->{$this->field_name}[$delta]->value, $value);
+        }
+        else {
+          $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}));
+        }
+      }
+    }
+
+    // Load the "current revision" and check the values.
+    $entity = $storage_controller->load($entity->id());
+    foreach ($values[$revision_id] as $delta => $value) {
+      if ($delta < $this->field['cardinality']) {
+        $this->assertEqual($entity->{$this->field_name}[$delta]->value, $value);
+      }
+      else {
+        $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}));
+      }
+    }
+
+    // Add a translation in an unavailable language code and verify it is not
+    // loaded.
+    $unavailable_langcode = 'xx';
+    $values = array($bundle, 0, $entity->id(), $entity->getRevisionId(), 0, $unavailable_langcode, mt_rand(1, 127));
+    db_insert($this->table)->fields($columns)->values($values)->execute();
+    db_insert($this->revision_table)->fields($columns)->values($values)->execute();
+    $entity = $storage_controller->load($entity->id());
+    $this->assertFalse(array_key_exists($unavailable_langcode, $entity->{$this->field_name}));
+  }
+
+  /**
+   * Tests field saving works correctly by reading directly from the tables.
+   */
+  function testFieldWrite() {
+    $entity_type = $bundle = 'entity_test_rev';
+    $entity = entity_create($entity_type, array());
+
+    $revision_values = array();
+
+    // Check insert. Add one value too many.
+    $values = array();
+    for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
+      $values[$delta]['value'] = mt_rand(1, 127);
+    }
+    $entity->{$this->field_name} = $values;
+    $entity->save();
+
+    // Read the tables and check the correct values have been stored.
+    $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
+    $this->assertEqual(count($rows), $this->field['cardinality']);
+    foreach ($rows as $delta => $row) {
+      $expected = array(
+        'bundle' => $bundle,
+        'deleted' => 0,
+        'entity_id' => $entity->id(),
+        'revision_id' => $entity->getRevisionId(),
+        'langcode' => $entity->language()->id,
+        'delta' => $delta,
+        $this->field_name . '_value' => $values[$delta]['value'],
+      );
+      $this->assertEqual($row, $expected, "Row $delta was stored as expected.");
+    }
+
+    // Test update. Add less values and check that the previous values did not
+    // persist.
+    $values = array();
+    for ($delta = 0; $delta <= $this->field['cardinality'] - 2; $delta++) {
+      $values[$delta]['value'] = mt_rand(1, 127);
+    }
+    $entity->{$this->field_name} = $values;
+    $entity->save();
+    $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
+    $this->assertEqual(count($rows), count($values));
+    foreach ($rows as $delta => $row) {
+      $expected = array(
+        'bundle' => $bundle,
+        'deleted' => 0,
+        'entity_id' => $entity->id(),
+        'revision_id' => $entity->getRevisionId(),
+        'langcode' => $entity->language()->id,
+        'delta' => $delta,
+        $this->field_name . '_value' => $values[$delta]['value'],
+      );
+      $this->assertEqual($row, $expected, "Row $delta was stored as expected.");
+    }
+
+    // Create a new revision.
+    $revision_values[$entity->getRevisionId()] = $values;
+    $values = array();
+    for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
+      $values[$delta]['value'] = mt_rand(1, 127);
+    }
+    $entity->{$this->field_name} = $values;
+    $entity->setNewRevision();
+    $entity->save();
+    $revision_values[$entity->getRevisionId()] = $values;
+
+    // Check that data for both revisions are in the revision table.
+    foreach ($revision_values as $revision_id => $values) {
+      $rows = db_select($this->revision_table, 't')->fields('t')->condition('revision_id', $revision_id)->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
+      $this->assertEqual(count($rows), min(count($values), $this->field['cardinality']));
+      foreach ($rows as $delta => $row) {
+        $expected = array(
+          'bundle' => $bundle,
+          'deleted' => 0,
+          'entity_id' => $entity->id(),
+          'revision_id' => $revision_id,
+          'langcode' => $entity->language()->id,
+          'delta' => $delta,
+          $this->field_name . '_value' => $values[$delta]['value'],
+        );
+        $this->assertEqual($row, $expected, "Row $delta was stored as expected.");
+      }
+    }
+
+    // Test emptying the field.
+    $entity->{$this->field_name} = NULL;
+    $entity->save();
+    $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
+    $this->assertEqual(count($rows), 0);
+  }
+
+  /**
+   * Tests that long entity type and field names do not break.
+   */
+  function testLongNames() {
+    // Use one of the longest entity_type names in core.
+    $entity_type = $bundle = 'entity_test_label_callback';
+    $storage_controller = $this->container->get('plugin.manager.entity')->getStorageController($entity_type);
+
+    // Create two fields with instances, and generate randome values.
+    $name_base = drupal_strtolower($this->randomName(Field::NAME_MAX_LENGTH - 1));
+    $field_names = array();
+    $values = array();
+    for ($i = 0; $i < 2; $i++) {
+      $field_names[$i] = $name_base . $i;
+      entity_create('field_entity', array(
+        'name' => $field_names[$i],
+        'entity_type' => $entity_type,
+        'type' => 'test_field',
+      ))->save();
+      entity_create('field_instance', array(
+        'field_name' => $field_names[$i],
+        'entity_type' => $entity_type,
+        'bundle' => $bundle,
+      ))->save();
+      $values[$field_names[$i]] = mt_rand(1, 127);
+    }
+
+    // Save an entity with values.
+    $entity = entity_create($entity_type, $values);
+    $entity->save();
+
+    // Load the entity back and check the values.
+    $entity = $storage_controller->load($entity->id());
+    foreach ($field_names as $field_name) {
+      $this->assertEqual($entity->get($field_name)->value, $values[$field_name]);
+    }
+  }
+
+  /**
+   * Test trying to update a field with data.
+   */
+  function testUpdateFieldSchemaWithData() {
+    $entity_type = 'entity_test_rev';
+    // Create a decimal 5.2 field and add some data.
+    $field = entity_create('field_entity', array(
+      'name' => 'decimal52',
+      'entity_type' => $entity_type,
+      'type' => 'number_decimal',
+      'settings' => array('precision' => 5, 'scale' => 2),
+    ));
+    $field->save();
+    $instance = entity_create('field_instance', array(
+      'field_name' => 'decimal52',
+      'entity_type' => $entity_type,
+      'bundle' => $entity_type,
+    ));
+    $instance->save();
+    $entity = entity_create($entity_type, array(
+      'id' => 0,
+      'revision_id' => 0,
+    ));
+    $entity->decimal52->value = '1.235';
+    $entity->save();
+
+    // Attempt to update the field in a way that would work without data.
+    $field->settings['scale'] = 3;
+    try {
+      $field->save();
+      $this->fail(t('Cannot update field schema with data.'));
+    }
+    catch (FieldException $e) {
+      $this->pass(t('Cannot update field schema with data.'));
+    }
+  }
+
+  /**
+   * Test that failure to create fields is handled gracefully.
+   */
+  function testFieldUpdateFailure() {
+    // Create a text field.
+    $field = entity_create('field_entity', array(
+      'name' => 'test_text',
+      'entity_type' => 'entity_test',
+      'type' => 'text',
+      'settings' => array('max_length' => 255),
+    ));
+    $field->save();
+
+    // Attempt to update the field in a way that would break the storage.
+    $prior_field = $field;
+    $field->settings['max_length'] = -1;
+    try {
+      $field->save();
+      $this->fail(t('Update succeeded.'));
+    }
+    catch (\Exception $e) {
+      $this->pass(t('Update properly failed.'));
+    }
+
+    // Ensure that the field tables are still there.
+    foreach (DatabaseStorageController::_fieldSqlSchema($prior_field) as $table_name => $table_info) {
+      $this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name)));
+    }
+  }
+
+  /**
+   * Test adding and removing indexes while data is present.
+   */
+  function testFieldUpdateIndexesWithData() {
+    // Create a decimal field.
+    $field_name = 'testfield';
+    $entity_type = 'entity_test_rev';
+    $field = entity_create('field_entity', array(
+      'name' => $field_name,
+      'entity_type' => $entity_type,
+      'type' => 'text',
+    ));
+    $field->save();
+    $instance = entity_create('field_instance', array(
+      'field_name' => $field_name,
+      'entity_type' => $entity_type,
+      'bundle' => $entity_type,
+    ));
+    $instance->save();
+    $tables = array(DatabaseStorageController::_fieldTableName($field), DatabaseStorageController::_fieldRevisionTableName($field));
+
+    // Verify the indexes we will create do not exist yet.
+    foreach ($tables as $table) {
+      $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), t("No index named value exists in $table"));
+      $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), t("No index named value_format exists in $table"));
+    }
+
+    // Add data so the table cannot be dropped.
+    $entity = entity_create($entity_type, array(
+      'id' => 1,
+      'revision_id' => 1,
+    ));
+    $entity->$field_name->value = 'field data';
+    $entity->enforceIsNew();
+    $entity->save();
+
+    // Add an index.
+    $field->indexes = array('value' => array(array('value', 255)));
+    $field->save();
+    foreach ($tables as $table) {
+      $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value created in $table"));
+    }
+
+    // Add a different index, removing the existing custom one.
+    $field->indexes = array('value_format' => array(array('value', 127), array('format', 127)));
+    $field->save();
+    foreach ($tables as $table) {
+      $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), t("Index on value_format created in $table"));
+      $this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value removed in $table"));
+    }
+
+    // Verify that the tables were not dropped in the process.
+    field_cache_clear();
+    $entity = $this->container->get('plugin.manager.entity')->getStorageController($entity_type)->load(1);
+    $this->assertEqual($entity->$field_name->value, 'field data', t("Index changes performed without dropping the tables"));
+  }
+
+  /**
+   * Test foreign key support.
+   */
+  function testFieldSqlStorageForeignKeys() {
+    // Create a 'shape' field, with a configurable foreign key (see
+    // field_test_field_schema()).
+    $field_name = 'testfield';
+    $foreign_key_name = 'shape';
+    $field = entity_create('field_entity', array(
+      'name' => $field_name,
+      'entity_type' => 'entity_test',
+      'type' => 'shape',
+      'settings' => array('foreign_key_name' => $foreign_key_name),
+    ));
+    $field->save();
+    // Get the field schema.
+    $schema = $field->getSchema();
+
+    // Retrieve the field definition and check that the foreign key is in place.
+    $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name preserved through CRUD');
+    $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name preserved through CRUD');
+
+    // Update the field settings, it should update the foreign key definition too.
+    $foreign_key_name = 'color';
+    $field->settings['foreign_key_name'] = $foreign_key_name;
+    $field->save();
+    // Reload the field schema after the update.
+    $schema = $field->getSchema();
+
+    // Retrieve the field definition and check that the foreign key is in place.
+    $field = field_info_field('entity_test', $field_name);
+    $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update');
+    $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update');
+
+    // Verify the SQL schema.
+    $schemas = DatabaseStorageController::_fieldSqlSchema($field);
+    $schema = $schemas[DatabaseStorageController::_fieldTableName($field)];
+    $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema');
+    $foreign_key = reset($schema['foreign keys']);
+    $foreign_key_column = DatabaseStorageController::_fieldColumnName($field, $foreign_key_name);
+    $this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema');
+    $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema');
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/ArbitraryRebuildTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/ArbitraryRebuildTest.php
index cc6546a..193c2d2 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/ArbitraryRebuildTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/ArbitraryRebuildTest.php
@@ -34,7 +34,8 @@ function setUp() {
 
     // Auto-create a field for testing.
     entity_create('field_entity', array(
-      'field_name' => 'test_multiple',
+      'name' => 'test_multiple',
+      'entity_type' => 'user',
       'type' => 'text',
       'cardinality' => -1,
       'translatable' => FALSE,
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php
index c580e88..460e0ca 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/RebuildTest.php
@@ -72,7 +72,8 @@ function testPreserveFormActionAfterAJAX() {
     // Create a multi-valued field for 'page' nodes to use for Ajax testing.
     $field_name = 'field_ajax_test';
     $field = array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'node',
       'type' => 'text',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
     );
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
index 01794f0..982a70d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
@@ -14,7 +14,7 @@
  */
 class TreeOutputTest extends DrupalUnitTestBase {
 
-  public static $modules = array('system', 'menu_link');
+  public static $modules = array('system', 'menu_link', 'field');
 
   /**
    * Dummy link structure acceptable for menu_tree_output().
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
index b04e2d8..262537b 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
@@ -17,7 +17,7 @@
  */
 class ContextPluginTest extends DrupalUnitTestBase {
 
-  public static $modules = array('system', 'user', 'node');
+  public static $modules = array('system', 'user', 'node', 'field');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/PasswordHashingTest.php b/core/modules/system/lib/Drupal/system/Tests/System/PasswordHashingTest.php
index 7b61c15..53ff2fc 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/PasswordHashingTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/PasswordHashingTest.php
@@ -20,7 +20,7 @@ class PasswordHashingTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('user');
+  public static $modules = array('field', 'user');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
index 66a95c0..d50ad4e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
@@ -6,7 +6,10 @@
  */
 
 namespace Drupal\system\Tests\Upgrade;
+
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Language\Language;
+use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
  * Tests upgrade of system variables.
@@ -121,27 +124,24 @@ function testFieldUpgradeToConfig() {
     $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.');
 
     // Check that the configuration for the 'body' field is correct.
-    $config = \Drupal::config('field.field.body')->get();
+    $config = \Drupal::config('field.field.node.body')->get();
+    // This will be necessary to retrieve the table name.
+    $field_entity = new Field($config);
     // We cannot predict the value of the UUID, we just check it's present.
     $this->assertFalse(empty($config['uuid']));
     $field_uuid = $config['uuid'];
     unset($config['uuid']);
     $this->assertEqual($config, array(
-      'id' => 'body',
+      'id' => 'node.body',
+      'name' => 'body',
       'type' => 'text_with_summary',
       'module' => 'text',
       'active' => '1',
+      'entity_type' => 'node',
       'settings' => array(),
-      'storage' => array(
-        'type' => 'field_sql_storage',
-        'module' => 'field_sql_storage',
-        'active' => '1',
-        'settings' => array(),
-      ),
       'locked' => 0,
       'cardinality' => 1,
       'translatable' => 0,
-      'entity_types' => array('node'),
       'indexes' => array(
         'format' => array('format')
       ),
@@ -195,7 +195,7 @@ function testFieldUpgradeToConfig() {
     $uuid_key = key($deleted_fields);
     $deleted_field = $deleted_fields[$uuid_key];
     $this->assertEqual($deleted_field['uuid'], $uuid_key);
-    $this->assertEqual($deleted_field['id'], 'test_deleted_field');
+    $this->assertEqual($deleted_field['id'], 'node.test_deleted_field');
 
     // Check that the definition of a deleted instance is stored in state rather
     // than config.
@@ -209,16 +209,11 @@ function testFieldUpgradeToConfig() {
     // The deleted field uuid and deleted instance field_uuid must match.
     $this->assertEqual($deleted_field['uuid'], $deleted_instance['field_uuid']);
 
-    // Check that pre-existing deleted field values are read correctly.
-    $entity = _field_create_entity_from_ids((object) array(
-      'entity_type' => 'node',
-      'bundle' => 'article',
-      'entity_id' => 2,
-      'revision_id' => 2,
-    ));
-    field_attach_load('node', array(2 => $entity), FIELD_LOAD_CURRENT, array('instance' => entity_create('field_instance', $deleted_instance)));
-    $deleted_value = $entity->get('test_deleted_field');
-    $this->assertEqual($deleted_value[Language::LANGCODE_NOT_SPECIFIED][0]['value'], 'Some deleted value');
+    // Check that pre-existing deleted field table is renamed correctly.
+    $field_entity = new Field($deleted_field);
+    $table_name = DatabaseStorageController::_fieldTableName($field_entity);
+    $this->assertEqual("field_deleted_data_" . substr(hash('sha256', $deleted_field['uuid']), 0, 10), $table_name);
+    $this->assertTrue(db_table_exists($table_name));
 
     // Check that creation of a new node works as expected.
     $value = $this->randomName();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php
index 2503aa5..c8825af 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UserPictureUpgradePathTest.php
@@ -52,7 +52,7 @@ public function testUserPictureUpgrade() {
 
     // Check file usage for the default image.
     $usage = file_usage()->listUsage($file);
-    $field = field_info_field('user_picture');
+    $field = field_info_field('user', 'user_picture');
     $this->assertTrue(isset($usage['image']['default_image'][$field['uuid']]));
 
     $this->assertEqual($instance['settings']['max_resolution'], '800x800', 'User picture maximum resolution has been migrated.');
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install
index d6f1b34..346eccb 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.install
+++ b/core/modules/system/tests/modules/entity_test/entity_test.install
@@ -9,14 +9,6 @@
  * Implements hook_install().
  */
 function entity_test_install() {
-  // Auto-create a field for testing.
-  entity_create('field_entity', array(
-    'field_name' => 'field_test_text',
-    'type' => 'text',
-    'cardinality' => 1,
-    'translatable' => FALSE,
-  ))->save();
-
   $entity_types = array(
     'entity_test',
     'entity_test_rev',
@@ -24,6 +16,14 @@ function entity_test_install() {
     'entity_test_mulrev',
   );
   foreach ($entity_types as $entity_type) {
+    // Auto-create fields for testing.
+    entity_create('field_entity', array(
+      'name' => 'field_test_text',
+      'entity_type' => $entity_type,
+      'type' => 'text',
+      'cardinality' => 1,
+      'translatable' => FALSE,
+    ))->save();
     entity_create('field_instance', array(
       'entity_type' => $entity_type,
       'field_name' => 'field_test_text',
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/field/widget/TaxonomyAutocompleteWidget.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/field/widget/TaxonomyAutocompleteWidget.php
index d2ad106..0ce4fdd 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/field/widget/TaxonomyAutocompleteWidget.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/field/widget/TaxonomyAutocompleteWidget.php
@@ -73,7 +73,7 @@ public function formElement(FieldInterface $items, $delta, array $element, $lang
     $element += array(
       '#type' => 'textfield',
       '#default_value' => taxonomy_implode_tags($tags),
-      '#autocomplete_path' => $this->getSetting('autocomplete_path') . '/' . $this->fieldDefinition->getFieldName(),
+      '#autocomplete_path' => $this->getSetting('autocomplete_path') . '/' . $items->getParent()->entityType() . '/' . $this->fieldDefinition->getFieldName(),
       '#size' => $this->getSetting('size'),
       '#placeholder' => $this->getSetting('placeholder'),
       '#maxlength' => 1024,
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php
index 0257435..b95a633 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php
@@ -135,10 +135,10 @@ public function getArgument() {
       // Just check, if a node could be detected.
       if ($node) {
         $taxonomy = array();
-        $fields = field_info_instances('node', $node->type);
-        foreach ($fields as $name => $info) {
-          $field_info = field_info_field($name);
-          if ($field_info['type'] == 'taxonomy_term_reference') {
+        $instances = field_info_instances('node', $node->type);
+        foreach ($instances as $instance) {
+          $field = $instance->getField();
+          if ($field['type'] == 'taxonomy_term_reference') {
             foreach ($node->get($name) as $item) {
               $taxonomy[$item->target_id] = $field_info['settings']['allowed_values'][0]['vocabulary'];
             }
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php
index c9b8078..905cc14 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/RssTest.php
@@ -38,7 +38,8 @@ function setUp() {
     $this->field_name = 'taxonomy_' . $this->vocabulary->id();
 
     $this->field = entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php
index 2308f63..c5e96f7 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php
@@ -45,7 +45,8 @@ public function setUp() {
     $vocabulary->save();
 
     entity_create('field_entity', array(
-      'field_name' => 'field_test_taxonomy',
+      'name' => 'field_test_taxonomy',
+      'entity_type' => 'entity_test',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php
index c19ce9d..4cfb173 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php
@@ -43,7 +43,8 @@ function setUp() {
     // Set up a field and instance.
     $this->field_name = drupal_strtolower($this->randomName());
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'entity_test',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
@@ -125,7 +126,7 @@ function testTaxonomyTermFieldMultipleVocabularies() {
     $this->assertNoText($term2->label(), 'Term 2 name is not displayed.');
 
     // Verify that field and instance settings are correct.
-    $field_info = field_info_field($this->field_name);
+    $field_info = field_info_field('entity_test', $this->field_name);
     $this->assertEqual(count($field_info['settings']['allowed_values']), 1, 'Only one vocabulary is allowed for the field.');
 
     // The widget should still be displayed.
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php
index e893001..1fee5c3 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php
@@ -46,7 +46,8 @@ function setUp() {
     // Setup a field and instance.
     $this->field_name = drupal_strtolower($this->randomName());
     $this->field = entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'entity_test',
       'type' => 'taxonomy_term_reference',
       'settings' => array(
         'allowed_values' => array(
@@ -159,7 +160,7 @@ function testTaxonomyTermFieldChangeMachineName() {
     $this->vocabulary->save();
 
     // Check that the field instance is still attached to the vocabulary.
-    $field = field_info_field($this->field_name);
+    $field = field_info_field('entity_test', $this->field_name);
     $allowed_values = $field['settings']['allowed_values'];
     $this->assertEqual($allowed_values[0]['vocabulary'], $new_name, 'Index 0: Machine name was updated correctly.');
     $this->assertEqual($allowed_values[1]['vocabulary'], $new_name, 'Index 1: Machine name was updated correctly.');
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
index 991f274..0697de2 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
@@ -34,7 +34,8 @@ function setUp() {
 
     $this->field_name_1 = drupal_strtolower($this->randomName());
     entity_create('field_entity', array(
-      'field_name' => $this->field_name_1,
+      'name' => $this->field_name_1,
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
@@ -64,7 +65,8 @@ function setUp() {
 
     $this->field_name_2 = drupal_strtolower($this->randomName());
     entity_create('field_entity', array(
-      'field_name' => $this->field_name_2,
+      'name' => $this->field_name_2,
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
index 99ab62e..5896c09 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
@@ -29,7 +29,8 @@ function setUp() {
     $this->vocabulary = $this->createVocabulary();
 
     $field = array(
-      'field_name' => 'taxonomy_' . $this->vocabulary->id(),
+      'name' => 'taxonomy_' . $this->vocabulary->id(),
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
@@ -219,21 +220,21 @@ function testNodeTermCreationAndDeletion() {
     // Test autocomplete on term 3, which contains a comma.
     // The term will be quoted, and the " will be encoded in unicode (\u0022).
     $input = substr($term_objects['term3']->label(), 0, 3);
-    $json = $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input)));
+    $json = $this->drupalGet('taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input)));
     $this->assertEqual($json, '{"\u0022' . $term_objects['term3']->label() . '\u0022":"' . $term_objects['term3']->label() . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->label())));
 
     // Test autocomplete on term 4 - it is alphanumeric only, so no extra
     // quoting.
     $input = substr($term_objects['term4']->label(), 0, 3);
-    $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input)));
+    $this->drupalGet('taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id(), array('query' => array('q' => $input)));
     $this->assertRaw('{"' . $term_objects['term4']->label() . '":"' . $term_objects['term4']->label() . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term4']->label())));
 
     // Test taxonomy autocomplete with a nonexistent field.
     $field_name = $this->randomName();
     $tag = $this->randomName();
     $message = t("Taxonomy field @field_name not found.", array('@field_name' => $field_name));
-    $this->assertFalse(field_info_field($field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name)));
-    $this->drupalGet('taxonomy/autocomplete/' . $field_name, array('query' => array('q' => $tag)));
+    $this->assertFalse(field_info_field('node', $field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name)));
+    $this->drupalGet('taxonomy/autocomplete/node/' . $field_name, array('query' => array('q' => $tag)));
     $this->assertRaw($message, 'Autocomplete returns correct error message when the taxonomy field does not exist.');
   }
 
@@ -257,7 +258,7 @@ function testTermAutocompletion() {
     // Try to autocomplete a term name that matches both terms.
     // We should get both term in a json encoded string.
     $input = '10/';
-    $path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id();
+    $path = 'taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id();
     // The result order is not guaranteed, so check each term separately.
     $result = $this->drupalGet($path, array('query' => array('q' => $input)));
     $data = drupal_json_decode($result);
@@ -267,14 +268,14 @@ function testTermAutocompletion() {
     // Try to autocomplete a term name that matches first term.
     // We should only get the first term in a json encoded string.
     $input = '10/16';
-    $path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id();
+    $path = 'taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id();
     $this->drupalGet($path, array('query' => array('q' => $input)));
     $target = array($first_term->label() => check_plain($first_term->label()));
     $this->assertRaw(drupal_json_encode($target), 'Autocomplete returns only the expected matching term.');
 
     // Try to autocomplete a term name with both a comma and a slash.
     $input = '"term with, comma and / a';
-    $path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->id();
+    $path = 'taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id();
     $this->drupalGet($path, array('query' => array('q' => $input)));
     $n = $third_term->label();
     // Term names containing commas or quotes must be wrapped in quotes.
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php
index 35f4555..d785f2f 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TokenReplaceTest.php
@@ -30,7 +30,8 @@ function setUp() {
     $this->langcode = Language::LANGCODE_NOT_SPECIFIED;
     $this->field_name = 'taxonomy_' . $this->vocabulary->id();
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/Views/TaxonomyTestBase.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/Views/TaxonomyTestBase.php
index e7c7031..6e5e7ff 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/Views/TaxonomyTestBase.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/Views/TaxonomyTestBase.php
@@ -78,7 +78,8 @@ protected function mockStandardInstall() {
     $this->vocabulary->save();
     $this->field_name = 'field_' . $this->vocabulary->id();
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       // Set cardinality to unlimited for tagging.
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
index 8901db1..13d4762 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php
@@ -155,7 +155,8 @@ function testTaxonomyVocabularyLoadMultiple() {
   function testTaxonomyVocabularyChangeMachineName() {
     // Add a field instance to the vocabulary.
     entity_create('field_entity', array(
-      'field_name' => 'field_test',
+      'name' => 'field_test',
+      'entity_type' => 'taxonomy_term',
       'type' => 'test_field',
     ))->save();
     entity_create('field_instance', array(
@@ -187,7 +188,8 @@ function testUninstallReinstall() {
     // removed when the module is uninstalled.
     $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
     $this->field_definition = array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'taxonomy_term',
       'type' => 'text',
       'cardinality' => 4
     );
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
index f89db3a..191e718 100644
--- a/core/modules/taxonomy/taxonomy.install
+++ b/core/modules/taxonomy/taxonomy.install
@@ -6,12 +6,17 @@
  */
 
 use Drupal\Component\Uuid\Uuid;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
  * Implements hook_uninstall().
  */
 function taxonomy_uninstall() {
+  Drupal::entityManager()->addNamespaces(new ArrayIterator(array(
+    'Drupal\taxonomy' => DRUPAL_ROOT . '/core/modules/taxonomy/lib',
+  )));
+  drupal_classloader_register('taxonomy', 'core/modules/taxonomy');
   // Remove taxonomy_term bundles.
   $config_names = config_get_storage_names_with_prefix('taxonomy.vocabulary.');
   foreach ($config_names as $config_name) {
@@ -199,9 +204,9 @@ function taxonomy_field_schema($field) {
  */
 function taxonomy_update_dependencies() {
   // Convert the 'tid' column of the taxonomy reference field to 'target_id'
-  // after fields and instances have been moved to the config system.
+  // after the field tables have been reorganized.
   $dependencies['taxonomy'][8007] = array(
-    'field' => 8003,
+    'field' => 8006,
   );
   return $dependencies;
 }
@@ -356,16 +361,16 @@ function taxonomy_update_8007() {
   foreach (config_get_storage_names_with_prefix('field.field.') as $config_name) {
     $field_config = Drupal::config($config_name);
     // Only update taxonomy reference fields that use the default SQL storage.
-    if ($field_config->get('type') == 'taxonomy_term_reference' && $field_config->get('storage.type') == 'field_sql_storage') {
+    if ($field_config->get('type') == 'taxonomy_term_reference') {
       $field = new Field($field_config->get());
 
       $tables = array(
-        _field_sql_storage_tablename($field),
-        _field_sql_storage_revision_tablename($field),
+        DatabaseStorageController::_fieldTableName($field),
+        DatabaseStorageController::_fieldRevisionTableName($field),
       );
 
       foreach ($tables as $table_name) {
-        db_change_field($table_name, $field->id() . '_tid', $field->id() . '_target_id', array(
+        db_change_field($table_name, $field->name . '_tid', $field->name . '_target_id', array(
           'description' => 'The ID of the target entity.',
           'type' => 'int',
           'unsigned' => TRUE,
@@ -373,8 +378,8 @@ function taxonomy_update_8007() {
         ));
 
         // Change the index.
-        db_drop_index($table_name, $field->id() . '_tid');
-        db_add_index($table_name, $field->id() . '_target_id', array($field->id() . '_target_id'));
+        db_drop_index($table_name, $field->name . '_tid');
+        db_add_index($table_name, $field->name . '_target_id', array($field->name . '_target_id'));
       }
 
       // Update the indexes in field config as well.
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 090baaf..f826c5f 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -9,6 +9,7 @@
 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\DatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\Field\FieldDefinitionInterface;
 
@@ -292,10 +293,10 @@ function taxonomy_menu() {
     'type' => MENU_CALLBACK,
     'file' => 'taxonomy.pages.inc',
   );
-  $items['taxonomy/autocomplete/%'] = array(
+  $items['taxonomy/autocomplete/%/%'] = array(
     'title' => 'Autocomplete taxonomy',
     'page callback' => 'taxonomy_autocomplete',
-    'page arguments' => array(2),
+    'page arguments' => array(2, 3),
     'access arguments' => array('access content'),
     'type' => MENU_CALLBACK,
     'file' => 'taxonomy.pages.inc',
@@ -1128,18 +1129,18 @@ function taxonomy_node_insert(EntityInterface $node) {
 function taxonomy_build_node_index($node) {
   // We maintain a denormalized table of term/node relationships, containing
   // only data for current, published nodes.
-  $status = NULL;
-  if (Drupal::config('taxonomy.settings')->get('maintain_index_table')) {
-    // If a node property is not set in the node object when $node->save() is
-    // called, the old value from $node->original is used.
-    if (!empty($node->original)) {
-      $status = (int)(!empty($node->status) || (!isset($node->status) && !empty($node->original->status)));
-      $sticky = (int)(!empty($node->sticky) || (!isset($node->sticky) && !empty($node->original->sticky)));
-    }
-    else {
-      $status = (int)(!empty($node->status));
-      $sticky = (int)(!empty($node->sticky));
-    }
+  if (!Drupal::config('taxonomy.settings')->get('maintain_index_table') || !(Drupal::entityManager()->getStorageController('node') instanceof DatabaseStorageController)) {
+    return;
+  }
+  // If a node property is not set in the node object when $node->save() is
+  // called, the old value from $node->original is used.
+  if (!empty($node->original)) {
+    $status = (int)(!empty($node->status) || (!isset($node->status) && !empty($node->original->status)));
+    $sticky = (int)(!empty($node->sticky) || (!isset($node->sticky) && !empty($node->original->sticky)));
+  }
+  else {
+    $status = (int)(!empty($node->status));
+    $sticky = (int)(!empty($node->sticky));
   }
   // We only maintain the taxonomy index for published nodes.
   if ($status && $node->isDefaultRevision()) {
@@ -1147,8 +1148,8 @@ function taxonomy_build_node_index($node) {
     $tid_all = array();
     foreach (field_info_instances('node', $node->type) as $instance) {
       $field_name = $instance['field_name'];
-      $field = field_info_field($field_name);
-      if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') {
+      $field = $instance->getField();
+      if ($field['module'] == 'taxonomy') {
         // If a field value is not set in the node object when $node->save() is
         // called, the old value from $node->original is used.
         if (isset($node->{$field_name})) {
diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc
index 4baf150..ea6261e 100644
--- a/core/modules/taxonomy/taxonomy.pages.inc
+++ b/core/modules/taxonomy/taxonomy.pages.inc
@@ -98,19 +98,21 @@ function taxonomy_term_feed(Term $term) {
  *   };
  * @endcode
  *
- * @param $field_name
+ * @param string $entity_type
+ *   The entity_type.
+ * @param string $field_name
  *   The name of the term reference field.
  *
  * @see taxonomy_menu()
  * @see taxonomy_field_widget_info()
  */
-function taxonomy_autocomplete($field_name) {
+function taxonomy_autocomplete($entity_type, $field_name) {
   // A comma-separated list of term names entered in the autocomplete form
   // element. Only the last term is used for autocompletion.
   $tags_typed = Drupal::request()->query->get('q');
 
   // Make sure the field exists and is a taxonomy field.
-  if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') {
+  if (!($field = field_info_field($entity_type, $field_name)) || $field['type'] !== 'taxonomy_term_reference') {
     // Error string. The JavaScript handler will realize this is not JSON and
     // will display it as debugging information.
     print t('Taxonomy field @field_name not found.', array('@field_name' => $field_name));
diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc
index 5646268..9b37e9e 100644
--- a/core/modules/taxonomy/taxonomy.views.inc
+++ b/core/modules/taxonomy/taxonomy.views.inc
@@ -7,6 +7,9 @@
  * @ingroup views_module_handlers
  */
 
+use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\field\FieldInterface;
+
 /**
  * Implements hook_views_data().
  */
@@ -335,7 +338,7 @@ function taxonomy_views_data_alter(&$data) {
  *
  * @see field_views_field_default_views_data()
  */
-function taxonomy_field_views_data($field) {
+function taxonomy_field_views_data(FieldInterface $field) {
   $data = field_views_field_default_views_data($field);
   foreach ($data as $table_name => $table_data) {
     foreach ($table_data as $field_name => $field_data) {
@@ -363,40 +366,33 @@ function taxonomy_field_views_data($field) {
  *
  * Views integration to provide reverse relationships on term references.
  */
-function taxonomy_field_views_data_views_data_alter(&$data, $field) {
-  foreach ($field['bundles'] as $entity_type => $bundles) {
-    $entity_info = entity_get_info($entity_type);
-    $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type;
-
-    list($label, $all_labels) = field_views_field_label($field['field_name']);
-    $entity = $entity_info['label'];
-    if ($entity == t('Node')) {
-      $entity = t('Content');
-    }
-
-    $data['taxonomy_term_data'][$pseudo_field_name]['relationship'] = array(
-      'title' => t('@entity using @field', array('@entity' => $entity, '@field' => $label)),
-      'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity, '@field' => $label)),
-      'id' => 'entity_reverse',
-      'field_name' => $field['field_name'],
-      'field table' => _field_sql_storage_tablename($field),
-      'field field' => $field['field_name'] . '_target_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(
-          'field' => 'entity_type',
-          'value' => $entity_type,
-        ),
-        1 => array(
-          'field' => 'deleted',
-          'value' => 0,
-          'numeric' => TRUE,
-        ),
+function taxonomy_field_views_data_views_data_alter(array &$data, FieldInterface $field) {
+  $entity_type = $field['entity_type'];
+  $entity_info = entity_get_info($entity_type);
+  $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type;
+
+  list($label,) = field_views_field_label($entity_type, $field['field_name']);
+  $entity_label = ($entity_info['label'] == t('Node')) ? t('Content') : $entity_info['label'];
+
+  $data['taxonomy_term_data'][$pseudo_field_name]['relationship'] = array(
+    'title' => t('@entity using @field', array('@entity' => $entity_label, '@field' => $label)),
+    'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity_label, '@field' => $label)),
+    'id' => 'entity_reverse',
+    'field_name' => $field['field_name'],
+    'entity_type' => $field['entity_type'],
+    'field table' => DatabaseStorageController::_fieldTableName($field),
+    'field field' => $field['field_name'] . '_target_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(
+        'field' => 'deleted',
+        'value' => 0,
+        'numeric' => TRUE,
       ),
-    );
-  }
+    ),
+  );
 }
 
 /**
diff --git a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php
index 6158f07..8e290ba 100644
--- a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php
+++ b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php
@@ -21,7 +21,6 @@ class TelephoneFieldTest extends WebTestBase {
    */
   public static $modules = array(
     'field',
-    'field_sql_storage',
     'node',
     'telephone'
   );
@@ -54,7 +53,8 @@ function testTelephoneField() {
 
     // Add the telepone field to the article content type.
     entity_create('field_entity', array(
-      'field_name' => 'field_telephone',
+      'name' => 'field_telephone',
+      'entity_type' => 'node',
       'type' => 'telephone',
     ))->save();
     entity_create('field_instance', array(
diff --git a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneItemTest.php b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneItemTest.php
index bd3633b..6049260 100644
--- a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneItemTest.php
+++ b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneItemTest.php
@@ -36,7 +36,8 @@ public function setUp() {
 
     // Create a telephone field and instance for validation.
     entity_create('field_entity', array(
-      'field_name' => 'field_test',
+      'name' => 'field_test',
+      'entity_type' => 'entity_test',
       'type' => 'telephone',
     ))->save();
     entity_create('field_instance', array(
diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
index 0a2b900..f568103 100644
--- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
@@ -26,7 +26,7 @@ class TextPlainUnitTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('entity', 'field', 'field_sql_storage', 'text', 'entity_test', 'system');
+  public static $modules = array('entity', 'field', 'text', 'entity_test', 'system');
 
   /**
    * Contains rendered content.
@@ -68,7 +68,8 @@ function setUp() {
     $this->formatter_settings = array();
 
     $this->field = entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => $this->entity_type,
       'type' => $this->field_type,
       'settings' => $this->field_settings,
     ));
diff --git a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php
index 4627fb5..fb9e901 100644
--- a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php
@@ -50,7 +50,8 @@ function testTextFieldValidation() {
     // Create a field with settings to validate.
     $max_length = 3;
     $this->field = entity_create('field_entity', array(
-      'field_name' => drupal_strtolower($this->randomName()),
+      'name' => drupal_strtolower($this->randomName()),
+      'entity_type' => 'entity_test',
       'type' => 'text',
       'settings' => array(
         'max_length' => $max_length,
@@ -58,7 +59,7 @@ function testTextFieldValidation() {
     ));
     $this->field->save();
     entity_create('field_instance', array(
-      'field_name' => $this->field->id(),
+      'field_name' => $this->field->name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
     ))->save();
@@ -66,8 +67,8 @@ function testTextFieldValidation() {
     // Test validation with valid and invalid values.
     $entity = entity_create('entity_test', array());
     for ($i = 0; $i <= $max_length + 2; $i++) {
-      $entity->{$this->field->id()}->value = str_repeat('x', $i);
-      $violations = $entity->{$this->field->id()}->validate();
+      $entity->{$this->field->name}->value = str_repeat('x', $i);
+      $violations = $entity->{$this->field->name}->validate();
       if ($i <= $max_length) {
         $this->assertEqual(count($violations), 0, "Length $i does not cause validation error when max_length is $max_length");
       }
@@ -90,10 +91,10 @@ function testTextfieldWidgets() {
    */
   function _testTextfieldWidgets($field_type, $widget_type) {
     // Setup a field and instance
-    $entity_type = 'entity_test';
     $this->field_name = drupal_strtolower($this->randomName());
     $this->field = entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'entity_test',
       'type' => $field_type
     ));
     $this->field->save();
@@ -161,7 +162,8 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
     // Setup a field and instance
     $this->field_name = drupal_strtolower($this->randomName());
     $this->field = entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'entity_test',
       'type' => $field_type
     ));
     $this->field->save();
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php b/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php
index eaaf350..8458886 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php
@@ -40,7 +40,8 @@ protected function testUserAdd() {
     // Create a field and an instance.
     $field_name = 'test_field';
     $field = array(
-      'field_name' => $field_name,
+      'name' => $field_name,
+      'entity_type' => 'user',
       'module' => 'image',
       'type' => 'image',
       'cardinality' => 1,
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php b/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php
index 14d885c..c073b95 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php
@@ -23,7 +23,7 @@ class UserEntityTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'user');
+  public static $modules = array('system', 'user', 'field');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php b/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php
index ec86be2..fe53305 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php
@@ -196,8 +196,9 @@ function testRegistrationDefaultValues() {
   function testRegistrationWithUserFields() {
     // Create a field, and an instance on 'user' entity type.
     $field = entity_create('field_entity', array(
+      'name' => 'test_user_field',
+      'entity_type' => 'user',
       'type' => 'test_field',
-      'field_name' => 'test_user_field',
       'cardinality' => 1,
     ));
     $field->save();
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index a441023..59f0086 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -11,7 +11,9 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Password\PasswordInterface;
 use Drupal\Core\Database\Connection;
+use Drupal\field\FieldInfo;
 use Drupal\user\UserDataInterface;
+use Symfony\Component\DependencyInjection\Container;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Entity\DatabaseStorageControllerNG;
 
@@ -46,13 +48,15 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS
    *   An array of entity info for the entity type.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
+   * @param \Drupal\field\FieldInfo $field_info
+   *   The field info service.
    * @param \Drupal\Core\Password\PasswordInterface $password
    *   The password hashing service.
    * @param \Drupal\user\UserDataInterface $user_data
    *   The user data service.
    */
-  public function __construct($entity_type, $entity_info, Connection $database, PasswordInterface $password, UserDataInterface $user_data) {
-    parent::__construct($entity_type, $entity_info, $database);
+  public function __construct($entity_type, $entity_info, Connection $database, FieldInfo $field_info, PasswordInterface $password, UserDataInterface $user_data) {
+    parent::__construct($entity_type, $entity_info, $database, $field_info);
 
     $this->password = $password;
     $this->userData = $user_data;
@@ -66,6 +70,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_type,
       $entity_info,
       $container->get('database'),
+      $container->get('field.info'),
       $container->get('password'),
       $container->get('user.data')
     );
@@ -143,14 +148,9 @@ public function deleteUserRoles(array $uids) {
    * {@inheritdoc}
    */
   protected function invokeHook($hook, EntityInterface $entity) {
-    $function = 'field_attach_' . $hook;
-    // @todo: field_attach_delete_revision() is named the wrong way round,
-    // consider renaming it.
-    if ($function == 'field_attach_revision_delete') {
-      $function = 'field_attach_delete_revision';
-    }
-    if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
-      $function($entity);
+    $method = Container::camelize('field_' . $hook);
+    if (!empty($this->entityInfo['fieldable']) && method_exists($this, $method)) {
+      $this->$method($entity);
     }
 
     // Invoke the hook.
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index 4a48efb..6d15166 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -268,7 +268,8 @@ function user_install() {
  */
 function user_install_picture_field() {
   $field = array(
-    'field_name' => 'user_picture',
+    'name' => 'user_picture',
+    'entity_type' => 'user',
     'module' => 'image',
     'type' => 'image',
     'cardinality' => 1,
@@ -343,9 +344,9 @@ function user_install_picture_field() {
  */
 function user_update_dependencies() {
   // Convert user picture to field after the fields and instances are converted
-  // to ConfigEntities.
+  // to ConfigEntities and the tables are split.
   $dependencies['user'][8011] = array(
-    'field' => 8003,
+    'field' => 8006,
   );
   return $dependencies;
 }
@@ -655,7 +656,8 @@ function user_update_8011() {
 
   // Create the field and instance.
   $field = array(
-    'id' => 'user_picture',
+    'name' => 'user_picture',
+    'entity_type' => 'user',
     'module' => 'image',
     'type' => 'image',
     'cardinality' => 1,
@@ -710,10 +712,9 @@ function user_update_8011() {
   _update_8003_field_create_field($field);
 
   $instance = array(
-    'id' => 'user.user.user_picture',
     'entity_type' => 'user',
-    'label' => 'Picture',
     'bundle' => 'user',
+    'label' => 'Picture',
     'description' => update_variable_get('user_picture_guidelines', ''),
     'required' => FALSE,
     'settings' => array(
@@ -816,9 +817,8 @@ function user_update_8012(&$sandbox) {
     $rows = db_query_range('SELECT uid, picture FROM {users} WHERE picture > 0', 0, 20)->fetchAllKeyed();
     foreach ($rows as $uid => $fid) {
       // Add a row to the field data and revision tables.
-      db_insert('field_data_user_picture')
+      db_insert('user__user_picture')
         ->fields(array(
-          'entity_type' => 'user',
           'bundle' => 'user',
           'entity_id' => $uid,
           'revision_id' => $uid,
@@ -827,9 +827,8 @@ function user_update_8012(&$sandbox) {
           'user_picture_target_id' => $fid,
         ))
         ->execute();
-      db_insert('field_revision_user_picture')
+      db_insert('user_revision__user_picture')
         ->fields(array(
-          'entity_type' => 'user',
           'bundle' => 'user',
           'entity_id' => $uid,
           'revision_id' => $uid,
diff --git a/core/modules/views/config/views.view.comments_recent.yml b/core/modules/views/config/views.view.comments_recent.yml
index 378170d..9b9db7d 100644
--- a/core/modules/views/config/views.view.comments_recent.yml
+++ b/core/modules/views/config/views.view.comments_recent.yml
@@ -378,7 +378,7 @@ display:
           provider: comment
         comment:
           id: comment
-          table: field_data_comment_body
+          table: comment__comment_body
           field: comment_body
           label: ''
           plugin_id: field
diff --git a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
index 066f518..f3321f8 100644
--- a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
@@ -62,7 +62,8 @@ protected function setUp() {
     // Setup a field and instance.
     $this->field_name = drupal_strtolower($this->randomName());
     entity_create('field_entity', array(
-      'field_name' => $this->field_name,
+      'name' => $this->field_name,
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'settings' => array(
         'allowed_values' => array(
diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
index 2f870d8..c7ec4e0 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
@@ -56,7 +56,8 @@ function setUp() {
 
     // Create the tag field itself.
     $this->tag_field = entity_create('field_entity', array(
-      'field_name' => 'field_views_testing_tags',
+      'name' => 'field_views_testing_tags',
+      'entity_type' => 'node',
       'type' => 'taxonomy_term_reference',
       'cardinality' => FIELD_CARDINALITY_UNLIMITED,
       'settings' => array(
@@ -108,7 +109,7 @@ function testTaggedWith() {
     $node_add_path = 'node/add/' . $this->node_type_with_tags->type;
 
     // Create three nodes, with different tags.
-    $tag_field = $this->tag_field->id() . '[' . Language::LANGCODE_NOT_SPECIFIED . ']';
+    $tag_field = $this->tag_field->name . '[' . Language::LANGCODE_NOT_SPECIFIED . ']';
     $edit = array();
     $edit['title'] = $node_tag1_title = $this->randomName();
     $edit[$tag_field] = 'tag1';
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_admin_ui.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_admin_ui.yml
index 910d770..1c993fb 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_admin_ui.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_exposed_admin_ui.yml
@@ -26,7 +26,7 @@ display:
           provider: views
         body_value:
           id: body_value
-          table: field_data_body
+          table: node__body
           field: body_value
           relationship: none
           group_type: group
diff --git a/core/modules/views_ui/admin.inc b/core/modules/views_ui/admin.inc
index d05888f..aeea6b2 100644
--- a/core/modules/views_ui/admin.inc
+++ b/core/modules/views_ui/admin.inc
@@ -224,7 +224,7 @@ function views_ui_taxonomy_autocomplete_validate($element, &$form_state) {
   if ($tags = $element['#value']) {
     // Get the machine names of the vocabularies we will search, keyed by the
     // vocabulary IDs.
-    $field = field_info_field($element['#field_name']);
+    $field = field_info_field($element['#entity_type'], $element['#field_name']);
     $vocabularies = array();
     if (!empty($field['settings']['allowed_values'])) {
       foreach ($field['settings']['allowed_values'] as $tree) {
diff --git a/core/profiles/standard/config/field.field.field_image.yml b/core/profiles/standard/config/field.field.node.field_image.yml
similarity index 76%
rename from core/profiles/standard/config/field.field.field_image.yml
rename to core/profiles/standard/config/field.field.node.field_image.yml
index 0fc032a..9611082 100644
--- a/core/profiles/standard/config/field.field.field_image.yml
+++ b/core/profiles/standard/config/field.field.node.field_image.yml
@@ -1,4 +1,6 @@
-id: field_image
+id: node.field_image
+name: field_image
+entity_type: node
 type: image
 module: image
 active: '1'
@@ -18,15 +20,9 @@ settings:
     title:
       label: Title
       translatable: true
-storage:
-  type: field_sql_storage
-  module: field_sql_storage
-  active: '1'
-  settings: {  }
 locked: '0'
 cardinality: '1'
 translatable: '0'
-entity_types: {  }
 indexes:
   target_id:
     - target_id
diff --git a/core/profiles/standard/config/field.field.field_tags.yml b/core/profiles/standard/config/field.field.node.field_tags.yml
similarity index 65%
rename from core/profiles/standard/config/field.field.field_tags.yml
rename to core/profiles/standard/config/field.field.node.field_tags.yml
index e0f6952..5cab894 100644
--- a/core/profiles/standard/config/field.field.field_tags.yml
+++ b/core/profiles/standard/config/field.field.node.field_tags.yml
@@ -1,4 +1,6 @@
-id: field_tags
+id: node.field_tags
+name: field_tags
+entity_type: node
 type: taxonomy_term_reference
 module: taxonomy
 active: '1'
@@ -7,15 +9,9 @@ settings:
     -
       vocabulary: tags
       parent: 0
-storage:
-  type: field_sql_storage
-  module: field_sql_storage
-  active: '1'
-  settings: {  }
 locked: '0'
 cardinality: '-1'
 translatable: '0'
-entity_types: {  }
 indexes:
   target_id:
     - target_id
