diff --git a/core/includes/form.inc b/core/includes/form.inc
index 65ef9bc..7b2a932 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -3542,6 +3542,7 @@ function form_process_machine_name($element, &$form_state) {
   // 'source' only) would leave all other properties undefined, if the defaults
   // were defined in hook_element_info(). Therefore, we apply the defaults here.
   $element['#machine_name'] += array(
+    // @todo Use 'label' by default.
     'source' => array('name'),
     'target' => '#' . $element['#id'],
     'label' => t('Machine name'),
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index 10bf925..cfede74 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -320,6 +320,7 @@ class Config {
    * Deletes the configuration object.
    */
   public function delete() {
+    // @todo Consider to remove the pruning of data for Config::delete().
     $this->data = array();
     $this->storage->delete($this->name);
     $this->isNew = TRUE;
diff --git a/core/lib/Drupal/Core/File/FileStorageController.php b/core/lib/Drupal/Core/File/FileStorageController.php
index 940e1fd..dd8ee7a 100644
--- a/core/lib/Drupal/Core/File/FileStorageController.php
+++ b/core/lib/Drupal/Core/File/FileStorageController.php
@@ -8,7 +8,7 @@
 namespace Drupal\Core\File;
 
 use Drupal\entity\DatabaseStorageController;
-use Drupal\entity\EntityInterface;
+use Drupal\entity\StorableInterface;
 
 /**
  * File storage controller for files.
@@ -34,7 +34,7 @@ class FileStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::presave().
    */
-  protected function preSave(EntityInterface $entity) {
+  protected function preSave(StorableInterface $entity) {
     $entity->timestamp = REQUEST_TIME;
     $entity->filesize = filesize($entity->uri);
     if (!isset($entity->langcode)) {
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
index 3ca70a9..59296a6 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\comment;
 
-use Drupal\entity\EntityInterface;
+use Drupal\entity\StorableInterface;
 use Drupal\entity\DatabaseStorageController;
 use LogicException;
 
@@ -58,7 +58,7 @@ class CommentStorageController extends DatabaseStorageController {
    * @see comment_int_to_alphadecimal()
    * @see comment_alphadecimal_to_int()
    */
-  protected function preSave(EntityInterface $comment) {
+  protected function preSave(StorableInterface $comment) {
     global $user;
 
     if (!isset($comment->status)) {
@@ -151,7 +151,7 @@ class CommentStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::postSave().
    */
-  protected function postSave(EntityInterface $comment, $update) {
+  protected function postSave(StorableInterface $comment, $update) {
     $this->releaseThreadLock();
     // Update the {node_comment_statistics} table prior to executing the hook.
     $this->updateNodeStatistics($comment->nid);
diff --git a/core/modules/config/config.api.php b/core/modules/config/config.api.php
index f0c3afa..6aef002 100644
--- a/core/modules/config/config.api.php
+++ b/core/modules/config/config.api.php
@@ -31,12 +31,12 @@
  *   A configuration object containing the old configuration data.
  */
 function MODULE_config_import_create($name, $new_config, $old_config) {
-  // Only configurable thingies require custom handling. Any other module
+  // Only configurable entities require custom handling. Any other module
   // settings can be synchronized directly.
   if (strpos($name, 'config_test.dynamic.') !== 0) {
     return FALSE;
   }
-  $config_test = new ConfigTest($new_config);
+  $config_test = entity_create('config_test', $new_config->get());
   $config_test->save();
   return TRUE;
 }
@@ -60,13 +60,25 @@ function MODULE_config_import_create($name, $new_config, $old_config) {
  *   A configuration object containing the old configuration data.
  */
 function MODULE_config_import_change($name, $new_config, $old_config) {
-  // Only configurable thingies require custom handling. Any other module
+  // Only configurable entities require custom handling. Any other module
   // settings can be synchronized directly.
   if (strpos($name, 'config_test.dynamic.') !== 0) {
     return FALSE;
   }
-  $config_test = new ConfigTest($new_config);
-  $config_test->setOriginal($old_config);
+
+  // @todo Make this less ugly.
+  $id = substr($name, strlen(ConfigTest::getConfigPrefix()) + 1);
+  $config_test = entity_load('config_test', $id);
+
+  $config_test->original = clone $config_test;
+  foreach ($old_config->get() as $property => $value) {
+    $config_test->original->$property = $value;
+  }
+
+  foreach ($new_config->get() as $property => $value) {
+    $config_test->$property = $value;
+  }
+
   $config_test->save();
   return TRUE;
 }
@@ -90,7 +102,7 @@ function MODULE_config_import_change($name, $new_config, $old_config) {
  *   A configuration object containing the old configuration data.
  */
 function MODULE_config_import_delete($name, $new_config, $old_config) {
-  // Only configurable thingies require custom handling. Any other module
+  // Only configurable entities require custom handling. Any other module
   // settings can be synchronized directly.
   if (strpos($name, 'config_test.dynamic.') !== 0) {
     return FALSE;
@@ -100,8 +112,8 @@ function MODULE_config_import_delete($name, $new_config, $old_config) {
   //   But that is impossible currently, since the config system only knows
   //   about deleted and added changes. Introduce an 'old_ID' key within
   //   config objects as a standard?
-  $config_test = new ConfigTest($old_config);
-  $config_test->delete();
+  $id = substr($name, strlen(ConfigTest::getConfigPrefix()) + 1);
+  config_test_delete($id);
   return TRUE;
 }
 
diff --git a/core/modules/config/lib/Drupal/config/ConfigStorageController.php b/core/modules/config/lib/Drupal/config/ConfigStorageController.php
new file mode 100644
index 0000000..b9bae2d
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/ConfigStorageController.php
@@ -0,0 +1,346 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\ConfigStorageController.
+ */
+
+namespace Drupal\config;
+
+use Drupal\Component\Uuid\Uuid;
+use Drupal\entity\StorableInterface;
+use Drupal\entity\EntityStorageControllerInterface;
+
+/**
+ * Defines the storage controller class for configurable entities.
+ */
+class ConfigStorageController implements EntityStorageControllerInterface {
+
+  /**
+   * Entity type for this controller instance.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * Array of information about the entity.
+   *
+   * @var array
+   *
+   * @see entity_get_info()
+   */
+  protected $entityInfo;
+
+  /**
+   * Additional arguments to pass to hook_TYPE_load().
+   *
+   * Set before calling Drupal\config\ConfigStorageController::attachLoad().
+   *
+   * @var array
+   */
+  protected $hookLoadArguments;
+
+  /**
+   * Name of the entity's ID field in the entity database table.
+   *
+   * @var string
+   */
+  protected $idKey;
+
+  /**
+   * Implements Drupal\entity\EntityStorageControllerInterface::__construct().
+   *
+   * Sets basic variables.
+   */
+  public function __construct($entityType) {
+    $this->entityType = $entityType;
+    $this->entityInfo = entity_get_info($entityType);
+    $this->hookLoadArguments = array();
+    $this->idKey = $this->entityInfo['entity keys']['id'];
+
+    // The UUID key and property is hard-coded for all configurables.
+    $this->uuidKey = 'uuid';
+  }
+
+  /**
+   * Implements Drupal\entity\EntityStorageControllerInterface::resetCache().
+   */
+  public function resetCache(array $ids = NULL) {
+    // The configuration system is fast enough and/or implements its own
+    // (advanced) caching mechanism already.
+  }
+
+  /**
+   * Implements Drupal\entity\EntityStorageControllerInterface::load().
+   */
+  public function load($ids = array(), $conditions = array()) {
+    $entities = array();
+
+    // Create a new variable which is either a prepared version of the $ids
+    // array for later comparison with the entity cache, or FALSE if no $ids
+    // were passed.
+    $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
+
+    // Load any remaining entities. This is the case if $ids
+    // is set to FALSE (so we load all entities),
+    // or if $conditions was passed without $ids.
+    if ($ids === FALSE || $ids || ($conditions && !$passed_ids)) {
+      $queried_entities = $this->buildQuery($ids, $conditions);
+    }
+
+    // Pass all entities loaded from the database through $this->attachLoad(),
+    // which calls the
+    // entity type specific load callback, for example hook_node_type_load().
+    if (!empty($queried_entities)) {
+      $this->attachLoad($queried_entities);
+      $entities += $queried_entities;
+    }
+
+    // Ensure that the returned array is ordered the same as the original
+    // $ids array if this was passed in and remove any invalid ids.
+    if ($passed_ids) {
+      // Remove any invalid ids from the array.
+      $passed_ids = array_intersect_key($passed_ids, $entities);
+      foreach ($entities as $entity) {
+        $passed_ids[$entity->{$this->idKey}] = $entity;
+      }
+      $entities = $passed_ids;
+    }
+
+    return $entities;
+  }
+
+  /**
+   * Builds the query to load the entity.
+   *
+   * This has full revision support. For entities requiring special queries,
+   * the class can be extended, and the default query can be constructed by
+   * calling parent::buildQuery(). This is usually necessary when the object
+   * being loaded needs to be augmented with additional data from another
+   * table, such as loading node type into comments or vocabulary machine name
+   * into terms, however it can also support $conditions on different tables.
+   * See Drupal\comment\CommentStorageController::buildQuery() or
+   * Drupal\taxonomy\TermStorageController::buildQuery() for examples.
+   *
+   * @param $ids
+   *   An array of entity IDs, or FALSE to load all entities.
+   * @param $conditions
+   *   An array of conditions in the form 'field' => $value.
+   * @param $revision_id
+   *   The ID of the revision to load, or FALSE if this query is asking for the
+   *   most current revision(s).
+   *
+   * @return SelectQuery
+   *   A SelectQuery object for loading the entity.
+   */
+  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
+    $config_class = $this->entityInfo['entity class'];
+    $prefix = $config_class::getConfigPrefix() . '.';
+
+    // @todo Handle $conditions?
+    if ($ids === FALSE) {
+      $names = drupal_container()->get('config.storage')->listAll($prefix);
+      $result = array();
+      foreach ($names as $name) {
+        $config = config($name);
+        $result[$config->get($this->idKey)] = new $config_class($config->get(), $this->entityType);
+      }
+      return $result;
+    }
+    else {
+      $result = array();
+      foreach ($ids as $id) {
+        $config = config($prefix . $id);
+        if (!$config->isNew()) {
+          $result[$id] = new $config_class($config->get(), $this->entityType);
+        }
+      }
+      return $result;
+    }
+  }
+
+  /**
+   * Attaches data to entities upon loading.
+   *
+   * This will attach fields, if the entity is fieldable. It calls
+   * hook_entity_load() for modules which need to add data to all entities.
+   * It also calls hook_TYPE_load() on the loaded entities. For example
+   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
+   * expects special parameters apart from the queried entities, you can set
+   * $this->hookLoadArguments prior to calling the method.
+   * See Drupal\node\NodeStorageController::attachLoad() for an example.
+   *
+   * @param $queried_entities
+   *   Associative array of query results, keyed on the entity ID.
+   * @param $revision_id
+   *   ID of the revision that was loaded, or FALSE if the most current revision
+   *   was loaded.
+   */
+  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
+    // Call hook_entity_load().
+    foreach (module_implements('entity_load') as $module) {
+      $function = $module . '_entity_load';
+      $function($queried_entities, $this->entityType);
+    }
+    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
+    // always the queried entities, followed by additional arguments set in
+    // $this->hookLoadArguments.
+    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
+    foreach (module_implements($this->entityType . '_load') as $module) {
+      call_user_func_array($module . '_' . $this->entityType . '_load', $args);
+    }
+  }
+
+  /**
+   * Implements Drupal\entity\EntityStorageControllerInterface::create().
+   */
+  public function create(array $values) {
+    $class = isset($this->entityInfo['entity class']) ? $this->entityInfo['entity class'] : 'Drupal\entity\Entity';
+
+    $entity = new $class($values, $this->entityType);
+
+    // Assign a new UUID if there is none yet.
+    if (!isset($entity->{$this->uuidKey})) {
+      $uuid = new Uuid();
+      $entity->{$this->uuidKey} = $uuid->generate();
+    }
+
+    return $entity;
+  }
+
+  /**
+   * Implements Drupal\entity\EntityStorageControllerInterface::delete().
+   */
+  public function delete($ids) {
+    $entities = $ids ? $this->load($ids) : FALSE;
+    if (!$entities) {
+      // If no IDs or invalid IDs were passed, do nothing.
+      return;
+    }
+
+    $this->preDelete($entities);
+    foreach ($entities as $id => $entity) {
+      $this->invokeHook('predelete', $entity);
+    }
+
+    foreach ($entities as $id => $entity) {
+      $config = config($entity::getConfigPrefix() . '.' . $entity->id());
+      $config->delete();
+    }
+
+    $this->postDelete($entities);
+    foreach ($entities as $id => $entity) {
+      $this->invokeHook('delete', $entity);
+    }
+  }
+
+  /**
+   * Implements Drupal\entity\EntityStorageControllerInterface::save().
+   */
+  public function save(StorableInterface $entity) {
+    $prefix = $entity::getConfigPrefix() . '.';
+
+    // Load the stored entity, if any.
+    if ($entity->getOriginalID()) {
+      $id = $entity->getOriginalID();
+    }
+    else {
+      $id = $entity->id();
+    }
+    $config = config($prefix . $id);
+    $config->setName($prefix . $entity->id());
+
+    if (!$config->isNew() && !isset($entity->original)) {
+      $entity->original = entity_load_unchanged($this->entityType, $id);
+    }
+
+    $this->preSave($entity);
+    $this->invokeHook('presave', $entity);
+
+    // Configuration objects do not have a schema. Extract all key names from
+    // class properties.
+    $class_info = new \ReflectionClass($entity);
+    foreach ($class_info->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
+      $name = $property->getName();
+      $config->set($name, $entity->$name);
+    }
+
+    if (!$config->isNew()) {
+      $return = SAVED_NEW;
+      $config->save();
+      $this->postSave($entity, TRUE);
+      $this->invokeHook('update', $entity);
+    }
+    else {
+      $return = SAVED_UPDATED;
+      $config->save();
+      $entity->enforceIsNew(FALSE);
+      $this->postSave($entity, FALSE);
+      $this->invokeHook('insert', $entity);
+    }
+
+    unset($entity->original);
+
+    return $return;
+  }
+
+  /**
+   * Acts on an entity before the presave hook is invoked.
+   *
+   * Used before the entity is saved and before invoking the presave hook.
+   */
+  protected function preSave(StorableInterface $entity) {
+  }
+
+  /**
+   * Acts on a saved entity before the insert or update hook is invoked.
+   *
+   * Used after the entity is saved, but before invoking the insert or update
+   * hook.
+   *
+   * @param $update
+   *   (bool) TRUE if the entity has been updated, or FALSE if it has been
+   *   inserted.
+   */
+  protected function postSave(StorableInterface $entity, $update) {
+    // Delete the original configurable entity, in case the entity ID was
+    // renamed.
+    if ($update && !empty($entity->original) && $entity->{$this->idKey} !== $entity->original->{$this->idKey}) {
+      // @todo This should just delete the original config object without going
+      //   through the API, no?
+      $entity->original->delete();
+    }
+  }
+
+  /**
+   * Acts on entities before they are deleted.
+   *
+   * Used before the entities are deleted and before invoking the delete hook.
+   */
+  protected function preDelete($entities) {
+  }
+
+  /**
+   * Acts on deleted entities before the delete hook is invoked.
+   *
+   * Used after the entities are deleted but before invoking the delete hook.
+   */
+  protected function postDelete($entities) {
+  }
+
+  /**
+   * Invokes a hook on behalf of the entity.
+   *
+   * @param $hook
+   *   One of 'presave', 'insert', 'update', 'predelete', or 'delete'.
+   * @param $entity
+   *   The entity object.
+   */
+  protected function invokeHook($hook, StorableInterface $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);
+  }
+}
diff --git a/core/modules/config/lib/Drupal/config/ConfigurableBase.php b/core/modules/config/lib/Drupal/config/ConfigurableBase.php
new file mode 100644
index 0000000..a8bc318
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/ConfigurableBase.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\ConfigurableBase.
+ */
+
+namespace Drupal\config;
+
+use Drupal\entity\StorableBase;
+
+/**
+ * Defines a base configurable entity class.
+ */
+abstract class ConfigurableBase extends StorableBase implements ConfigurableInterface {
+
+  /**
+   * The original ID of the configurable entity.
+   *
+   * The ID of a configurable entity is a unique string (machine name). When a
+   * configurable entity is updated and its machine name is renamed, the
+   * original ID needs to be known.
+   *
+   * @var string
+   */
+  protected $originalID;
+
+  /**
+   * Overrides Entity::__construct().
+   */
+  public function __construct(array $values = array(), $entity_type) {
+    parent::__construct($values, $entity_type);
+
+    // Backup the original ID, if any.
+    if ($original_id = $this->id()) {
+      $this->originalID = $original_id;
+    }
+  }
+
+  /**
+   * Implements ConfigurableInterface::getOriginalID().
+   */
+  public function getOriginalID() {
+    return $this->originalID;
+  }
+
+  /**
+   * Overrides Entity::isNew().
+   *
+   * EntityInterface::enforceIsNew() is not supported by configurable entities,
+   * since each Configurable is unique.
+   */
+  final public function isNew() {
+    return !$this->id();
+  }
+
+  /**
+   * Overrides Entity::bundle().
+   *
+   * EntityInterface::bundle() is not supported by configurable entities, since
+   * a Configurable is a bundle.
+   */
+  final public function bundle() {
+    return $this->entityType;
+  }
+
+  /**
+   * Overrides Entity::get().
+   *
+   * EntityInterface::get() implements support for fieldable entities, but
+   * configurable entities are not fieldable.
+   */
+  public function get($property_name, $langcode = NULL) {
+    // @todo: Add support for translatable properties being not fields.
+    return isset($this->{$property_name}) ? $this->{$property_name} : NULL;
+  }
+
+  /**
+   * Overrides Entity::set().
+   *
+   * EntityInterface::set() implements support for fieldable entities, but
+   * configurable entities are not fieldable.
+   */
+  public function set($property_name, $value, $langcode = NULL) {
+    // @todo: Add support for translatable properties being not fields.
+    $this->{$property_name} = $value;
+  }
+
+  /**
+   * Helper callback for uasort() to sort Configurable entities by weight and label.
+   */
+  public static function sort($a, $b) {
+    $a_weight = isset($a->weight) ? $a->weight : 0;
+    $b_weight = isset($b->weight) ? $b->weight : 0;
+    if ($a_weight == $b_weight) {
+      $a_label = $a->label();
+      $b_label = $b->label();
+      return strnatcasecmp($a_label, $b_label);
+    }
+    return ($a_weight < $b_weight) ? -1 : 1;
+  }
+}
diff --git a/core/modules/config/lib/Drupal/config/ConfigurableInterface.php b/core/modules/config/lib/Drupal/config/ConfigurableInterface.php
new file mode 100644
index 0000000..4d75122
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/ConfigurableInterface.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\ConfigurableInterface.
+ */
+
+namespace Drupal\config;
+
+use Drupal\entity\StorableInterface;
+
+/**
+ * Defines the interface common for all configurable entities.
+ */
+interface ConfigurableInterface extends StorableInterface {
+
+  /**
+   * Returns the original ID.
+   *
+   * @return string|null
+   *   The original ID, if any.
+   */
+  public function getOriginalID();
+
+  /**
+   * Returns the configuration object name prefix of the configurable entity.
+   *
+   * @return string
+   *   The configuration object name prefix; e.g., for a 'node.type.article'
+   *   configurable entity, this returns 'node.type'.
+   */
+  public static function getConfigPrefix();
+
+}
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigConfigurableTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigConfigurableTest.php
new file mode 100644
index 0000000..0867812
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigConfigurableTest.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\Tests\ConfigConfigurableTest.
+ */
+
+namespace Drupal\config\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests configurable entities.
+ */
+class ConfigConfigurableTest extends WebTestBase {
+
+  public static $modules = array('config_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Configurable entities',
+      'description' => 'Tests configurable entities.',
+      'group' => 'Configuration',
+    );
+  }
+
+  /**
+   * Tests basic CRUD operations through the UI.
+   */
+  function testCRUD() {
+    // Create a configurable entity.
+    $id = 'thingie';
+    $edit = array(
+      'id' => $id,
+      'label' => 'Thingie',
+    );
+    $this->drupalPost('admin/structure/config_test/add', $edit, 'Save');
+    $this->assertResponse(200);
+    $this->assertText('Thingie');
+
+    // Update the configurable entity.
+    $this->assertLinkByHref('admin/structure/config_test/manage/' . $id);
+    $edit = array(
+      'label' => 'Thongie',
+    );
+    $this->drupalPost('admin/structure/config_test/manage/' . $id, $edit, 'Save');
+    $this->assertResponse(200);
+    $this->assertNoText('Thingie');
+    $this->assertText('Thongie');
+
+    // Delete the configurable entity.
+    $this->assertLinkByHref('admin/structure/config_test/manage/' . $id . '/delete');
+    $this->drupalPost('admin/structure/config_test/manage/' . $id . '/delete', array(), 'Delete');
+    $this->assertResponse(200);
+    $this->assertNoText('Thingie');
+    $this->assertNoText('Thongie');
+
+    // Re-create a configurable entity.
+    $edit = array(
+      'id' => $id,
+      'label' => 'Thingie',
+    );
+    $this->drupalPost('admin/structure/config_test/add', $edit, 'Save');
+    $this->assertResponse(200);
+    $this->assertText('Thingie');
+
+    // Rename the configurable entity's ID/machine name.
+    $this->assertLinkByHref('admin/structure/config_test/manage/' . $id);
+    $new_id = 'zingie';
+    $edit = array(
+      'id' => $new_id,
+      'label' => 'Zingie',
+    );
+    $this->drupalPost('admin/structure/config_test/manage/' . $id, $edit, 'Save');
+    $this->assertResponse(200);
+    $this->assertNoText('Thingie');
+    $this->assertText('Zingie');
+  }
+}
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
index ec92fdf..7265abaa 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
@@ -31,6 +31,33 @@ class ConfigImportTest extends WebTestBase {
     );
   }
 
+  function setUp() {
+    parent::setUp();
+
+    // Clear out any possibly existing hook invocation records.
+    unset($GLOBALS['hook_config_test']);
+  }
+
+  /**
+   * Tests omission of module APIs for bare configuration operations.
+   */
+  function testNoImport() {
+    $dynamic_name = 'config_test.dynamic.default';
+
+    // Verify the default configuration values exist.
+    $config = config($dynamic_name);
+    $this->assertIdentical($config->get('id'), 'default');
+
+    // Verify that a bare config() does not involve module APIs.
+    $this->assertFalse(isset($GLOBALS['hook_config_test']));
+
+    // Export.
+    config_export();
+
+    // Verify that config_export() does not involve module APIs.
+    $this->assertFalse(isset($GLOBALS['hook_config_test']));
+  }
+
   /**
    * Tests deletion of configuration during import.
    */
@@ -64,6 +91,14 @@ class ConfigImportTest extends WebTestBase {
     $this->assertIdentical($config->get('foo'), NULL);
     $config = config($dynamic_name);
     $this->assertIdentical($config->get('id'), NULL);
+
+    // Verify that appropriate module API hooks have been invoked.
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['load']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['presave']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['insert']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['predelete']));
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['delete']));
   }
 
   /**
@@ -100,6 +135,14 @@ class ConfigImportTest extends WebTestBase {
     $this->assertIdentical($config->get('add_me'), 'new value');
     $config = config($dynamic_name);
     $this->assertIdentical($config->get('label'), 'New');
+
+    // Verify that appropriate module API hooks have been invoked.
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['load']));
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['insert']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
   }
 
   /**
@@ -138,5 +181,13 @@ class ConfigImportTest extends WebTestBase {
     $this->assertIdentical($config->get('foo'), 'beer');
     $config = config($dynamic_name);
     $this->assertIdentical($config->get('label'), 'Updated');
+
+    // Verify that appropriate module API hooks have been invoked.
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['load']));
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['insert']));
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['update']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
   }
 }
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
index 7ec6d8e..d730554 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
@@ -26,12 +26,12 @@ class ConfigInstallTest extends WebTestBase {
    */
   function testModuleInstallation() {
     $default_config = 'config_test.system';
-    $default_thingie = 'config_test.dynamic.default';
+    $default_configurable = 'config_test.dynamic.default';
 
     // Verify that default module config does not exist before installation yet.
     $config = config($default_config);
     $this->assertIdentical($config->isNew(), TRUE);
-    $config = config($default_thingie);
+    $config = config($default_configurable);
     $this->assertIdentical($config->isNew(), TRUE);
 
     // Install the test module.
@@ -40,11 +40,20 @@ class ConfigInstallTest extends WebTestBase {
     // Verify that default module config exists.
     $config = config($default_config);
     $this->assertIdentical($config->isNew(), FALSE);
-    $config = config($default_thingie);
+    $config = config($default_configurable);
     $this->assertIdentical($config->isNew(), FALSE);
 
     // Verify that configuration import callback was invoked for the dynamic
-    // thingie.
+    // configurable entity.
     $this->assertTrue($GLOBALS['hook_config_import']);
+
+    // Verify that config_test API hooks were invoked for the dynamic default
+    // configurable entity.
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['load']));
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
+    $this->assertTrue(isset($GLOBALS['hook_config_test']['insert']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
+    $this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
   }
 }
diff --git a/core/modules/config/tests/config_test/config_test.hooks.inc b/core/modules/config/tests/config_test/config_test.hooks.inc
new file mode 100644
index 0000000..80f3381
--- /dev/null
+++ b/core/modules/config/tests/config_test/config_test.hooks.inc
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Fake third-party hook implementations for ConfigTest entities.
+ *
+ * Testing the module/hook system is not the purpose of this test helper module.
+ * Therefore, this file implements hooks on behalf of config_test module for
+ * config_test entity hooks themselves.
+ */
+
+/**
+ * Implements hook_config_test_load().
+ */
+function config_test_config_test_load() {
+  $GLOBALS['hook_config_test']['load'] = __FUNCTION__;
+}
+
+/**
+ * Implements hook_config_test_presave().
+ */
+function config_test_config_test_presave() {
+  $GLOBALS['hook_config_test']['presave'] = __FUNCTION__;
+}
+
+/**
+ * Implements hook_config_test_insert().
+ */
+function config_test_config_test_insert() {
+  $GLOBALS['hook_config_test']['insert'] = __FUNCTION__;
+}
+
+/**
+ * Implements hook_config_test_update().
+ */
+function config_test_config_test_update() {
+  $GLOBALS['hook_config_test']['update'] = __FUNCTION__;
+}
+
+/**
+ * Implements hook_config_test_predelete().
+ */
+function config_test_config_test_predelete() {
+  $GLOBALS['hook_config_test']['predelete'] = __FUNCTION__;
+}
+
+/**
+ * Implements hook_config_test_delete().
+ */
+function config_test_config_test_delete() {
+  $GLOBALS['hook_config_test']['delete'] = __FUNCTION__;
+}
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
index 6fd84a2..c4c01b8 100644
--- a/core/modules/config/tests/config_test/config_test.module
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -1,18 +1,21 @@
 <?php
 
+use Drupal\config_test\ConfigTest;
+
+require_once dirname(__FILE__) . '/config_test.hooks.inc';
+
 /**
  * Implements MODULE_config_import_create().
  */
 function config_test_config_import_create($name, $new_config, $old_config) {
-  // Only configurable thingies require custom handling. Any other module
-  // settings can be synchronized directly.
   if (strpos($name, 'config_test.dynamic.') !== 0) {
     return FALSE;
   }
   // Set a global value we can check in test code.
   $GLOBALS['hook_config_import'] = __FUNCTION__;
 
-  $new_config->save();
+  $config_test = entity_create('config_test', $new_config->get());
+  $config_test->save();
   return TRUE;
 }
 
@@ -20,15 +23,26 @@ function config_test_config_import_create($name, $new_config, $old_config) {
  * Implements MODULE_config_import_change().
  */
 function config_test_config_import_change($name, $new_config, $old_config) {
-  // Only configurable thingies require custom handling. Any other module
-  // settings can be synchronized directly.
   if (strpos($name, 'config_test.dynamic.') !== 0) {
     return FALSE;
   }
   // Set a global value we can check in test code.
   $GLOBALS['hook_config_import'] = __FUNCTION__;
 
-  $new_config->save();
+  // @todo Make this less ugly.
+  $id = substr($name, strlen(ConfigTest::getConfigPrefix()) + 1);
+  $config_test = entity_load('config_test', $id);
+
+  $config_test->original = clone $config_test;
+  foreach ($old_config->get() as $property => $value) {
+    $config_test->original->$property = $value;
+  }
+
+  foreach ($new_config->get() as $property => $value) {
+    $config_test->$property = $value;
+  }
+
+  $config_test->save();
   return TRUE;
 }
 
@@ -36,15 +50,248 @@ function config_test_config_import_change($name, $new_config, $old_config) {
  * Implements MODULE_config_import_delete().
  */
 function config_test_config_import_delete($name, $new_config, $old_config) {
-  // Only configurable thingies require custom handling. Any other module
-  // settings can be synchronized directly.
   if (strpos($name, 'config_test.dynamic.') !== 0) {
     return FALSE;
   }
   // Set a global value we can check in test code.
   $GLOBALS['hook_config_import'] = __FUNCTION__;
 
-  $old_config->delete();
+  $id = substr($name, strlen(ConfigTest::getConfigPrefix()) + 1);
+  config_test_delete($id);
   return TRUE;
 }
 
+/**
+ * Implements hook_entity_info().
+ */
+function config_test_entity_info() {
+  $types['config_test'] = array(
+    'label' => 'Test configuration',
+    'controller class' => 'Drupal\config\ConfigStorageController',
+    'entity class' => 'Drupal\config_test\ConfigTest',
+    'uri callback' => 'config_test_uri',
+    'entity keys' => array(
+      'id' => 'id',
+      'label' => 'label',
+      'uuid' => 'uuid',
+    ),
+  );
+  return $types;
+}
+
+/**
+ * Entity uri callback.
+ *
+ * @param Drupal\config_test\ConfigTest $config_test
+ *   A ConfigTest entity.
+ */
+function config_test_uri(ConfigTest $config_test) {
+  return array(
+    'path' => 'admin/structure/config_test/manage/' . $config_test->id(),
+  );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function config_test_menu() {
+  $items['admin/structure/config_test'] = array(
+    'title' => 'Test configuration',
+    'page callback' => 'config_test_list_page',
+    'access callback' => TRUE,
+  );
+  $items['admin/structure/config_test/add'] = array(
+    'title' => 'Add test configuration',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('config_test_form'),
+    'access callback' => TRUE,
+    'type' => MENU_LOCAL_ACTION,
+  );
+  $items['admin/structure/config_test/manage/%config_test'] = array(
+    'title' => 'Edit test configuration',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('config_test_form', 4),
+    'access callback' => TRUE,
+  );
+  $items['admin/structure/config_test/manage/%config_test/edit'] = array(
+    'title' => 'Edit',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/structure/config_test/manage/%config_test/delete'] = array(
+    'title' => 'Delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('config_test_delete_form', 4),
+    'access callback' => TRUE,
+    'type' => MENU_LOCAL_TASK,
+  );
+  return $items;
+}
+
+/**
+ * Loads a ConfigTest object.
+ *
+ * @param string $id
+ *   The ID of the ConfigTest object to load.
+ */
+function config_test_load($id) {
+  return entity_load('config_test', $id);
+}
+
+/**
+ * Saves a ConfigTest object.
+ *
+ * @param Drupal\config_test\ConfigTest $config_test
+ *   The ConfigTest object to save.
+ */
+function config_test_save(ConfigTest $config_test) {
+  return $config_test->save();
+}
+
+/**
+ * Deletes a ConfigTest object.
+ *
+ * @param string $id
+ *   The ID of the ConfigTest object to delete.
+ */
+function config_test_delete($id) {
+  entity_delete_multiple('config_test', array($id));
+}
+
+/**
+ * Page callback; Lists available ConfigTest objects.
+ */
+function config_test_list_page() {
+  $entities = entity_load_multiple('config_test', FALSE);
+  uasort($entities, 'Drupal\config\ConfigurableBase::sort');
+
+  $rows = array();
+  foreach ($entities as $config_test) {
+    $uri = $config_test->uri();
+    $row = array();
+    $row['name']['data'] = array(
+      '#type' => 'link',
+      '#title' => $config_test->label(),
+      '#href' => $uri['path'],
+      '#options' => $uri['options'],
+    );
+    $row['delete']['data'] = array(
+      '#type' => 'link',
+      '#title' => t('Delete'),
+      '#href' => $uri['path'] . '/delete',
+      '#options' => $uri['options'],
+    );
+    $rows[] = $row;
+  }
+  $build = array(
+    '#theme' => 'table',
+    '#header' => array('Name', 'Operations'),
+    '#rows' => $rows,
+    '#empty' => format_string('No test configuration defined. <a href="@add-url">Add some</a>', array(
+      '@add-url' => url('admin/structure/config_test/add'),
+    )),
+  );
+  return $build;
+}
+
+/**
+ * Form constructor to add or edit a ConfigTest object.
+ *
+ * @param Drupal\config_test\ConfigTest $config_test
+ *   (optional) An existing ConfigTest object to edit. If omitted, the form
+ *   creates a new ConfigTest.
+ */
+function config_test_form($form, &$form_state, ConfigTest $config_test = NULL) {
+  // Standard procedure for handling the entity argument in entity forms, taking
+  // potential form caching and rebuilds properly into account.
+  // @see http://drupal.org/node/1499596
+  if (!isset($form_state['config_test'])) {
+    if (!isset($config_test)) {
+      $config_test = entity_create('config_test', array());
+    }
+    $form_state['config_test'] = $config_test;
+  }
+  else {
+    $config_test = $form_state['config_test'];
+  }
+
+  $form['label'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Label',
+    '#default_value' => $config_test->label(),
+    '#required' => TRUE,
+  );
+  $form['id'] = array(
+    '#type' => 'machine_name',
+    '#default_value' => $config_test->id(),
+    '#required' => TRUE,
+    '#machine_name' => array(
+      'exists' => 'config_test_load',
+      // @todo Update form_process_machine_name() to use 'label' by default.
+      'source' => array('label'),
+    ),
+  );
+  $form['style'] = array(
+    '#type' => 'select',
+    '#title' => 'Image style',
+    '#options' => array(),
+    '#default_value' => $config_test->get('style'),
+    '#access' => FALSE,
+  );
+  if (module_exists('image')) {
+    $form['style']['#access'] = TRUE;
+    $form['style']['#options'] = image_style_options();
+  }
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Save');
+
+  return $form;
+}
+
+/**
+ * Form submission handler for config_test_form().
+ */
+function config_test_form_submit($form, &$form_state) {
+  form_state_values_clean($form_state);
+
+  $config_test = $form_state['config_test'];
+  entity_form_submit_build_entity('config_test', $config_test, $form, $form_state);
+
+  $status = $config_test->save();
+
+  if ($status == SAVED_UPDATED) {
+    drupal_set_message(format_string('%label configuration has been updated.', array('%label' => $config_test->label())));
+  }
+  else {
+    drupal_set_message(format_string('%label configuration has been created.', array('%label' => $config_test->label())));
+  }
+
+  $form_state['redirect'] = 'admin/structure/config_test';
+}
+
+/**
+ * Form constructor to delete a ConfigTest object.
+ *
+ * @param Drupal\config_test\ConfigTest $config_test
+ *   The ConfigTest object to delete.
+ */
+function config_test_delete_form($form, &$form_state, ConfigTest $config_test) {
+  $form_state['config_test'] = $config_test;
+
+  $form['id'] = array('#type' => 'value', '#value' => $config_test->id());
+  return confirm_form($form,
+    format_string('Are you sure you want to delete %label', array('%label' => $config_test->label())),
+    'admin/structure/config_test',
+    NULL,
+    'Delete'
+  );
+}
+
+/**
+ * Form submission handler for config_test_delete_form().
+ */
+function config_test_delete_form_submit($form, &$form_state) {
+  $form_state['config_test']->delete();
+  $form_state['redirect'] = 'admin/structure/config_test';
+}
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php
new file mode 100644
index 0000000..104c11d
--- /dev/null
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config_test\ConfigTest.
+ */
+
+namespace Drupal\config_test;
+
+use Drupal\config\ConfigurableBase;
+
+/**
+ * Defines the ConfigTest configurable entity.
+ */
+class ConfigTest extends ConfigurableBase {
+
+  public $id;
+  public $uuid;
+  public $label;
+
+  /**
+   * The image style to use.
+   *
+   * @var string
+   */
+  public $style;
+
+  /**
+   * Implements Drupal\Core\Configurable\ConfigurableInterface::getConfigPrefix().
+   */
+  public static function getConfigPrefix() {
+    return 'config_test.dynamic';
+  }
+}
diff --git a/core/modules/entity/lib/Drupal/entity/DatabaseStorageController.php b/core/modules/entity/lib/Drupal/entity/DatabaseStorageController.php
index 8c4f3c8..c01ee77 100644
--- a/core/modules/entity/lib/Drupal/entity/DatabaseStorageController.php
+++ b/core/modules/entity/lib/Drupal/entity/DatabaseStorageController.php
@@ -183,7 +183,7 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
       if (!empty($this->entityInfo['entity class'])) {
         // We provide the necessary arguments for PDO to create objects of the
         // specified entity class.
-        // @see Drupal\entity\EntityInterface::__construct()
+        // @see Drupal\entity\StorableInterface::__construct()
         $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['entity class'], array(array(), $this->entityType));
       }
       $queried_entities = $query_result->fetchAllAssoc($this->idKey);
@@ -438,7 +438,7 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
   /**
    * Implements Drupal\entity\EntityStorageControllerInterface::save().
    */
-  public function save(EntityInterface $entity) {
+  public function save(StorableInterface $entity) {
     $transaction = db_transaction();
     try {
       // Load the stored entity, if any.
@@ -483,7 +483,7 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
    *
    * Used before the entity is saved and before invoking the presave hook.
    */
-  protected function preSave(EntityInterface $entity) { }
+  protected function preSave(StorableInterface $entity) { }
 
   /**
    * Acts on a saved entity before the insert or update hook is invoked.
@@ -495,7 +495,7 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
    *   (bool) TRUE if the entity has been updated, or FALSE if it has been
    *   inserted.
    */
-  protected function postSave(EntityInterface $entity, $update) { }
+  protected function postSave(StorableInterface $entity, $update) { }
 
   /**
    * Acts on entities before they are deleted.
@@ -519,7 +519,7 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
    * @param $entity
    *   The entity object.
    */
-  protected function invokeHook($hook, EntityInterface $entity) {
+  protected function invokeHook($hook, StorableInterface $entity) {
     if (!empty($this->entityInfo['fieldable']) && function_exists($function = 'field_attach_' . $hook)) {
       $function($this->entityType, $entity);
     }
diff --git a/core/modules/entity/lib/Drupal/entity/Entity.php b/core/modules/entity/lib/Drupal/entity/Entity.php
index 7e73024..6660fc7 100644
--- a/core/modules/entity/lib/Drupal/entity/Entity.php
+++ b/core/modules/entity/lib/Drupal/entity/Entity.php
@@ -17,265 +17,6 @@ use Drupal\Component\Uuid\Uuid;
  * This class can be used as-is by simple entity types. Entity types requiring
  * special handling can extend the class.
  */
-class Entity implements EntityInterface {
+class Entity extends StorableBase implements EntityInterface {
 
-  /**
-   * The language code of the entity's default language.
-   *
-   * @var string
-   */
-  public $langcode = LANGUAGE_NOT_SPECIFIED;
-
-  /**
-   * The entity type.
-   *
-   * @var string
-   */
-  protected $entityType;
-
-  /**
-   * Boolean indicating whether the entity should be forced to be new.
-   *
-   * @var bool
-   */
-  protected $enforceIsNew;
-
-  /**
-   * Indicates whether this is the current revision.
-   *
-   * @var bool
-   */
-  public $isCurrentRevision = TRUE;
-
-  /**
-   * Constructs a new entity object.
-   */
-  public function __construct(array $values = array(), $entity_type) {
-    $this->entityType = $entity_type;
-    // Set initial values.
-    foreach ($values as $key => $value) {
-      $this->$key = $value;
-    }
-  }
-
-  /**
-   * Implements EntityInterface::id().
-   */
-  public function id() {
-    return isset($this->id) ? $this->id : NULL;
-  }
-
-  /**
-   * Implements EntityInterface::isNew().
-   */
-  public function isNew() {
-    return !empty($this->enforceIsNew) || !$this->id();
-  }
-
-  /**
-   * Implements EntityInterface::enforceIsNew().
-   */
-  public function enforceIsNew($value = TRUE) {
-    $this->enforceIsNew = $value;
-  }
-
-  /**
-   * Implements EntityInterface::entityType().
-   */
-  public function entityType() {
-    return $this->entityType;
-  }
-
-  /**
-   * Implements EntityInterface::bundle().
-   */
-  public function bundle() {
-    return $this->entityType;
-  }
-
-  /**
-   * Implements EntityInterface::label().
-   */
-  public function label($langcode = NULL) {
-    $label = FALSE;
-    $entity_info = $this->entityInfo();
-    if (isset($entity_info['label callback']) && function_exists($entity_info['label callback'])) {
-      $label = $entity_info['label callback']($this->entityType, $this, $langcode);
-    }
-    elseif (!empty($entity_info['entity keys']['label']) && isset($this->{$entity_info['entity keys']['label']})) {
-      $label = $this->{$entity_info['entity keys']['label']};
-    }
-    return $label;
-  }
-
-  /**
-   * Implements EntityInterface::uri().
-   *
-   * @see entity_uri()
-   */
-  public function uri() {
-    $bundle = $this->bundle();
-    // A bundle-specific callback takes precedence over the generic one for the
-    // entity type.
-    $entity_info = $this->entityInfo();
-    if (isset($entity_info['bundles'][$bundle]['uri callback'])) {
-      $uri_callback = $entity_info['bundles'][$bundle]['uri callback'];
-    }
-    elseif (isset($entity_info['uri callback'])) {
-      $uri_callback = $entity_info['uri callback'];
-    }
-    else {
-      return NULL;
-    }
-
-    // Invoke the callback to get the URI. If there is no callback, return NULL.
-    if (isset($uri_callback) && function_exists($uri_callback)) {
-      $uri = $uri_callback($this);
-      // Pass the entity data to url() so that alter functions do not need to
-      // look up this entity again.
-      $uri['options']['entity_type'] = $this->entityType;
-      $uri['options']['entity'] = $this;
-      return $uri;
-    }
-  }
-
-  /**
-   * Implements EntityInterface::language().
-   */
-  public function language() {
-    // @todo: Check for language.module instead, once Field API language
-    // handling depends upon it too.
-    return module_exists('locale') ? language_load($this->langcode) : FALSE;
-  }
-
-  /**
-   * Implements EntityInterface::translations().
-   */
-  public function translations() {
-    $languages = array();
-    $entity_info = $this->entityInfo();
-    if ($entity_info['fieldable'] && ($default_language = $this->language())) {
-      // 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)) {
-          foreach ($this->$field_name as $langcode => $value)  {
-            $languages[$langcode] = TRUE;
-          }
-        }
-      }
-      // Remove the default language from the translations.
-      unset($languages[$default_language->langcode]);
-      $languages = array_intersect_key(language_list(), $languages);
-    }
-    return $languages;
-  }
-
-  /**
-   * Implements EntityInterface::get().
-   */
-  public function get($property_name, $langcode = NULL) {
-    // Handle fields.
-    $entity_info = $this->entityInfo();
-    if ($entity_info['fieldable'] && field_info_instance($this->entityType, $property_name, $this->bundle())) {
-      $field = field_info_field($property_name);
-      $langcode = $this->getFieldLangcode($field, $langcode);
-      return isset($this->{$property_name}[$langcode]) ? $this->{$property_name}[$langcode] : NULL;
-    }
-    else {
-      // Handle properties being not fields.
-      // @todo: Add support for translatable properties being not fields.
-      return isset($this->{$property_name}) ? $this->{$property_name} : NULL;
-    }
-  }
-
-  /**
-   * Implements EntityInterface::set().
-   */
-  public function set($property_name, $value, $langcode = NULL) {
-    // Handle fields.
-    $entity_info = $this->entityInfo();
-    if ($entity_info['fieldable'] && field_info_instance($this->entityType, $property_name, $this->bundle())) {
-      $field = field_info_field($property_name);
-      $langcode = $this->getFieldLangcode($field, $langcode);
-      $this->{$property_name}[$langcode] = $value;
-    }
-    else {
-      // Handle properties being not fields.
-      // @todo: Add support for translatable properties being not fields.
-      $this->{$property_name} = $value;
-    }
-  }
-
-  /**
-   * Determines the language code to use for accessing a field value in a certain language.
-   */
-  protected function getFieldLangcode($field, $langcode = NULL) {
-    // Only apply the given langcode if the entity is language-specific.
-    // Otherwise translatable fields are handled as non-translatable fields.
-    if (field_is_translatable($this->entityType, $field) && ($default_language = $this->language()) && !language_is_locked($this->langcode)) {
-      // For translatable fields the values in default language are stored using
-      // the language code of the default language.
-      return isset($langcode) ? $langcode : $default_language->langcode;
-    }
-    else {
-      // If there is a langcode defined for this field, just return it. Otherwise
-      // return LANGUAGE_NOT_SPECIFIED.
-      return (isset($this->langcode) ? $this->langcode : LANGUAGE_NOT_SPECIFIED);
-    }
-  }
-
-  /**
-   * Implements EntityInterface::save().
-   */
-  public function save() {
-    return entity_get_controller($this->entityType)->save($this);
-  }
-
-  /**
-   * Implements EntityInterface::delete().
-   */
-  public function delete() {
-    if (!$this->isNew()) {
-      entity_get_controller($this->entityType)->delete(array($this->id()));
-    }
-  }
-
-  /**
-   * Implements EntityInterface::createDuplicate().
-   */
-  public function createDuplicate() {
-    $duplicate = clone $this;
-    $entity_info = $this->entityInfo();
-    $this->{$entity_info['entity keys']['id']} = NULL;
-
-    // Check if the entity type supports UUIDs and generate a new one if so.
-    if (!empty($entity_info['entity keys']['uuid'])) {
-      $uuid = new Uuid();
-      $duplicate->{$entity_info['entity keys']['uuid']} = $uuid->generate();
-    }
-    return $duplicate;
-  }
-
-  /**
-   * Implements EntityInterface::entityInfo().
-   */
-  public function entityInfo() {
-    return entity_get_info($this->entityType);
-  }
-
-  /**
-   * Implements Drupal\entity\EntityInterface::getRevisionId().
-   */
-  public function getRevisionId() {
-    return NULL;
-  }
-
-  /**
-   * Implements Drupal\entity\EntityInterface::isCurrentRevision().
-   */
-  public function isCurrentRevision() {
-    return $this->isCurrentRevision;
-  }
 }
diff --git a/core/modules/entity/lib/Drupal/entity/EntityInterface.php b/core/modules/entity/lib/Drupal/entity/EntityInterface.php
index 7aedb48..1fc1d0a 100644
--- a/core/modules/entity/lib/Drupal/entity/EntityInterface.php
+++ b/core/modules/entity/lib/Drupal/entity/EntityInterface.php
@@ -10,198 +10,6 @@ namespace Drupal\entity;
 /**
  * Defines a common interface for all entity objects.
  */
-interface EntityInterface {
+interface EntityInterface extends StorableInterface {
 
-  /**
-   * Constructs a new entity object.
-   *
-   * @param $values
-   *   An array of values to set, keyed by property name. If the entity type
-   *   has bundles, the bundle key has to be specified.
-   * @param $entity_type
-   *   The type of the entity to create.
-   */
-  public function __construct(array $values, $entity_type);
-
-  /**
-   * Returns the entity identifier (the entity's machine name or numeric ID).
-   *
-   * @return
-   *   The identifier of the entity, or NULL if the entity does not yet have
-   *   an identifier.
-   */
-  public function id();
-
-  /**
-   * Returns whether the entity is new.
-   *
-   * Usually an entity is new if no ID exists for it yet. However, entities may
-   * be enforced to be new with existing IDs too.
-   *
-   * @return
-   *   TRUE if the entity is new, or FALSE if the entity has already been saved.
-   *
-   * @see Drupal\entity\EntityInterface::enforceIsNew()
-   */
-  public function isNew();
-
-  /**
-   * Enforces an entity to be new.
-   *
-   * Allows migrations to create entities with pre-defined IDs by forcing the
-   * entity to be new before saving.
-   *
-   * @param bool $value
-   *   (optional) Whether the entity should be forced to be new. Defaults to
-   *   TRUE.
-   *
-   * @see Drupal\entity\EntityInterface::isNew()
-   */
-  public function enforceIsNew($value = TRUE);
-
-  /**
-   * Returns the type of the entity.
-   *
-   * @return
-   *   The type of the entity.
-   */
-  public function entityType();
-
-  /**
-   * Returns the bundle of the entity.
-   *
-   * @return
-   *   The bundle of the entity. Defaults to the entity type if the entity type
-   *   does not make use of different bundles.
-   */
-  public function bundle();
-
-  /**
-   * Returns the label of the entity.
-   *
-   * @param $langcode
-   *   (optional) The language code of the language that should be used for
-   *   getting the label. If set to NULL, the entity's default language is
-   *   used.
-   *
-   * @return
-   *   The label of the entity, or NULL if there is no label defined.
-   */
-  public function label($langcode = NULL);
-
-  /**
-   * Returns the URI elements of the entity.
-   *
-   * @return
-   *   An array containing the 'path' and 'options' keys used to build the URI
-   *   of the entity, and matching the signature of url(). NULL if the entity
-   *   has no URI of its own.
-   */
-  public function uri();
-
-  /**
-   * Returns the default language of a language-specific entity.
-   *
-   * @return
-   *   The language object of the entity's default language, or FALSE if the
-   *   entity is not language-specific.
-   *
-   * @see Drupal\entity\EntityInterface::translations()
-   */
-  public function language();
-
-  /**
-   * Returns the languages the entity is translated to.
-   *
-   * @return
-   *   An array of language objects, keyed by language codes.
-   *
-   * @see Drupal\entity\EntityInterface::language()
-   */
-  public function translations();
-
-  /**
-   * Returns the value of an entity property.
-   *
-   * @param $property_name
-   *   The name of the property to return; e.g., 'title'.
-   * @param $langcode
-   *   (optional) If the property is translatable, the language code of the
-   *   language that should be used for getting the property. If set to NULL,
-   *   the entity's default language is being used.
-   *
-   * @return
-   *   The property value, or NULL if it is not defined.
-   *
-   * @see Drupal\entity\EntityInterface::language()
-   */
-  public function get($property_name, $langcode = NULL);
-
-  /**
-   * Sets the value of an entity property.
-   *
-   * @param $property_name
-   *   The name of the property to set; e.g., 'title'.
-   * @param $value
-   *   The value to set, or NULL to unset the property.
-   * @param $langcode
-   *   (optional) If the property is translatable, the language code of the
-   *   language that should be used for getting the property. If set to
-   *   NULL, the entity's default language is being used.
-   *
-   * @see Drupal\entity\EntityInterface::language()
-   */
-  public function set($property_name, $value, $langcode = NULL);
-
-  /**
-   * Saves an entity permanently.
-   *
-   * @return
-   *   Either SAVED_NEW or SAVED_UPDATED, depending on the operation performed.
-   *
-   * @throws Drupal\entity\EntityStorageException
-   *   In case of failures an exception is thrown.
-   */
-  public function save();
-
-  /**
-   * Deletes an entity permanently.
-   *
-   * @throws Drupal\entity\EntityStorageException
-   *   In case of failures an exception is thrown.
-   */
-  public function delete();
-
-  /**
-   * Creates a duplicate of the entity.
-   *
-   * @return Drupal\entity\EntityInterface
-   *   A clone of the current entity with all identifiers unset, so saving
-   *   it inserts a new entity into the storage system.
-   */
-  public function createDuplicate();
-
-  /**
-   * Returns the info of the type of the entity.
-   *
-   * @see entity_get_info()
-   */
-  public function entityInfo();
-
-  /**
-   * Returns the revision identifier of the entity.
-   *
-   * @return
-   *   The revision identifier of the entity, or NULL if the entity does not
-   *   have a revision identifier.
-   */
-  public function getRevisionId();
-
-  /**
-   * Checks if this entity is the current revision.
-   *
-   * @return bool
-   *   TRUE if the entity is the current revision, FALSE otherwise.
-   */
-  public function isCurrentRevision();
 }
diff --git a/core/modules/entity/lib/Drupal/entity/EntityStorageControllerInterface.php b/core/modules/entity/lib/Drupal/entity/EntityStorageControllerInterface.php
index 7f27e96..88b2ca5 100644
--- a/core/modules/entity/lib/Drupal/entity/EntityStorageControllerInterface.php
+++ b/core/modules/entity/lib/Drupal/entity/EntityStorageControllerInterface.php
@@ -57,7 +57,7 @@ interface EntityStorageControllerInterface {
    *   An array of values to set, keyed by property name. If the entity type has
    *   bundles the bundle key has to be specified.
    *
-   * @return Drupal\entity\EntityInterface
+   * @return Drupal\entity\StorableInterface
    *   A new entity object.
    */
   public function create(array $values);
@@ -76,7 +76,7 @@ interface EntityStorageControllerInterface {
   /**
    * Saves the entity permanently.
    *
-   * @param Drupal\entity\EntityInterface $entity
+   * @param Drupal\entity\StorableInterface $entity
    *   The entity to save.
    *
    * @return
@@ -86,6 +86,6 @@ interface EntityStorageControllerInterface {
    * @throws Drupal\entity\EntityStorageException
    *   In case of failures, an exception is thrown.
    */
-  public function save(EntityInterface $entity);
+  public function save(StorableInterface $entity);
 
 }
diff --git a/core/modules/entity/lib/Drupal/entity/Entity.php b/core/modules/entity/lib/Drupal/entity/StorableBase.php
similarity index 85%
copy from core/modules/entity/lib/Drupal/entity/Entity.php
copy to core/modules/entity/lib/Drupal/entity/StorableBase.php
index 7e73024..25cc1d7 100644
--- a/core/modules/entity/lib/Drupal/entity/Entity.php
+++ b/core/modules/entity/lib/Drupal/entity/StorableBase.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\entity\Entity.
+ * Definition of Drupal\entity\StorableBase.
  */
 
 namespace Drupal\entity;
@@ -12,12 +12,12 @@ use Drupal\Component\Uuid\Uuid;
 /**
  * Defines a base entity class.
  *
- * Default implementation of EntityInterface.
+ * Default implementation of StorableInterface.
  *
  * This class can be used as-is by simple entity types. Entity types requiring
  * special handling can extend the class.
  */
-class Entity implements EntityInterface {
+abstract class StorableBase implements StorableInterface {
 
   /**
    * The language code of the entity's default language.
@@ -45,7 +45,7 @@ class Entity implements EntityInterface {
    *
    * @var bool
    */
-  public $isCurrentRevision = TRUE;
+  protected $isCurrentRevision = TRUE;
 
   /**
    * Constructs a new entity object.
@@ -59,42 +59,42 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::id().
+   * Implements StorableInterface::id().
    */
   public function id() {
     return isset($this->id) ? $this->id : NULL;
   }
 
   /**
-   * Implements EntityInterface::isNew().
+   * Implements StorableInterface::isNew().
    */
   public function isNew() {
     return !empty($this->enforceIsNew) || !$this->id();
   }
 
   /**
-   * Implements EntityInterface::enforceIsNew().
+   * Implements StorableInterface::enforceIsNew().
    */
   public function enforceIsNew($value = TRUE) {
     $this->enforceIsNew = $value;
   }
 
   /**
-   * Implements EntityInterface::entityType().
+   * Implements StorableInterface::entityType().
    */
   public function entityType() {
     return $this->entityType;
   }
 
   /**
-   * Implements EntityInterface::bundle().
+   * Implements StorableInterface::bundle().
    */
   public function bundle() {
     return $this->entityType;
   }
 
   /**
-   * Implements EntityInterface::label().
+   * Implements StorableInterface::label().
    */
   public function label($langcode = NULL) {
     $label = FALSE;
@@ -109,7 +109,7 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::uri().
+   * Implements StorableInterface::uri().
    *
    * @see entity_uri()
    */
@@ -140,7 +140,7 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::language().
+   * Implements StorableInterface::language().
    */
   public function language() {
     // @todo: Check for language.module instead, once Field API language
@@ -149,7 +149,7 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::translations().
+   * Implements StorableInterface::translations().
    */
   public function translations() {
     $languages = array();
@@ -173,7 +173,7 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::get().
+   * Implements StorableInterface::get().
    */
   public function get($property_name, $langcode = NULL) {
     // Handle fields.
@@ -191,7 +191,7 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::set().
+   * Implements StorableInterface::set().
    */
   public function set($property_name, $value, $langcode = NULL) {
     // Handle fields.
@@ -227,14 +227,14 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::save().
+   * Implements StorableInterface::save().
    */
   public function save() {
     return entity_get_controller($this->entityType)->save($this);
   }
 
   /**
-   * Implements EntityInterface::delete().
+   * Implements StorableInterface::delete().
    */
   public function delete() {
     if (!$this->isNew()) {
@@ -243,7 +243,7 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::createDuplicate().
+   * Implements StorableInterface::createDuplicate().
    */
   public function createDuplicate() {
     $duplicate = clone $this;
@@ -259,23 +259,27 @@ class Entity implements EntityInterface {
   }
 
   /**
-   * Implements EntityInterface::entityInfo().
+   * Implements StorableInterface::entityInfo().
    */
   public function entityInfo() {
     return entity_get_info($this->entityType);
   }
 
   /**
-   * Implements Drupal\entity\EntityInterface::getRevisionId().
+   * Implements Drupal\entity\StorableInterface::getRevisionId().
    */
   public function getRevisionId() {
     return NULL;
   }
 
   /**
-   * Implements Drupal\entity\EntityInterface::isCurrentRevision().
+   * Implements Drupal\entity\StorableInterface::isCurrentRevision().
    */
-  public function isCurrentRevision() {
-    return $this->isCurrentRevision;
+  public function isCurrentRevision($new_value = NULL) {
+    $return = $this->isCurrentRevision;
+    if (isset($new_value)) {
+      $this->isCurrentRevision = (bool) $new_value;
+    }
+    return $return;
   }
 }
diff --git a/core/modules/entity/lib/Drupal/entity/EntityInterface.php b/core/modules/entity/lib/Drupal/entity/StorableInterface.php
similarity index 88%
copy from core/modules/entity/lib/Drupal/entity/EntityInterface.php
copy to core/modules/entity/lib/Drupal/entity/StorableInterface.php
index 7aedb48..d101998 100644
--- a/core/modules/entity/lib/Drupal/entity/EntityInterface.php
+++ b/core/modules/entity/lib/Drupal/entity/StorableInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\entity\EntityInterface.
+ * Definition of Drupal\entity\StorableInterface.
  */
 
 namespace Drupal\entity;
@@ -10,7 +10,7 @@ namespace Drupal\entity;
 /**
  * Defines a common interface for all entity objects.
  */
-interface EntityInterface {
+interface StorableInterface {
 
   /**
    * Constructs a new entity object.
@@ -41,7 +41,7 @@ interface EntityInterface {
    * @return
    *   TRUE if the entity is new, or FALSE if the entity has already been saved.
    *
-   * @see Drupal\entity\EntityInterface::enforceIsNew()
+   * @see Drupal\entity\StorableInterface::enforceIsNew()
    */
   public function isNew();
 
@@ -55,7 +55,7 @@ interface EntityInterface {
    *   (optional) Whether the entity should be forced to be new. Defaults to
    *   TRUE.
    *
-   * @see Drupal\entity\EntityInterface::isNew()
+   * @see Drupal\entity\StorableInterface::isNew()
    */
   public function enforceIsNew($value = TRUE);
 
@@ -106,7 +106,7 @@ interface EntityInterface {
    *   The language object of the entity's default language, or FALSE if the
    *   entity is not language-specific.
    *
-   * @see Drupal\entity\EntityInterface::translations()
+   * @see Drupal\entity\StorableInterface::translations()
    */
   public function language();
 
@@ -116,7 +116,7 @@ interface EntityInterface {
    * @return
    *   An array of language objects, keyed by language codes.
    *
-   * @see Drupal\entity\EntityInterface::language()
+   * @see Drupal\entity\StorableInterface::language()
    */
   public function translations();
 
@@ -133,7 +133,7 @@ interface EntityInterface {
    * @return
    *   The property value, or NULL if it is not defined.
    *
-   * @see Drupal\entity\EntityInterface::language()
+   * @see Drupal\entity\StorableInterface::language()
    */
   public function get($property_name, $langcode = NULL);
 
@@ -149,7 +149,7 @@ interface EntityInterface {
    *   language that should be used for getting the property. If set to
    *   NULL, the entity's default language is being used.
    *
-   * @see Drupal\entity\EntityInterface::language()
+   * @see Drupal\entity\StorableInterface::language()
    */
   public function set($property_name, $value, $langcode = NULL);
 
@@ -175,7 +175,7 @@ interface EntityInterface {
   /**
    * Creates a duplicate of the entity.
    *
-   * @return Drupal\entity\EntityInterface
+   * @return Drupal\entity\StorableInterface
    *   A clone of the current entity with all identifiers unset, so saving
    *   it inserts a new entity into the storage system.
    */
@@ -200,8 +200,13 @@ interface EntityInterface {
   /**
    * Checks if this entity is the current revision.
    *
+   * @param bool $new_value
+   *   (optional) A Boolean to (re)set the isCurrentRevision flag.
+   *
    * @return bool
-   *   TRUE if the entity is the current revision, FALSE otherwise.
+   *   TRUE if the entity is the current revision, FALSE otherwise. If
+   *   $new_value was passed, the previous value is returned.
    */
-  public function isCurrentRevision();
+  public function isCurrentRevision($new_value = NULL);
+
 }
diff --git a/core/modules/entity/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php b/core/modules/entity/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
index 147f030..84dfa65 100644
--- a/core/modules/entity/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
+++ b/core/modules/entity/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
@@ -9,7 +9,7 @@ namespace Drupal\entity_test;
 
 use PDO;
 
-use Drupal\entity\EntityInterface;
+use Drupal\entity\StorableInterface;
 use Drupal\entity\DatabaseStorageController;
 
 /**
@@ -90,7 +90,7 @@ class EntityTestStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::postSave().
    */
-  protected function postSave(EntityInterface $entity, $update) {
+  protected function postSave(StorableInterface $entity, $update) {
     $default_langcode = ($language = $entity->language()) ? $language->langcode : LANGUAGE_NOT_SPECIFIED;
     $langcodes = array_keys($entity->translations());
     $langcodes[] = $default_langcode;
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php
index 3bdab77..49129ca 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php
@@ -8,7 +8,7 @@
 namespace Drupal\field_test;
 
 use Drupal\entity\DatabaseStorageController;
-use Drupal\entity\EntityInterface;
+use Drupal\entity\StorableInterface;
 
 /**
  * Controller class for the test entity entity types.
@@ -18,7 +18,7 @@ class TestEntityController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::preSave().
    */
-  public function preSave(EntityInterface $entity) {
+  public function preSave(StorableInterface $entity) {
     // Prepare for a new revision.
     if (!$entity->isNew() && !empty($entity->revision)) {
       $entity->old_ftvid = $entity->ftvid;
@@ -29,7 +29,7 @@ class TestEntityController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::postSave().
    */
-  public function postSave(EntityInterface $entity, $update) {
+  public function postSave(StorableInterface $entity, $update) {
     // Only the test_entity entity type has revisions.
     if ($entity->entityType() == 'test_entity') {
       $update_entity = TRUE;
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 0fa7b33..0ee63e0 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -8,7 +8,7 @@
 namespace Drupal\node;
 
 use Drupal\entity\DatabaseStorageController;
-use Drupal\entity\EntityInterface;
+use Drupal\entity\StorableInterface;
 use Drupal\entity\EntityStorageException;
 use Exception;
 
@@ -82,7 +82,7 @@ class NodeStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::save().
    */
-  public function save(EntityInterface $entity) {
+  public function save(StorableInterface $entity) {
     $transaction = db_transaction();
     try {
       // Load the stored entity, if any.
@@ -129,10 +129,10 @@ class NodeStorageController extends DatabaseStorageController {
   /**
    * Saves a node revision.
    *
-   * @param Drupal\entity\EntityInterface $node
+   * @param Drupal\entity\StorableInterface $node
    *   The node entity.
    */
-  protected function saveRevision(EntityInterface $entity) {
+  protected function saveRevision(StorableInterface $entity) {
     $record = clone $entity;
     $record->uid = $entity->revision_uid;
     $record->timestamp = $entity->revision_timestamp;
@@ -151,7 +151,7 @@ class NodeStorageController extends DatabaseStorageController {
     $entity->{$this->revisionKey} = $record->{$this->revisionKey};
 
     // Mark this revision as the current one.
-    $entity->isCurrentRevision = TRUE;
+    $entity->isCurrentRevision(TRUE);
   }
 
   /**
@@ -197,7 +197,7 @@ class NodeStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::invokeHook().
    */
-  protected function invokeHook($hook, EntityInterface $node) {
+  protected function invokeHook($hook, StorableInterface $node) {
     if ($hook == 'insert' || $hook == 'update') {
       node_invoke($node, $hook);
     }
@@ -246,7 +246,7 @@ class NodeStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::preSave().
    */
-  protected function preSave(EntityInterface $node) {
+  protected function preSave(StorableInterface $node) {
     // Before saving the node, set changed and revision times.
     $node->changed = REQUEST_TIME;
 
@@ -262,7 +262,7 @@ class NodeStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::postSave().
    */
-  function postSave(EntityInterface $node, $update) {
+  function postSave(StorableInterface $node, $update) {
     node_access_acquire_grants($node, $update);
   }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php
index 8468185..8177165 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php
@@ -45,6 +45,8 @@ class ModuleApiTest extends WebTestBase {
     // Try to install a new module.
     module_enable(array('contact'));
     $module_list[] = 'contact';
+    // Contact module requires config.
+    $module_list[] = 'config';
     sort($module_list);
     $this->assertModuleList($module_list, t('After adding a module'));
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php
index 442e203..23b6895 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorageController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\taxonomy;
 
-use Drupal\entity\EntityInterface;
+use Drupal\entity\StorableInterface;
 use Drupal\entity\DatabaseStorageController;
 
 /**
@@ -112,7 +112,7 @@ class TermStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::postSave().
    */
-  protected function postSave(EntityInterface $entity, $update) {
+  protected function postSave(StorableInterface $entity, $update) {
     if (isset($entity->parent)) {
       db_delete('taxonomy_term_hierarchy')
         ->condition('tid', $entity->tid)
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php
index a5ad5c3..59d4b5b 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyStorageController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\taxonomy;
 
-use Drupal\entity\EntityInterface;
+use Drupal\entity\StorableInterface;
 use Drupal\entity\DatabaseStorageController;
 
 /**
@@ -29,7 +29,7 @@ class VocabularyStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::postSave().
    */
-  protected function postSave(EntityInterface $entity, $update) {
+  protected function postSave(StorableInterface $entity, $update) {
     if (!$update) {
       field_attach_create_bundle('taxonomy_term', $entity->machine_name);
     }
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index bde430c..67dcd38 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\user;
 
-use Drupal\entity\EntityInterface;
+use Drupal\entity\StorableInterface;
 use Drupal\entity\EntityMalformedException;
 use Drupal\entity\DatabaseStorageController;
 
@@ -75,7 +75,7 @@ class UserStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::save().
    */
-  public function save(EntityInterface $entity) {
+  public function save(StorableInterface $entity) {
     if (empty($entity->uid)) {
       $entity->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
       $entity->enforceIsNew();
@@ -86,7 +86,7 @@ class UserStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::preSave().
    */
-  protected function preSave(EntityInterface $entity) {
+  protected function preSave(StorableInterface $entity) {
     // Update the user password if it has changed.
     if ($entity->isNew() || (!empty($entity->pass) && $entity->pass != $entity->original->pass)) {
       // Allow alternate password hashing schemes.
@@ -160,7 +160,7 @@ class UserStorageController extends DatabaseStorageController {
   /**
    * Overrides Drupal\entity\DatabaseStorageController::postSave().
    */
-  protected function postSave(EntityInterface $entity, $update) {
+  protected function postSave(StorableInterface $entity, $update) {
 
     if ($update) {
       // If the password has been changed, delete all open sessions for the
