diff --git a/plugin_type_example/plugin_type_example.info.yml b/plugin_type_example/plugin_type_example.info.yml
new file mode 100644
index 0000000..5777402
--- /dev/null
+++ b/plugin_type_example/plugin_type_example.info.yml
@@ -0,0 +1,7 @@
+name: Plugin type example
+type: module
+description: Provides an example of defining a plugin type.
+package: Example modules
+core: 8.x
+dependencies:
+  - examples
diff --git a/plugin_type_example/plugin_type_example.links.menu.yml b/plugin_type_example/plugin_type_example.links.menu.yml
new file mode 100644
index 0000000..c607e36
--- /dev/null
+++ b/plugin_type_example/plugin_type_example.links.menu.yml
@@ -0,0 +1,3 @@
+plugin_type_example.description:
+  title: Plugin Type Example
+  route_name: plugin_type_example.description
diff --git a/plugin_type_example/plugin_type_example.module b/plugin_type_example/plugin_type_example.module
new file mode 100644
index 0000000..e9c7ac5
--- /dev/null
+++ b/plugin_type_example/plugin_type_example.module
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Demonstrates how to define a new plugin type.
+ */
+
+/**
+ * @defgroup plugin_type_example Example: Plugin Types
+ * @ingroup examples
+ * @{
+ * Example of how to define a plugin type.
+ *
+ * Our example defines a Sandwich plugin type.  Sandwich's come in many flavors,
+ * each having their unique qualities, but also sharing things in common.  Our
+ * Sandwich plugins will be very simple, each providing a unique description
+ * or themselves.  Sandwich plugins also have an id and a property called
+ * foobar, which we'll use to demonstrate how you can alter a plugin using an
+ * alter hook.
+ *
+ * Start at the class \Drupal\plugin_type_example\SandwichPluginManager, which
+ * defines our plugin type manager.  Our plugin type manager is exposed to
+ * Drupal as a service in the plugin_type_example.services.yml file.
+ *
+ * We've also defined two implementations of our plugin type,
+ * \Drupal\plugin_type_example\Plugin\Sandwich\ExampleHamSandwich and
+ * \Drupal\plugin_type_example\Plugin\Sandwich\ExampleMeatballSandwich. Take a
+ * look at each.  These plugins use annotated plugin discovery, and as such have
+ * an annotation block in the doc block for the class.  Other than that, they
+ * implement the \Drupal\plugin_type_example\SandwichInterface, which is
+ * required, since we've set that interface for the plugin type in
+ * \Drupal\plugin_type_example\SandwichPluginManager::__construct.
+ *
+ * To see the plugins in action visit /examples/plugin_type_example.  The output
+ * is rendered in
+ * Drupal\plugin_type_example\Controller\PluginTypeExampleController::description().
+ * Read through that method to see how to use plugins using the
+ * plugin.manager.sandwich service.
+ */
+
+/**
+ * Implements hook_sandwich_info_alter().
+ *
+ * This hook allows other modules to alter the definitions of all the Sandwich
+ * plugins. The module that creates the plugin type probably wouldn't implement
+ * this itself, this is just for demonstration.
+ *
+ * This hook is invoked by SandwichPluginManager::__construct().
+ *
+ * @param $sandwich_plugin_info
+ *   This is the array of plugin definitions.
+ */
+function plugin_type_example_sandwich_info_alter(&$sandwich_plugin_info) {
+  // Let's change the 'foobar' property for all sandwiches.
+  foreach ($sandwich_plugin_info as $plugin_id => $plugin_definition) {
+    $sandwich_plugin_info[$plugin_id]['foobar'] = 'We have altered this in the alter hook';
+  }
+
+  // The ham sandwich is more calorific in our system.  Note that the string
+  // 'ham_sandwich' is the id name of the Ham Sandwich plugin, which is set
+  // in the annotation in
+  // \Drupal\plugin_type_example\Plugin\Sandwich\ExampleHamSandwich
+  $sandwich_plugin_info['ham_sandwich']['calories'] = 500;
+}
diff --git a/plugin_type_example/plugin_type_example.routing.yml b/plugin_type_example/plugin_type_example.routing.yml
new file mode 100644
index 0000000..20b32ef
--- /dev/null
+++ b/plugin_type_example/plugin_type_example.routing.yml
@@ -0,0 +1,8 @@
+# This defines our example page's path to the routing system.
+plugin_type_example.description:
+  path: '/examples/plugin_type_example'
+  defaults:
+    _controller: '\Drupal\plugin_type_example\Controller\PluginTypeExampleController::description'
+    _title: 'Plugin Type Example'
+  requirements:
+    _permission: 'access content'
diff --git a/plugin_type_example/plugin_type_example.services.yml b/plugin_type_example/plugin_type_example.services.yml
new file mode 100644
index 0000000..bd9c129
--- /dev/null
+++ b/plugin_type_example/plugin_type_example.services.yml
@@ -0,0 +1,19 @@
+# This declares the plugin manager to the service container.
+# For background information on the service container, see
+# http://symfony.com/doc/current/book/service_container.html.
+# Services declaration files such as this one are compiled to PHP code in
+# sites/default/files/php/service_container, so changes here require that folder
+# to be deleted in order to have Drupal notice them.
+services:
+  # The machine name of the service. This is the string that must be passed to
+  # Drupal::service() to get the instantiated plugin manager.
+  plugin.manager.sandwich:
+    # This tells the service container the name of our plugin manager class.
+    class: Drupal\plugin_type_example\SandwichPluginManager
+    # The default parameters to pass to the class constructor,
+    # SandwichPluginManager::__construct().
+    # Arguments beginning with @ are the names of other services that are being
+    # injected into this service. Many common ones are found in
+    # /core/core.services.yml.
+    # See https://drupal.org/node/2133171 for more detail.
+    arguments: ['@container.namespaces', '@cache.default', '@module_handler']
diff --git a/plugin_type_example/src/Controller/PluginTypeExampleController.php b/plugin_type_example/src/Controller/PluginTypeExampleController.php
new file mode 100644
index 0000000..7f5b042
--- /dev/null
+++ b/plugin_type_example/src/Controller/PluginTypeExampleController.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\plugin_type_example\Controller\PluginTypeExampleController.
+ */
+
+namespace Drupal\plugin_type_example\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\plugin_type_example\SandwichPluginManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Controller for our example pages.
+ */
+class PluginTypeExampleController extends ControllerBase {
+
+  /**
+   * The sandwich plugin manager used to get all of the sandwich plugins for
+   * use.
+   *
+   * @var \Drupal\plugin_type_example\SandwichPluginManager
+   */
+  protected $sandwichManager;
+
+  /**
+   * Constructs a \Drupal\plugin_type_example\PluginTypeExampleController
+   * object.
+   *
+   * @param \Drupal\plugin_type_example\SandwichPluginManager $sandwichManager
+   *   The sandwich plugin manager service.
+   */
+  public function __construct(SandwichPluginManager $sandwichManager) {
+    $this->sandwichManager = $sandwichManager;
+  }
+
+  /**
+   * Displays a page with an overview of our plugin type and plugins.  Lists all
+   * the Sandwich plugin definitions by using methods on the
+   * \Drupal\plugin_type_example\SandwichPluginManager class.  Lists out the
+   * description for each plugin found by invoking methods defined on the
+   * plugins themselves.  You can find the plugins we have defined in the
+   * \Drupal\plugin_type_example\Plugin\Sandwich namespace.
+   */
+  public function description() {
+    $build = array();
+
+    $build['intro'] = array(
+      '#markup' => t('The plugin type example page.'),
+    );
+
+    // Get the list of all the sandwich plugins defined on the system from the
+    // plugin manager.
+    // Note that at this point, what we have is *definitions* of plugins, not
+    // the plugins themselves.
+    $sandwich_plugin_definitions = $this->sandwichManager->getDefinitions();
+
+    // Let's output a list of the plugin definitions we now have.
+    $items = array();
+    foreach ($sandwich_plugin_definitions as $sandwich_plugin_definition) {
+      // Here we use various properties from the plugin definition. These values
+      // are defined in the annotation at the top of the plugin class: see
+      // ExampleHamSandwich.
+      $items[] = t("!id (calories: !calories, foobar: !foobar )", array(
+        '!id'       => $sandwich_plugin_definition['id'],
+        '!calories' => $sandwich_plugin_definition['calories'],
+        '!foobar'   => $sandwich_plugin_definition['foobar'],
+      ));
+    }
+
+    // Add our list to the render array.
+    $build['plugin_definitions'] = array(
+      '#theme' => 'item_list',
+      '#title' => 'Sandwich plugin definitions',
+      '#items' => $items,
+    );
+
+    // If we want just a single plugin definition, we can use getDefinition().
+    // This requires us to know the ID of the plugin we want. This is set in the
+    // annotation on the plugin class: see ExampleHamSandwich.
+    $ham_sandwich_plugin_definition = $this->sandwichManager->getDefinition('ham_sandwich');
+
+    // To get an actual plugin, we call createInstance() on the plugin manager,
+    // passing the ID of the plugin we want to load.
+
+    // Let's output a list of the actual plugins.
+    $items = array();
+    // The array of plugin definitions is keyed by plugin id, so we can just use
+    // that to load our plugins.
+    foreach ($sandwich_plugin_definitions as $plugin_id => $sandwich_plugin_definition) {
+      // We now have a plugin! From here on it can be treated just as any other
+      // object: have its properties examined, methods called, etc.
+      $plugin = $this->sandwichManager->createInstance($plugin_id, array('of' => 'configuration values'));
+      $items[] = $plugin->description();
+    }
+
+    $build['plugins'] = array(
+      '#theme' => 'item_list',
+      '#title' => 'Sandwich plugins',
+      '#items' => $items,
+    );
+
+    return $build;
+  }
+
+  /**
+   * Override the parent method so that we can inject out sandwich plugin
+   * manager service into the controller.  This is dependancy injection at work
+   * for a controller.  Rather than access the global service container via
+   * \Drupal::service(), it's best practice to use dependancy injection.  Notice
+   * our controller extends ControllerBase, which implements the
+   * ContainerInjectionInterface interface.  As such we can implement a create
+   * method to control how our controller in instantiated.  We do just that,
+   * passing in the sandwich plugin manager service that we need.
+   */
+  public static function create(ContainerInterface $container) {
+    // Use the service container to instantiate a new instance of our
+    // controller.  The string we pass is the machine name of the service,
+    // which is set in the plugin_type_example.services.yml file.  We get back
+    // an instance of our SandwichPluginManager class, which is passed to the
+    // controller's constructor.
+    return new static($container->get('plugin.manager.sandwich'));
+  }
+}
diff --git a/plugin_type_example/src/Plugin/Sandwich/ExampleHamSandwich.php b/plugin_type_example/src/Plugin/Sandwich/ExampleHamSandwich.php
new file mode 100644
index 0000000..1c6df09
--- /dev/null
+++ b/plugin_type_example/src/Plugin/Sandwich/ExampleHamSandwich.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\plugin_type_example\Plugin\Sandwich\ExampleHamSandwich.
+ */
+
+namespace Drupal\plugin_type_example\Plugin\Sandwich;
+
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\plugin_type_example\SandwichInterface;
+
+/**
+ * Provides a ham sandwich.
+ *
+ * Because the plugin manager class for our plugins uses annotated class
+ * discovery, our ham sandwich only needs to exist within the Plugin\Sandwich
+ * namespace to be declared as a plugin. This is defined in
+ * \Drupal\plugin_type_example\SandwichPluginManager::__construct().
+ *
+ * The following is the plugin annotation. This is parsed by Doctrine to make
+ * the plugin definition. Any values defined here will be available in the
+ * plugin definition.
+ *
+ * This should be used for metadata that is specifically required to instantiate
+ * the plugin, or for example data that might be needed to display a list of all
+ * available plugins where the user selects one. This means many plugin
+ * annotations can be reduced to a plugin ID, a label and perhaps a description.
+ *
+ * @Plugin(
+ *   id = "ham_sandwich",
+ *   foobar = @Translation("This is an example value that is defined in the annotation."),
+ *   calories = 426,
+ * )
+ */
+class ExampleHamSandwich extends PluginBase implements SandwichInterface {
+
+  /**
+   * Get a description of the sandwich fillings.
+   *
+   * This is just an example method on our plugin that we can call to get
+   * something back.
+   */
+  public function description() {
+    return $this->t('Ham, mustard, rocket, sun-dried tomatoes.');
+  }
+
+}
diff --git a/plugin_type_example/src/Plugin/Sandwich/ExampleMeatballSandwich.php b/plugin_type_example/src/Plugin/Sandwich/ExampleMeatballSandwich.php
new file mode 100644
index 0000000..592ed07
--- /dev/null
+++ b/plugin_type_example/src/Plugin/Sandwich/ExampleMeatballSandwich.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\plugin_type_example\Plugin\Sandwich\ExampleMeatballSandwich.
+ */
+
+namespace Drupal\plugin_type_example\Plugin\Sandwich;
+
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\plugin_type_example\SandwichInterface;
+
+/**
+ * Provides a meatball sandwich.
+ *
+ * Because the plugin manager class for our plugins uses annotated class
+ * discovery, our meatball sandwich only needs to exist within the
+ * Plugin\Sandwich namespace to be declared as a plugin. This is defined in
+ * \Drupal\plugin_type_example\SandwichPluginManager::__construct().
+ *
+ * The following is the plugin annotation. This is parsed by Doctrine to make
+ * the plugin definition. Any values defined here will be available in the
+ * plugin definition.
+ *
+ * This should be used for metadata that is specifically required to instantiate
+ * the plugin, or for example data that might be needed to display a list of all
+ * available plugins where the user selects one. This means many plugin
+ * annotations can be reduced to a plugin ID, a label and perhaps a description.
+ *
+ * @Plugin(
+ *   id = "meatball_sandwich",
+ *   foobar = @Translation("Right around that corner there is a sandwich shop. They sell meatball sandwiches, the best I ever tasted. Would you go get me two? Come on partner. Thank you. Utah! Get me two!"),
+ *   calories = 1200,
+ * )
+ */
+class ExampleMeatballSandwich extends PluginBase implements SandwichInterface {
+
+  /**
+   * Get a description of the sandwich fillings.
+   *
+   * This is just an example method on our plugin that we can call to get
+   * something back.
+   */
+  public function description() {
+    return $this->t('Enjoy Italian style meatballs drenched in irresistible marinara sauce, served on freshly baked bread.');
+  }
+
+}
diff --git a/plugin_type_example/src/SandwichInterface.php b/plugin_type_example/src/SandwichInterface.php
new file mode 100644
index 0000000..fbe82a5
--- /dev/null
+++ b/plugin_type_example/src/SandwichInterface.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * @file
+ * Provides Drupal\plugin_type_example\SandwichInterface
+ */
+
+namespace Drupal\plugin_type_example;
+
+/**
+ * An interface for all Sandwich type plugins.
+ */
+interface SandwichInterface {
+
+  /**
+   * Provide a description of the sandwich.
+   *
+   * @return string
+   *   A string description of the sandwich.
+   */
+  public function description();
+}
diff --git a/plugin_type_example/src/SandwichPluginManager.php b/plugin_type_example/src/SandwichPluginManager.php
new file mode 100644
index 0000000..ee6b0c4
--- /dev/null
+++ b/plugin_type_example/src/SandwichPluginManager.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\plugin_type_example\SandwichPluginManager.
+ */
+
+namespace Drupal\plugin_type_example;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+
+/**
+ * Manages sandwich plugins.
+ *
+ * The SandwichPluginManager class extends the DefaultPluginManager to provide
+ * a way to manage sandwich plugins.
+ *
+ * As well as this class definition, we need to declare our plugin manager class
+ * as a service, in the plugin_type_example.services.yml file.
+ */
+class SandwichPluginManager extends DefaultPluginManager {
+
+  /**
+   * Creates the discovery object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    // We replace the $subdir parameter with our own value.
+    // This tells the plugin system to look for plugins in the 'Plugin/Sandwich'
+    // subfolder inside modules' 'src' folder.
+    $subdir = 'Plugin/Sandwich';
+
+    // The name of the interface that plugins should adhere to.  Drupal will
+    // enforce this as a requirement.  If a plugin does not implement this
+    // interface, than Drupal will throw an error.
+    $plugin_interface = 'Drupal\plugin_type_example\SandwichInterface';
+
+    // The name of the annotation class that contains the plugin definition.
+    $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin';
+
+    parent::__construct($subdir, $namespaces, $module_handler, $plugin_interface, $plugin_definition_annotation_name);
+
+    // This allows the plugin definitions to be altered by an alter hook. The
+    // parameter defines the name of the hook, thus: hook_sandwich_info_alter().
+    // In this example, we implement this hook to change the plugin definitions:
+    // see plugin_type_example_sandwich_info_alter().
+    // The parameter is the name of the alter hook.
+    $this->alterInfo('sandwich_info');
+
+    // This sets the caching method for our plugin definitions.  Plugin
+    // definitions are cached using the provided cache backend.  For our
+    // Sandwich plugin type, we've specified the @cache.default service be used
+    // in the plugin_type_example.services.yml file.  The second argument is a
+    // cache key prefix.  Out of the box Drupal with the default cache backend
+    // setup will store our plugin definition in the cache_default table using
+    // the sandwich_info key.  All that is implementation details however,
+    // all we care about it that caching for our plugin definition is taken
+    // care of by this call.
+    $this->setCacheBackend($cache_backend, 'sandwich_info');
+  }
+}
diff --git a/plugin_type_example/src/Tests/PluginTypeExampleTest.php b/plugin_type_example/src/Tests/PluginTypeExampleTest.php
new file mode 100644
index 0000000..d3899e4
--- /dev/null
+++ b/plugin_type_example/src/Tests/PluginTypeExampleTest.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Test case for Testing the page example module.
+ *
+ * This file contains the test cases to check if module is performing as
+ * expected.
+ */
+
+namespace Drupal\plugin_type_example\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Test the functionality of the Plugin Type Example module.
+ *
+ * @ingroup plugin_type_example
+ * @group plugin_type_example
+ * @group examples
+ */
+class PluginTypeExampleTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('plugin_type_example');
+
+  /**
+   * The installation profile to use with this test.
+   *
+   * @var string
+   */
+  protected $profile = 'minimal';
+
+  /**
+   * Test the plugin manager can be loaded, and the plugins are registered.
+   */
+  function testPluginExample() {
+    $manager = \Drupal::service('plugin.manager.sandwich');
+
+    $sandwich_plugin_definitions = $manager->getDefinitions();
+
+    // Check we have only one sandwich plugin.
+    $this->assertEqual(count($sandwich_plugin_definitions), 2, 'There are two sandwich plugins defined.');
+
+    // Check some of the properties of the ham sandwich plugin definition.
+    $sandwich_plugin_definition = $sandwich_plugin_definitions['ham_sandwich'];
+    $this->assertEqual($sandwich_plugin_definition['calories'], 500, 'The ham sandwich plugin definition\'s calories property is set.');
+
+    // Check the alter hook fired and changed a property.
+    $this->assertEqual($sandwich_plugin_definition['foobar'], 'We have altered this in the alter hook', 'The ham sandwich plugin definition\'s foobar property is set, and was correctly altered by the plugin info alter hook.');
+
+    // Create an instance of the ham sandwich plugin to check it works.
+    $plugin = $manager->createInstance('ham_sandwich', array('of' => 'configuration values'));
+
+    $this->assertEqual(get_class($plugin), 'Drupal\plugin_type_example\Plugin\Sandwich\ExampleHamSandwich', 'The ham sandwich plugin is instantiated and of the correct class.');
+  }
+
+  /**
+   * Test the output of the example page.
+   */
+  function testPluginExamplePage() {
+    $this->drupalGet('examples/plugin_type_example');
+    $this->assertResponse(200, 'Example page successfully accessed.');
+
+    // Check we see the plugin id.
+    $this->assertText(t('ham_sandwich'), 'The plugin ID is output.');
+
+    // Check we see the plugin description.
+    $this->assertText(t('Ham, mustard, rocket, sun-dried tomatoes.'), 'The plugin description is output.');
+  }
+
+}
