diff --git a/core/core.services.yml b/core/core.services.yml
index 3c27130..e4d163a 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -287,7 +287,7 @@ services:
     arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']
   module_handler:
     class: Drupal\Core\Extension\ModuleHandler
-    arguments: ['%container.modules%', '@cache.bootstrap']
+    arguments: ['%container.modules%', '@cache.bootstrap', '@event_dispatcher']
   theme_handler:
     class: Drupal\Core\Extension\ThemeHandler
     arguments: ['@config.factory', '@module_handler', '@state', '@info_parser', '@logger.channel.default', '@asset.css.collection_optimizer', '@config.installer', '@config.manager', '@router.builder']
@@ -297,6 +297,7 @@ services:
     parent: container.trait
     tags:
       - { name: plugin_manager_cache_clear }
+      - { name: event_subscriber }
   entity.definitions.installed:
     class: Drupal\Core\KeyValueStore\KeyValueStoreInterface
     factory_method: get
@@ -304,7 +305,7 @@ services:
     arguments: ['entity.definitions.installed']
   entity.definition_update_manager:
     class: Drupal\Core\Entity\EntityDefinitionUpdateManager
-    arguments: ['@entity.manager']
+    arguments: ['@entity.manager', '@event_dispatcher']
   entity.form_builder:
     class: Drupal\Core\Entity\EntityFormBuilder
     arguments: ['@entity.manager', '@form_builder']
diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
index 4f92c8a..e04d601 100644
--- a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
@@ -7,10 +7,12 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Core\Entity\Schema\EntityStorageSchemaInterface;
 use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface;
+use Drupal\Core\Entity\Schema\EntityStorageSchemaInterface;
+use Drupal\Core\Field\FieldStorageDefinitionEvent;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Manages entity definition updates.
@@ -47,13 +49,23 @@ class EntityDefinitionUpdateManager implements EntityDefinitionUpdateManagerInte
   protected $entityManager;
 
   /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
    * Constructs a new EntityDefinitionUpdateManager.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+   *   The event dispatcher.
    */
-  public function __construct(EntityManagerInterface $entity_manager) {
+  public function __construct(EntityManagerInterface $entity_manager, EventDispatcherInterface $event_dispatcher) {
     $this->entityManager = $entity_manager;
+    $this->eventDispatcher = $event_dispatcher;
   }
 
   /**
@@ -111,7 +123,7 @@ public function applyUpdates() {
       if (!empty($change_list['entity_type']) && $change_list['entity_type'] == static::DEFINITION_UPDATED) {
         $entity_type = $this->entityManager->getDefinition($entity_type_id);
         $original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
-        $this->entityManager->onEntityTypeUpdate($entity_type, $original);
+        $this->eventDispatcher->dispatch(EntityTypeEvent::UPDATE, new EntityTypeEvent($entity_type, $original));
       }
 
       // Process field storage definition changes.
@@ -122,15 +134,15 @@ public function applyUpdates() {
         foreach ($change_list['field_storage_definitions'] as $field_name => $change) {
           switch ($change) {
             case static::DEFINITION_CREATED:
-              $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions[$field_name]);
+              $this->eventDispatcher->dispatch(FieldStorageDefinitionEvent::CREATE, new FieldStorageDefinitionEvent($storage_definitions[$field_name]));
               break;
 
             case static::DEFINITION_UPDATED:
-              $this->entityManager->onFieldStorageDefinitionUpdate($storage_definitions[$field_name], $original_storage_definitions[$field_name]);
+              $this->eventDispatcher->dispatch(FieldStorageDefinitionEvent::UPDATE, new FieldStorageDefinitionEvent($storage_definitions[$field_name], $original_storage_definitions[$field_name]));
               break;
 
             case static::DEFINITION_DELETED:
-              $this->entityManager->onFieldStorageDefinitionDelete($original_storage_definitions[$field_name]);
+              $this->eventDispatcher->dispatch(FieldStorageDefinitionEvent::DELETE, new FieldStorageDefinitionEvent($original_storage_definitions[$field_name]));
               break;
           }
         }
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 9560c71..56e0280 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -10,19 +10,20 @@
 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\Component\Utility\String;
-use Drupal\Core\DependencyInjection\ClassResolverInterface;
-use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\Core\Entity\Exception\AmbiguousEntityClassException;
 use Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException;
 use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldStorageDefinitionEventSubscriberTrait;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
-use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
@@ -48,6 +49,8 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
 
   use ContainerAwareTrait;
   use StringTranslationTrait;
+  use EntityTypeEventSubscriberTrait;
+  use FieldStorageDefinitionEventSubscriberTrait;
 
   /**
    * Extra fields by bundle.
@@ -200,6 +203,13 @@ public function __construct(\Traversable $namespaces, ModuleHandlerInterface $mo
   /**
    * {@inheritdoc}
    */
+  public static function getSubscribedEvents() {
+    return static::getEntityTypeEvents() + static::getFieldStorageDefinitionEvents();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function clearCachedDefinitions() {
     parent::clearCachedDefinitions();
     $this->clearCachedBundles();
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index 8cf95ee..ec4d1da 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -9,11 +9,12 @@
 
 use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
  * Provides an interface for entity type managers.
  */
-interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListenerInterface, EntityBundleListenerInterface, FieldStorageDefinitionListenerInterface {
+interface EntityManagerInterface extends PluginManagerInterface, EventSubscriberInterface, EntityTypeListenerInterface, EntityBundleListenerInterface, FieldStorageDefinitionListenerInterface {
 
   /**
    * Builds a list of entity type labels suitable for a Form API options list.
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeEvent.php b/core/lib/Drupal/Core/Entity/EntityTypeEvent.php
new file mode 100644
index 0000000..fccc396
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityTypeEvent.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\EntityTypeEvent.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Symfony\Component\EventDispatcher\GenericEvent;
+
+/**
+ * Defines a base class for all field storage definition events.
+ */
+class EntityTypeEvent extends GenericEvent {
+
+  const CREATE = 'entity_type.definition.create';
+  const UPDATE = 'entity_type.definition.update';
+  const DELETE = 'entity_type.definition.delete';
+
+  /**
+   * The entity type.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface
+   */
+  protected $entityType;
+
+  /**
+   * The original entity type.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface
+   */
+  protected $original;
+
+  /**
+   * Constructs a new EntityTypeEvent.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The field storage definition.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $original
+   *   (optional) The original entity type. This should be passed only when
+   *   updating the entity type.
+   */
+  public function __construct(EntityTypeInterface $entity_type, EntityTypeInterface $original = NULL) {
+    $this->entityType = $entity_type;
+    $this->original = $original;
+  }
+
+  /**
+   * The field storage definition.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface
+   */
+  public function getEntityType() {
+    return $this->entityType;
+  }
+
+  /**
+   * The original field storage definition.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface
+   */
+  public function getOriginal() {
+    return $this->original;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeEventSubscriberTrait.php b/core/lib/Drupal/Core/Entity/EntityTypeEventSubscriberTrait.php
new file mode 100644
index 0000000..c9fdfb4
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityTypeEventSubscriberTrait.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\EntityTypeEventSubscriberTrait.
+ */
+
+namespace Drupal\Core\Entity;
+
+/**
+ * Helper methods for EntityTypeListenerInterface.
+ *
+ * This allows a class implementing EntityTypeListenerInterface to subscribe and
+ * react to entity type events.
+ *
+ * @see \Symfony\Component\EventDispatcher\EventSubscriberInterface
+ * @see \Drupal\Core\Entity\EntityTypeListenerInterface
+ */
+trait EntityTypeEventSubscriberTrait {
+
+  /**
+   * Returns the subscribed events.
+   *
+   * @return array
+   *   An array of subscribed event names.
+   *
+   * @see \Symfony\Component\EventDispatcher\EventSubscriberInterface::getSubscribedEvents()
+   */
+  public static function getEntityTypeEvents() {
+    $event = array('onEntityTypeEvent', 100);
+    $events[EntityTypeEvent::CREATE][] = $event;
+    $events[EntityTypeEvent::UPDATE][] = $event;
+    $events[EntityTypeEvent::DELETE][] = $event;
+    return $events;
+  }
+
+  /**
+   * Listener method for any entity type definition event.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeEvent $event
+   *   The field storage definition event object.
+   * @param string $event_name
+   *   The event name.
+   */
+  public function onEntityTypeEvent(EntityTypeEvent $event, $event_name) {
+    switch ($event_name) {
+      case EntityTypeEvent::CREATE:
+        $this->onEntityTypeCreate($event->getEntityType());
+        break;
+
+      case EntityTypeEvent::UPDATE:
+        $this->onEntityTypeUpdate($event->getEntityType(), $event->getOriginal());
+        break;
+
+      case EntityTypeEvent::DELETE:
+        $this->onEntityTypeDelete($event->getEntityType());
+        break;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  abstract public function onEntityTypeCreate(EntityTypeInterface $entity_type);
+
+  /**
+   * {@inheritdoc}
+   */
+  abstract public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original);
+
+  /**
+   * {@inheritdoc}
+   */
+  abstract public function onEntityTypeDelete(EntityTypeInterface $entity_type);
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index 428ca2a..b049230 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -12,7 +12,9 @@
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Entity\EntityTypeEvent;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Class that manages modules in a Drupal installation.
@@ -86,6 +88,13 @@ class ModuleHandler implements ModuleHandlerInterface {
   protected $alterFunctions;
 
   /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
    * Constructs a ModuleHandler object.
    *
    * @param array $module_list
@@ -94,16 +103,19 @@ class ModuleHandler implements ModuleHandlerInterface {
    *   %container.modules% parameter being set up by DrupalKernel.
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
    *   Cache backend for storing module hook implementation information.
+   * @param $event_dispatcher
+   *   The event dispatcher service.
    *
    * @see \Drupal\Core\DrupalKernel
    * @see \Drupal\Core\CoreServiceProvider
    */
-  public function __construct(array $module_list = array(), CacheBackendInterface $cache_backend) {
+  public function __construct(array $module_list = array(), CacheBackendInterface $cache_backend, EventDispatcherInterface $event_dispatcher) {
     $this->moduleList = array();
     foreach ($module_list as $name => $module) {
       $this->moduleList[$name] = new Extension($module['type'], $module['pathname'], $module['filename']);
     }
     $this->cacheBackend = $cache_backend;
+    $this->eventDispatcher = $event_dispatcher;
   }
 
   /**
@@ -821,14 +833,12 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
           $version = max(max($versions), $version);
         }
 
-        // Notify the entity manager that this module's entity types are new,
-        // so that it can notify all interested handlers. For example, a
-        // SQL-based storage handler can use this as an opportunity to create
-        // the necessary database tables.
-        $entity_manager = \Drupal::entityManager();
-        foreach ($entity_manager->getDefinitions() as $entity_type) {
+        // Notify interested components that this module's entity types are new.
+        // For example, a SQL-based storage handler can use this as an
+        // opportunity to create the necessary database tables.
+        foreach (\Drupal::entityManager()->getDefinitions() as $entity_type) {
           if ($entity_type->getProvider() == $module) {
-            $entity_manager->onEntityTypeCreate($entity_type);
+            $this->eventDispatcher->dispatch(EntityTypeEvent::CREATE, new EntityTypeEvent($entity_type));
           }
         }
 
@@ -968,13 +978,12 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
       // Remove all configuration belonging to the module.
       \Drupal::service('config.manager')->uninstall('module', $module);
 
-      // Notify the entity manager that this module's entity types are being
-      // deleted, so that it can notify all interested handlers. For example,
-      // a SQL-based storage handler can use this as an opportunity to drop
-      // the corresponding database tables.
-      foreach ($entity_manager->getDefinitions() as $entity_type) {
+      // Notify interested components that this module's entity types are being
+      // deleted. For example, a SQL-based storage handler can use this as an
+      // opportunity to drop the corresponding database tables.
+      foreach (\Drupal::entityManager()->getDefinitions() as $entity_type) {
         if ($entity_type->getProvider() == $module) {
-          $entity_manager->onEntityTypeDelete($entity_type);
+          $this->eventDispatcher->dispatch(EntityTypeEvent::DELETE, new EntityTypeEvent($entity_type));
         }
       }
 
diff --git a/core/lib/Drupal/Core/Field/FieldStorageDefinitionEvent.php b/core/lib/Drupal/Core/Field/FieldStorageDefinitionEvent.php
new file mode 100644
index 0000000..ccfccf6
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/FieldStorageDefinitionEvent.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\FieldStorageDefinitionEvent.
+ */
+
+namespace Drupal\Core\Field;
+
+use Symfony\Component\EventDispatcher\GenericEvent;
+
+/**
+ * Defines a base class for all field storage definition events.
+ */
+class FieldStorageDefinitionEvent extends GenericEvent {
+
+  const CREATE = 'field_storage.definition.create';
+  const UPDATE = 'field_storage.definition.update';
+  const DELETE = 'field_storage.definition.delete';
+
+  /**
+   * The field storage definition.
+   *
+   * @var \Drupal\Core\Field\FieldStorageDefinitionInterface
+   */
+  protected $fieldStorageDefinition;
+
+  /**
+   * The original field storage definition.
+   *
+   * @var \Drupal\Core\Field\FieldStorageDefinitionInterface
+   */
+  protected $original;
+
+  /**
+   * Constructs a new FieldStorageDefinitionEvent.
+   *
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
+   *   The field storage definition.
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $original
+   *   (optional) The original field storage definition. This should be passed
+   *   only when updating the storage definition.
+   */
+  public function __construct(FieldStorageDefinitionInterface $field_storage_definition, FieldStorageDefinitionInterface $original = NULL) {
+    $this->fieldStorageDefinition = $field_storage_definition;
+    $this->original = $original;
+  }
+
+  /**
+   * The field storage definition.
+   *
+   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
+   */
+  public function getFieldStorageDefinition() {
+    return $this->fieldStorageDefinition;
+  }
+
+  /**
+   * The original field storage definition.
+   *
+   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
+   */
+  public function getOriginal() {
+    return $this->original;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Field/FieldStorageDefinitionEventSubscriberTrait.php b/core/lib/Drupal/Core/Field/FieldStorageDefinitionEventSubscriberTrait.php
new file mode 100644
index 0000000..704b390
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/FieldStorageDefinitionEventSubscriberTrait.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\FieldStorageDefinitionEventSubscriberTrait.
+ */
+
+namespace Drupal\Core\Field;
+
+/**
+ * Helper methods for FieldStorageDefinitionListenerInterface.
+ *
+ * This allows a class implementing FieldStorageDefinitionListenerInterface to
+ * subscribe and react to field storage definition events.
+ *
+ * @see \Symfony\Component\EventDispatcher\EventSubscriberInterface
+ * @see \Drupal\Core\Field\FieldStorageDefinitionListenerInterface
+ */
+trait FieldStorageDefinitionEventSubscriberTrait {
+
+  /**
+   * Returns the subscribed events.
+   *
+   * @return array
+   *   An array of subscribed event names.
+   *
+   * @see \Symfony\Component\EventDispatcher\EventSubscriberInterface::getSubscribedEvents()
+   */
+  public static function getFieldStorageDefinitionEvents() {
+    $event = array('onFieldStorageDefinitionEvent', 100);
+    $events[FieldStorageDefinitionEvent::CREATE][] = $event;
+    $events[FieldStorageDefinitionEvent::UPDATE][] = $event;
+    $events[FieldStorageDefinitionEvent::DELETE][] = $event;
+    return $events;
+  }
+
+  /**
+   * Listener method for any field storage definition event.
+   *
+   * @param \Drupal\Core\Field\FieldStorageDefinitionEvent $event
+   *   The field storage definition event object.
+   * @param string $event_name
+   *   The event name.
+   */
+  public function onFieldStorageDefinitionEvent(FieldStorageDefinitionEvent $event, $event_name) {
+    switch ($event_name) {
+      case FieldStorageDefinitionEvent::CREATE:
+        $this->onFieldStorageDefinitionCreate($event->getFieldStorageDefinition());
+        break;
+
+      case FieldStorageDefinitionEvent::UPDATE:
+        $this->onFieldStorageDefinitionUpdate($event->getFieldStorageDefinition(), $event->getOriginal());
+        break;
+
+      case FieldStorageDefinitionEvent::DELETE:
+        $this->onFieldStorageDefinitionDelete($event->getFieldStorageDefinition());
+        break;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  abstract public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition);
+
+  /**
+   * {@inheritdoc}
+   */
+  abstract public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original);
+
+  /**
+   * {@inheritdoc}
+   */
+  abstract public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition);
+
+}
diff --git a/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php b/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php
index 752a1f3..9483d1b 100644
--- a/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php
@@ -8,8 +8,10 @@
 namespace Drupal\system\Tests\Entity;
 
 use Drupal\Core\Entity\EntityStorageException;
+use Drupal\Core\Entity\EntityTypeEvent;
 use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
 use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldStorageDefinitionEvent;
 use Drupal\entity_test\FieldStorageDefinition;
 
 /**
@@ -443,6 +445,41 @@ public function testEntityIndexCreateWithData() {
   }
 
   /**
+   * Tests entity type and field storage definition events.
+   */
+  public function testDefinitionEvents() {
+    /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */
+    $event_dispatcher = $this->container->get('event_dispatcher');
+    /** @var \Drupal\entity_test\EntityTestDefinitionSubscriber $event_subscriber */
+    $event_subscriber = $this->container->get('entity_test.definition.subscriber');
+    $event_subscriber->enableEventTracking();
+
+    // Test field storage definition events.
+    $storage_definition = current($this->entityManager->getFieldStorageDefinitions('entity_test_rev'));
+    $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvent::DELETE), 'Entity type delete was not dispatched yet.');
+    $event_dispatcher->dispatch(FieldStorageDefinitionEvent::DELETE, new FieldStorageDefinitionEvent($storage_definition));
+    $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvent::DELETE), 'Entity type delete event successfully dispatched.');
+    $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvent::CREATE), 'Entity type create was not dispatched yet.');
+    $event_dispatcher->dispatch(FieldStorageDefinitionEvent::CREATE, new FieldStorageDefinitionEvent($storage_definition));
+    $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvent::CREATE), 'Entity type create event successfully dispatched.');
+    $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvent::UPDATE), 'Entity type update was not dispatched yet.');
+    $event_dispatcher->dispatch(FieldStorageDefinitionEvent::UPDATE, new FieldStorageDefinitionEvent($storage_definition, $storage_definition));
+    $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvent::UPDATE), 'Entity type update event successfully dispatched.');
+
+    // Test entity type events.
+    $entity_type = $this->entityManager->getDefinition('entity_test_rev');
+    $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvent::CREATE), 'Entity type create was not dispatched yet.');
+    $event_dispatcher->dispatch(EntityTypeEvent::CREATE, new EntityTypeEvent($entity_type));
+    $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvent::CREATE), 'Entity type create event successfully dispatched.');
+    $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvent::UPDATE), 'Entity type update was not dispatched yet.');
+    $event_dispatcher->dispatch(EntityTypeEvent::UPDATE, new EntityTypeEvent($entity_type, $entity_type));
+    $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvent::UPDATE), 'Entity type update event successfully dispatched.');
+    $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvent::DELETE), 'Entity type delete was not dispatched yet.');
+    $event_dispatcher->dispatch(EntityTypeEvent::DELETE, new EntityTypeEvent($entity_type));
+    $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvent::DELETE), 'Entity type delete event successfully dispatched.');
+  }
+
+  /**
    * Updates the 'entity_test_update' entity type to revisionable.
    */
   protected function updateEntityTypeToRevisionable() {
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.services.yml b/core/modules/system/tests/modules/entity_test/entity_test.services.yml
new file mode 100644
index 0000000..8769fbc
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/entity_test.services.yml
@@ -0,0 +1,6 @@
+services:
+  entity_test.definition.subscriber:
+    class: Drupal\entity_test\EntityTestDefinitionSubscriber
+    arguments: ['@state']
+    tags:
+      - { name: event_subscriber }
diff --git a/core/modules/system/tests/modules/entity_test/src/EntityTestDefinitionSubscriber.php b/core/modules/system/tests/modules/entity_test/src/EntityTestDefinitionSubscriber.php
new file mode 100644
index 0000000..a789d7b
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/EntityTestDefinitionSubscriber.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity_test\EntityTestDefinitionSubscriber.
+ */
+
+namespace Drupal\entity_test;
+
+use Drupal\Core\Entity\EntityTypeEvent;
+use Drupal\Core\Entity\EntityTypeEventSubscriberTrait;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeListenerInterface;
+use Drupal\Core\Field\FieldStorageDefinitionEvent;
+use Drupal\Core\Field\FieldStorageDefinitionEventSubscriberTrait;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
+use Drupal\Core\State\StateInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+
+/**
+ * Test entity type and field storage definition event subscriber.
+ */
+class EntityTestDefinitionSubscriber implements EventSubscriberInterface, EntityTypeListenerInterface, FieldStorageDefinitionListenerInterface {
+
+  use EntityTypeEventSubscriberTrait;
+  use FieldStorageDefinitionEventSubscriberTrait;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * Flag determining whether events should be tracked.
+   *
+   * @var bool
+   */
+  protected $trackEvents = FALSE;
+
+  /**
+   * {@inheritdoc}
+   */
+  function __construct(StateInterface $state) {
+    $this->state = $state;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    return static::getEntityTypeEvents() + static::getFieldStorageDefinitionEvents();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onEntityTypeCreate(EntityTypeInterface $entity_type) {
+    $this->storeEvent(EntityTypeEvent::CREATE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
+    $this->storeEvent(EntityTypeEvent::UPDATE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
+    $this->storeEvent(EntityTypeEvent::DELETE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
+    $this->storeEvent(FieldStorageDefinitionEvent::CREATE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
+    $this->storeEvent(FieldStorageDefinitionEvent::UPDATE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {
+    $this->storeEvent(FieldStorageDefinitionEvent::DELETE);
+  }
+
+  /**
+   * Enables event tracking.
+   */
+  public function enableEventTracking() {
+    $this->trackEvents = TRUE;
+  }
+
+  /**
+   * Checks whether an event has been dispatched.
+   *
+   * @param string $event_name
+   *   The event name.
+   *
+   * @return bool
+   *   TRUE if the event has been dispatched, FALSE otherwise.
+   */
+  public function hasEventFired($event_name) {
+    return (bool) $this->state->get($event_name);
+  }
+
+  /**
+   * Stores the specified event.
+   *
+   * @param string $event_name
+   *   The event name.
+   */
+  protected function storeEvent($event_name) {
+    if ($this->trackEvents) {
+      $this->state->set($event_name, TRUE);
+    }
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php
index bc5da97..f4c38a5 100644
--- a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php
+++ b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php
@@ -24,6 +24,12 @@ class ModuleHandlerTest extends UnitTestCase {
    */
   protected $cacheBackend;
 
+  /**
+   * The mocked event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
 
   /**
    * The tested module handler.
@@ -39,13 +45,14 @@ class ModuleHandlerTest extends UnitTestCase {
    */
   protected function setUp() {
     $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->eventDispatcher = $this->getMock('\Symfony\Component\EventDispatcher\EventDispatcherInterface');
     $this->moduleHandler = new ModuleHandler(array(
       'module_handler_test' => array(
         'type' => 'module',
         'pathname' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/module_handler_test.info.yml',
         'filename' => 'module_handler_test.module',
       )
-    ), $this->cacheBackend);
+    ), $this->cacheBackend, $this->eventDispatcher);
   }
 
   /**
@@ -96,7 +103,7 @@ public function testModuleReloading() {
             'pathname' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/module_handler_test.info.yml',
             'filename' => 'module_handler_test.module',
           )
-        ), $this->cacheBackend
+        ), $this->cacheBackend, $this->eventDispatcher
       ))
       ->setMethods(array('load'))
       ->getMock();
@@ -164,7 +171,7 @@ public function testGetModuleWithNonExistingModule() {
   public function testSetModuleList() {
     $module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandler')
       ->setConstructorArgs(array(
-        array(), $this->cacheBackend
+        array(), $this->cacheBackend, $this->eventDispatcher
       ))
       ->setMethods(array('resetImplementations'))
       ->getMock();
@@ -192,7 +199,7 @@ public function testAddModule() {
 
     $module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandler')
       ->setConstructorArgs(array(
-        array(), $this->cacheBackend
+        array(), $this->cacheBackend, $this->eventDispatcher
       ))
       ->setMethods(array('resetImplementations'))
       ->getMock();
@@ -214,7 +221,7 @@ public function testAddProfile() {
 
     $module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandler')
       ->setConstructorArgs(array(
-        array(), $this->cacheBackend
+        array(), $this->cacheBackend, $this->eventDispatcher
       ))
       ->setMethods(array('resetImplementations'))
       ->getMock();
@@ -250,7 +257,7 @@ public function testLoadAllIncludes() {
             'pathname' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/module_handler_test.info.yml',
             'filename' => 'module_handler_test.module',
           )
-        ), $this->cacheBackend
+        ), $this->cacheBackend, $this->eventDispatcher
       ))
       ->setMethods(array('loadInclude'))
       ->getMock();
@@ -331,7 +338,7 @@ public function testCachedGetImplementations() {
             'pathname' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/module_handler_test.info.yml',
             'filename' => 'module_handler_test.module',
           )
-        ), $this->cacheBackend
+        ), $this->cacheBackend, $this->eventDispatcher
       ))
       ->setMethods(array('buildImplementationInfo', 'loadInclude'))
       ->getMock();
@@ -366,7 +373,7 @@ public function testCachedGetImplementationsMissingMethod() {
             'pathname' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/module_handler_test.info.yml',
             'filename' => 'module_handler_test.module',
           )
-        ), $this->cacheBackend
+        ), $this->cacheBackend, $this->eventDispatcher
       ))
       ->setMethods(array('buildImplementationInfo'))
       ->getMock();
