diff --git a/core/lib/Drupal/Component/Plugin/PluginDefinition.php b/core/lib/Drupal/Component/Plugin/PluginDefinition.php
new file mode 100644
index 0000000..69c123d
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/PluginDefinition.php
@@ -0,0 +1,229 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Plugin\PluginDefinition.
+ */
+
+namespace Drupal\Component\Plugin;
+
+/**
+ * Provides a plugin definition.
+ *
+ * @ingroup Plugin
+ */
+class PluginDefinition implements PluginDefinitionInterface {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The class.
+   *
+   * @param string
+   *   A class that implements
+   *   \Drupal\Component\Plugin\PluginInspectionInterface.
+   */
+  protected $class;
+
+  /**
+   * The human-readable plugin label.
+   *
+   * @var string|null
+   */
+  protected $label;
+
+  /**
+   * The deriver class.
+   *
+   * @var string|null
+   *   A class that implements
+   *   \Drupal\Component\Plugin\Derivative\DeriverInterface or null.
+   */
+  protected $deriverClass;
+
+  /**
+   * The plugin provider.
+   *
+   * @var string|null
+   */
+  protected $provider;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setId($id) {
+    $this->id = $id;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getId() {
+    return $this->id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setClass($class) {
+    if (!is_subclass_of($class, 'Drupal\Component\Plugin\PluginInspectionInterface')) {
+      throw new \InvalidArgumentException('Plugin classes must implement \Drupal\Component\Plugin\PluginInspectionInterface.');
+    }
+
+    $this->class = $class;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    return $this->class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setProvider($provider) {
+    $this->provider = $provider;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProvider() {
+    return $this->provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLabel($label) {
+    $this->label = $label;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabel() {
+    return $this->label;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDeriverClass($class) {
+    if (!is_subclass_of($class, 'Drupal\Component\Plugin\Derivative\DeriverInterface')) {
+      throw new \InvalidArgumentException('Plugin deriver classes must implement \Drupal\Component\Plugin\Derivative\DeriverInterface.');
+    }
+
+    $this->deriverClass = $class;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDeriverClass() {
+    return $this->deriverClass;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetExists($offset) {
+    switch ($offset) {
+      case 'id':
+        return !is_null($this->getId());
+      case 'class':
+        return !is_null($this->getClass());
+      case 'provider':
+        return !is_null($this->getProvider());
+      case 'label':
+        return !is_null($this->getLabel());
+      case 'deriver':
+        return !is_null($this->getDeriverClass());
+      default:
+        return FALSE;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetGet($offset) {
+    switch ($offset) {
+      case 'id':
+        return $this->getId();
+      case 'class':
+        return $this->getClass();
+      case 'provider':
+        return $this->getProvider();
+      case 'label':
+        return $this->getLabel();
+      case 'deriver':
+        return $this->getDeriverClass();
+      default:
+        return NULL;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetSet($offset, $value) {
+    switch ($offset) {
+      case 'id':
+        $this->setId($value);
+        break;
+      case 'class':
+        $this->setClass($value);
+        break;
+      case 'provider':
+        $this->setProvider($value);
+        break;
+      case 'label':
+        $this->setLabel($value);
+        break;
+      case 'deriver':
+        $this->setDeriverClass($value);
+        break;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function offsetUnset($offset) {
+    $allowed_offsets = ['label', 'deriver'];
+    if (!in_array($offset, $allowed_offsets)) {
+      throw new \InvalidArgumentException(sprintf('Key %s is required and cannot be unset.', $offset));
+    }
+
+    switch ($offset) {
+      case 'provider':
+        $this->setProvider(NULL);
+        break;
+      case 'label':
+        $this->setLabel(NULL);
+        break;
+      case 'deriver':
+        $this->setDeriverClass(NULL);
+        break;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Component/Plugin/PluginDefinitionInterface.php b/core/lib/Drupal/Component/Plugin/PluginDefinitionInterface.php
new file mode 100644
index 0000000..f1d1e60
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/PluginDefinitionInterface.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Plugin\PluginDefinitionInterface.
+ */
+
+namespace Drupal\Component\Plugin;
+
+/**
+ * Defines a plugin definition.
+ *
+ * For backwards compatibility with array-based plugin definitions, this
+ * interface implements \ArrayAccess. The required array keys and their
+ * corresponding setters and getters are:
+ * - id: static::setId() and static::getId()
+ * - class: static::setClass() and static::getClass()
+ * - label: static::setLabel() and static::getLabel()
+ * - deriver: static::setDeriverClass() and static::getDeriverClass()
+ *
+ * @ingroup Plugin
+ */
+interface PluginDefinitionInterface extends \ArrayAccess {
+
+  /**
+   * Sets the plugin ID.
+   *
+   * @param string $id
+   *
+   * @return $this
+   */
+  public function setId($id);
+
+  /**
+   * Gets the plugin ID.
+   *
+   * @return string
+   */
+  public function getId();
+
+  /**
+   * Sets the class.
+   *
+   * @param string $class
+   *   A class that implements
+   *   \Drupal\Component\Plugin\PluginInspectionInterface.
+   *
+   * @return $this
+   *
+   * @throws \InvalidArgumentException
+   */
+  public function setClass($class);
+
+  /**
+   * Gets the class.
+   *
+   * @return string
+   *   A class that implements
+   *   \Drupal\Component\Plugin\PluginInspectionInterface.
+   */
+  public function getClass();
+
+  /**
+   * Sets the plugin provider.
+   *
+   * The provider is an arbitrary string that identifies the
+   * package/component/module/extension that provides the plugin.
+   *
+   * @param string $provider
+   *
+   * @return $this
+   */
+  public function setProvider($provider);
+
+  /**
+   * Gets the plugin provider.
+   *
+   * The provider is an arbitrary string that identifies the
+   * package/component/module/extension that provides the plugin.
+   *
+   * @return string
+   */
+  public function getProvider();
+
+  /**
+   * Sets the human-readable plugin label.
+   *
+   * @param string $label
+   *
+   * @return $this
+   */
+  public function setLabel($label);
+
+  /**
+   * Gets the human-readable plugin label.
+   *
+   * @return string|null
+   */
+  public function getLabel();
+
+  /**
+   * Sets the deriver class.
+   *
+   * @param string $class
+   *   A class that implements
+   *   \Drupal\Component\Plugin\Derivative\DeriverInterface.
+   *
+   * @return $this
+   *
+   * @throws \InvalidArgumentException
+   */
+  public function setDeriverClass($class);
+
+  /**
+   * Gets the deriver class.
+   *
+   * @return string|null
+   *   A class that implements
+   *   \Drupal\Component\Plugin\Derivative\DeriverInterface or null.
+   */
+  public function getDeriverClass();
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index f9dc88c..cc75c7f 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Component\Plugin\PluginDefinition;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\Exception\EntityTypeIdLengthException;
@@ -17,7 +18,7 @@
  *
  * @ingroup entity_api
  */
-class EntityType implements EntityTypeInterface {
+class EntityType extends PluginDefinition implements EntityTypeInterface {
 
   use StringTranslationTrait;
 
@@ -50,27 +51,6 @@ class EntityType implements EntityTypeInterface {
   protected $entity_keys = array();
 
   /**
-   * The unique identifier of this entity type.
-   *
-   * @var string
-   */
-  protected $id;
-
-  /**
-   * The name of the provider of this entity type.
-   *
-   * @var string
-   */
-  protected $provider;
-
-  /**
-   * The name of the entity type class.
-   *
-   * @var string
-   */
-  protected $class;
-
-  /**
    * The name of the original entity type class.
    *
    * This is only set if the class name is changed.
@@ -172,13 +152,6 @@ class EntityType implements EntityTypeInterface {
   protected $translatable = FALSE;
 
   /**
-   * The human-readable name of the type.
-   *
-   * @var string
-   */
-  protected $label = '';
-
-  /**
    * A callable that can be used to provide the entity URI.
    *
    * @var callable|null
@@ -350,28 +323,25 @@ public function id() {
   /**
    * {@inheritdoc}
    */
-  public function getProvider() {
-    return $this->provider;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getClass() {
-    return $this->class;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getOriginalClass() {
     return $this->originalClass ?: $this->class;
   }
 
   /**
-   * {@inheritdoc}
+   * Sets the class.
+   *
+   * @param string $class
+   *   A class that implements \Drupal\Core\Entity\EntityInterface.
+   *
+   * @return $this
+   *
+   * @throws \InvalidArgumentException
    */
   public function setClass($class) {
+    if (!is_subclass_of($class, 'Drupal\Core\Entity\EntityInterface')) {
+      throw new \InvalidArgumentException('Entity classes must implement \Drupal\Core\Entity\EntityInterface.');
+    }
+
     if (!$this->originalClass && $this->class) {
       // If the original class is currently not set, set it to the current
       // class, assume that is the original class name.
@@ -682,13 +652,6 @@ public function getDataTable() {
   /**
    * {@inheritdoc}
    */
-  public function getLabel() {
-    return (string) $this->label;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getLowercaseLabel() {
     return Unicode::strtolower($this->getLabel());
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
index 6b68b01..8fc7297 100644
--- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Component\Plugin\PluginDefinitionInterface;
+
 /**
  * Provides an interface for an entity type and its metadata.
  *
@@ -15,7 +17,7 @@
  * implemented to alter existing data and fill-in defaults. Module-specific
  * properties should be documented in the hook implementations defining them.
  */
-interface EntityTypeInterface {
+interface EntityTypeInterface extends PluginDefinitionInterface {
 
   /**
    * The maximum length of ID, in characters.
@@ -59,22 +61,6 @@ public function set($property, $value);
   public function id();
 
   /**
-   * Returns the name of the provider of this entity type.
-   *
-   * @return string
-   *   The name of the provider of this entity type.
-   */
-  public function getProvider();
-
-  /**
-   * Returns the name of the entity type class.
-   *
-   * @return string
-   *   The name of the entity type class.
-   */
-  public function getClass();
-
-  /**
    * Returns the name of the original entity type class.
    *
    * In case the class name was changed with setClass(), this will return
@@ -171,16 +157,6 @@ public function isRenderCacheable();
   public function isPersistentlyCacheable();
 
   /**
-   * Sets the name of the entity type class.
-   *
-   * @param string $class
-   *   The name of the entity type class.
-   *
-   * @return $this
-   */
-  public function setClass($class);
-
-  /**
    * Determines if there is a handler for a given type.
    *
    * @param string $handler_type
@@ -619,14 +595,6 @@ public function getRevisionTable();
   public function getDataTable();
 
   /**
-   * Returns the human-readable name of the entity type.
-   *
-   * @return string
-   *   The human-readable name of the entity type.
-   */
-  public function getLabel();
-
-  /**
    * Returns the lowercase form of the human-readable entity type name.
    *
    * @return string
diff --git a/core/tests/Drupal/Tests/Component/Plugin/PluginDefinitionTest.php b/core/tests/Drupal/Tests/Component/Plugin/PluginDefinitionTest.php
new file mode 100644
index 0000000..b5f76f5
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Plugin/PluginDefinitionTest.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Component\Plugin\PluginDefinitionTest.
+ */
+
+namespace Drupal\Tests\Component\Plugin;
+
+use Drupal\Component\Plugin\PluginDefinition;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Component\Plugin\PluginDefinition
+ * @group Plugin
+ */
+class PluginDefinitionTest extends UnitTestCase {
+
+  /**
+   * The class under test.
+   *
+   * @var \Drupal\Component\Plugin\PluginDefinition
+   */
+  protected $sut;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->sut = new PluginDefinition();
+  }
+
+  /**
+   * @covers ::setId
+   * @covers ::getId
+   * @covers ::offsetExists
+   * @covers ::offsetSet
+   * @covers ::offsetGet
+   */
+  public function testGetId() {
+    $id = $this->randomMachineName();
+
+    $this->assertFalse(isset($this->sut['id']));
+    $this->assertSame($this->sut, $this->sut->setId($id));
+    $this->assertSame($id, $this->sut->getId());
+    $this->assertSame($id, $this->sut['id']);
+    $this->assertTrue(isset($this->sut['id']));
+
+    $id = $this->randomMachineName();
+    $this->sut['id'] = $id;
+    $this->assertSame($id, $this->sut->getId());
+    $this->assertSame($id, $this->sut['id']);
+  }
+
+  /**
+   * @covers ::setClass
+   * @covers ::getClass
+   * @covers ::offsetExists
+   * @covers ::offsetSet
+   * @covers ::offsetGet
+   */
+  public function testGetClass() {
+    $class = get_class($this->getMock('\Drupal\Component\Plugin\PluginInspectionInterface'));
+
+    $this->assertFalse(isset($this->sut['class']));
+    $this->assertSame($this->sut, $this->sut->setClass($class));
+    $this->assertSame($class, $this->sut->getClass());
+    $this->assertSame($class, $this->sut['class']);
+    $this->assertTrue(isset($this->sut['class']));
+
+    $class = get_class($this->getMock('\Drupal\Component\Plugin\PluginInspectionInterface'));
+    $this->sut['class'] = $class;
+    $this->assertSame($class, $this->sut->getClass());
+    $this->assertSame($class, $this->sut['class']);
+  }
+
+  /**
+   * @covers ::setDeriverClass
+   * @covers ::getDeriverClass
+   * @covers ::offsetExists
+   * @covers ::offsetSet
+   * @covers ::offsetGet
+   */
+  public function testGetDeriverClass() {
+    $class = get_class($this->getMock('\Drupal\Component\Plugin\Derivative\DeriverInterface'));
+
+    $this->assertFalse(isset($this->sut['deriver']));
+    $this->assertSame($this->sut, $this->sut->setDeriverClass($class));
+    $this->assertSame($class, $this->sut->getDeriverClass());
+    $this->assertSame($class, $this->sut['deriver']);
+    $this->assertTrue(isset($this->sut['deriver']));
+
+    $class = get_class($this->getMock('\Drupal\Component\Plugin\Derivative\DeriverInterface'));
+    $this->sut['deriver'] = $class;
+    $this->assertSame($class, $this->sut->getDeriverClass());
+    $this->assertSame($class, $this->sut['deriver']);
+  }
+
+  /**
+   * @covers ::setProvider
+   * @covers ::getProvider
+   * @covers ::offsetExists
+   * @covers ::offsetSet
+   * @covers ::offsetGet
+   */
+  public function testGetProvider() {
+    $provider = $this->randomMachineName();
+
+    $this->assertFalse(isset($this->sut['provider']));
+    $this->assertSame($this->sut, $this->sut->setProvider($provider));
+    $this->assertSame($provider, $this->sut->getProvider());
+    $this->assertSame($provider, $this->sut['provider']);
+    $this->assertTrue(isset($this->sut['provider']));
+
+    $provider = $this->randomMachineName();
+    $this->sut['provider'] = $provider;
+    $this->assertSame($provider, $this->sut->getProvider());
+    $this->assertSame($provider, $this->sut['provider']);
+  }
+
+  /**
+   * @covers ::setLabel
+   * @covers ::getLabel
+   * @covers ::offsetExists
+   * @covers ::offsetSet
+   * @covers ::offsetGet
+   */
+  public function testGetLabel() {
+    $label = $this->randomMachineName();
+
+    $this->assertFalse(isset($this->sut['label']));
+    $this->assertSame($this->sut, $this->sut->setLabel($label));
+    $this->assertSame($label, $this->sut->getLabel());
+    $this->assertSame($label, $this->sut['label']);
+    $this->assertTrue(isset($this->sut['label']));
+
+    $label = $this->randomMachineName();
+    $this->sut['label'] = $label;
+    $this->assertSame($label, $this->sut->getLabel());
+    $this->assertSame($label, $this->sut['label']);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
index c350b48..83c4ebc 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php
@@ -212,23 +212,30 @@ public function testgetOriginalClassUnchanged() {
   }
 
   /**
+   * @covers ::getClass
    * @covers ::setClass
    * @covers ::getOriginalClass
    */
   public function testgetOriginalClassChanged() {
-    $class = $this->randomMachineName();
-    $entity_type = $this->setUpEntityType(array('class' => $class));
-    $entity_type->setClass($this->randomMachineName());
-    $this->assertEquals($class, $entity_type->getOriginalClass());
+    $original_class = get_class($this->getMock('\Drupal\Core\Entity\EntityInterface'));
+    $new_class = get_class($this->getMock('\Drupal\Core\Entity\EntityInterface'));
+    $entity_type = $this->setUpEntityType(array('class' => $original_class));
+    $this->assertEquals($original_class, $entity_type->getClass());
+    $entity_type->setClass($new_class);
+    $this->assertEquals($original_class, $entity_type->getOriginalClass());
+    $this->assertEquals($new_class, $entity_type->getClass());
   }
 
   /**
    * @covers ::id
+   * @covers ::setId
+   * @covers ::getId
    */
   public function testId() {
     $id = $this->randomMachineName(32);
     $entity_type = $this->setUpEntityType(array('id' => $id));
     $this->assertEquals($id, $entity_type->id());
+    $this->assertEquals($id, $entity_type->getId());
   }
 
   /**
