diff --git a/core/core.services.yml b/core/core.services.yml
index 6134359..9baa90c 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -306,10 +306,12 @@ services:
     arguments: ['@router.dumper', '@lock', '@event_dispatcher', '@module_handler', '@controller_resolver']
   path.alias_manager.cached:
     class: Drupal\Core\CacheDecorator\AliasManagerCacheDecorator
+    tags:
+      - { name: event_subscriber }
     arguments: ['@path.alias_manager', '@cache.path']
   path.crud:
     class: Drupal\Core\Path\Path
-    arguments: ['@database', '@path.alias_manager']
+    arguments: ['@database', '@event_dispatcher']
 # The argument to the hashing service defined in services.yml, to the
 # constructor of PhpassHashedPassword is the log2 number of iterations for
 # password stretching.
diff --git a/core/lib/Drupal/Core/CacheDecorator/AliasManagerCacheDecorator.php b/core/lib/Drupal/Core/CacheDecorator/AliasManagerCacheDecorator.php
index 01264c2..56b9fdc 100644
--- a/core/lib/Drupal/Core/CacheDecorator/AliasManagerCacheDecorator.php
+++ b/core/lib/Drupal/Core/CacheDecorator/AliasManagerCacheDecorator.php
@@ -9,11 +9,13 @@
 
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Path\AliasManagerInterface;
+use Drupal\Core\Path\PathEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
  * Class used by the PathSubscriber to get the system path and cache path lookups.
  */
-class AliasManagerCacheDecorator implements CacheDecoratorInterface, AliasManagerInterface {
+class AliasManagerCacheDecorator implements CacheDecoratorInterface, AliasManagerInterface, EventSubscriberInterface {
 
   /**
    * @var \Drupal\Core\Path\AliasManagerInterface
@@ -55,14 +57,14 @@ public function __construct(AliasManagerInterface $alias_manager, CacheBackendIn
   }
 
   /**
-   * Implements \Drupal\Core\CacheDecorator\CacheDecoratorInterface::setCacheKey().
+   * {@inheritdoc}
    */
   public function setCacheKey($key) {
     $this->cacheKey = $key;
   }
 
   /**
-   * Implements \Drupal\Core\CacheDecorator\CacheDecoratorInterface::writeCache().
+   * {@inheritdoc}
    *
    * Cache an array of the system paths available on each page. We assume
    * that aliases will be needed for the majority of these paths during
@@ -81,7 +83,7 @@ public function writeCache() {
   }
 
   /**
-   * Implements \Drupal\Core\Path\AliasManagerInterface::getSystemPath().
+   * {@inheritdoc}
    */
   public function getSystemPath($path, $path_language = NULL) {
     $system_path = $this->aliasManager->getSystemPath($path, $path_language);
@@ -98,23 +100,41 @@ public function getSystemPath($path, $path_language = NULL) {
   }
 
   /**
-   * Implements \Drupal\Core\Path\AliasManagerInterface::getPathAlias().
+   * {@inheritdoc}
    */
   public function getPathAlias($path, $path_language = NULL) {
     return $this->aliasManager->getPathAlias($path, $path_language);
   }
 
   /**
-   * Implements \Drupal\Core\Path\AliasManagerInterface::getPathLookups().
+   * {@inheritdoc}
    */
   public function getPathLookups() {
     return $this->aliasManager->getPathLookups();
   }
 
   /**
-   * Implements \Drupal\Core\Path\AliasManagerInterface::preloadPathLookups().
+   * {@inheritdoc}
    */
   public function preloadPathLookups(array $path_list) {
     $this->aliasManager->preloadPathLookups($path_list);
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cacheClear() {
+    $this->cache->delete($this->cacheKey);
+    $this->aliasManager->cacheClear();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  static function getSubscribedEvents() {
+    $events[PathEvents::UPDATE][] = array('cacheClear');
+    $events[PathEvents::INSERT][] = array('cacheClear');
+    $events[PathEvents::DELETE][] = array('cacheClear');
+    return $events;
+  }
 }
diff --git a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
index 5f4563e..09aceb6 100644
--- a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\EventSubscriber;
 
-use Drupal\Core\CacheDecorator\AliasManagerCacheDecorator;
+use Drupal\Core\Path\AliasManagerInterface;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpKernel\KernelEvents;
@@ -23,7 +23,7 @@ class PathSubscriber extends PathListenerBase implements EventSubscriberInterfac
   /**
    * The alias manager that caches alias lookups based on the request.
    *
-   * @var \Drupal\Core\CacheDecorator\AliasManagerCacheDecorator
+   * @var \Drupal\Core\Path\AliasManagerInterface
    */
   protected $aliasManager;
 
@@ -34,7 +34,7 @@ class PathSubscriber extends PathListenerBase implements EventSubscriberInterfac
    */
   protected $pathProcessor;
 
-  public function __construct(AliasManagerCacheDecorator $alias_manager, InboundPathProcessorInterface $path_processor) {
+  public function __construct(AliasManagerInterface $alias_manager, InboundPathProcessorInterface $path_processor) {
     $this->aliasManager = $alias_manager;
     $this->pathProcessor = $path_processor;
   }
diff --git a/core/lib/Drupal/Core/Path/AliasManager.php b/core/lib/Drupal/Core/Path/AliasManager.php
index 151bb23..74df0a7 100644
--- a/core/lib/Drupal/Core/Path/AliasManager.php
+++ b/core/lib/Drupal/Core/Path/AliasManager.php
@@ -124,13 +124,13 @@ public function getPathAlias($path, $path_language = NULL) {
   /**
    * Implements \Drupal\Core\Path\AliasManagerInterface::cacheClear().
    */
-  public function cacheClear($source = NULL) {
+  public function cacheClear() {
     $this->lookupMap = array();
     $this->noSource = array();
     $this->no_aliases = array();
     $this->firstCall = TRUE;
     $this->preloadedPathLookups = array();
-    $this->pathAliasWhitelistRebuild($source);
+    $this->pathAliasWhitelistRebuild();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Path/Path.php b/core/lib/Drupal/Core/Path/Path.php
index 4a3410f..f381e82 100644
--- a/core/lib/Drupal/Core/Path/Path.php
+++ b/core/lib/Drupal/Core/Path/Path.php
@@ -7,9 +7,9 @@
 
 namespace Drupal\Core\Path;
 
-use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Language\Language;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Defines a class for CRUD operations on path aliases.
@@ -24,21 +24,24 @@ class Path {
   protected $connection;
 
   /**
+   * An event dispatcher instance to use for configuration events.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
    * Constructs a Path CRUD object.
    *
    * @param \Drupal\Core\Database\Connection $connection
    *   A database connection for reading and writing path aliases.
    *
-   * @param \Drupal\Core\Path\AliasManager $alias_manager
-   *   An alias manager with an internal cache of stored aliases.
-   *
-   * @todo This class should not take an alias manager in its constructor. Once
-   *   we move to firing an event for CRUD operations instead of invoking a
-   *   hook, we can have a listener that calls cacheClear() on the alias manager.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+   *   An event dispatcher instance to use for configuration events.
    */
-  public function __construct(Connection $connection, AliasManager $alias_manager) {
+  public function __construct(Connection $connection, EventDispatcherInterface $event_dispatcher) {
     $this->connection = $connection;
-    $this->alias_manager = $alias_manager;
+    $this->eventDispatcher = $event_dispatcher;
   }
 
   /**
@@ -78,8 +81,7 @@ public function save($source, $alias, $langcode = Language::LANGCODE_NOT_SPECIFI
         ->fields($fields);
       $pid = $query->execute();
       $fields['pid'] = $pid;
-      // @todo: Find a correct place to invoke hook_path_insert().
-      $hook = 'path_insert';
+      $operation = 'insert';
     }
     else {
       $fields['pid'] = $pid;
@@ -87,13 +89,11 @@ public function save($source, $alias, $langcode = Language::LANGCODE_NOT_SPECIFI
         ->fields($fields)
         ->condition('pid', $pid);
       $pid = $query->execute();
-      // @todo: figure out where we can invoke hook_path_update()
-      $hook = 'path_update';
+      $operation = 'update';
     }
     if ($pid) {
-      // @todo Switch to using an event for this instead of a hook.
-      module_invoke_all($hook, $fields);
-      $this->alias_manager->cacheClear();
+      $event = $operation == 'update' ? PathEvents::UPDATE : PathEvents::INSERT;
+      $this->eventDispatcher->dispatch($event, new PathInsertUpdateEvent($fields));
       return $fields;
     }
     return FALSE;
@@ -137,9 +137,7 @@ public function delete($conditions) {
       $query->condition($field, $value);
     }
     $deleted = $query->execute();
-    // @todo Switch to using an event for this instead of a hook.
-    module_invoke_all('path_delete', $path);
-    $this->alias_manager->cacheClear();
+    $this->eventDispatcher->dispatch(PathEvents::DELETE, new PathDeleteEvent($path));
     return $deleted;
   }
 }
diff --git a/core/lib/Drupal/Core/Path/PathDeleteEvent.php b/core/lib/Drupal/Core/Path/PathDeleteEvent.php
new file mode 100644
index 0000000..fd927be
--- /dev/null
+++ b/core/lib/Drupal/Core/Path/PathDeleteEvent.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Path\PathDeleteEvent.
+ */
+
+namespace Drupal\Core\Path;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class PathDeleteEvent extends Event {
+
+  /**
+   * Path being deleted.
+   *
+   * @var string
+   */
+  protected $path;
+
+  /**
+   * Constructs PathDeleteEvent.
+   *
+   * @param string $path
+   *   Path being deleted.
+   */
+  public function __construct($path) {
+    $this->path = $path;
+  }
+
+  /**
+   * Returns path that is being deleted at this event.
+   *
+   * @return string
+   *   Path being deleted.
+   */
+  public function getPath() {
+    return $this->path;
+  }
+}
diff --git a/core/lib/Drupal/Core/Path/PathEvents.php b/core/lib/Drupal/Core/Path/PathEvents.php
new file mode 100644
index 0000000..c2e8db4
--- /dev/null
+++ b/core/lib/Drupal/Core/Path/PathEvents.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Path\PathEvents.
+ */
+
+namespace Drupal\Core\Path;
+
+/**
+ * Contains all events thrown by path alias system.
+ */
+final class PathEvents {
+  /**
+   * The INSERT event occurs when a new path alias is inserted.
+   *
+   * @var string
+   */
+  const INSERT = 'path.insert';
+
+  /**
+   * The UPDATE event occurs when an existing path alias is updated.
+   *
+   * @var string
+   */
+  const UPDATE = 'path.update';
+
+  /**
+   * The DELETE event occurs when a path alias is deleted.
+   *
+   * @var string
+   */
+  const DELETE = 'path.delete';
+}
diff --git a/core/lib/Drupal/Core/Path/PathInsertUpdateEvent.php b/core/lib/Drupal/Core/Path/PathInsertUpdateEvent.php
new file mode 100644
index 0000000..0d70542
--- /dev/null
+++ b/core/lib/Drupal/Core/Path/PathInsertUpdateEvent.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Path\PathInsertUpdateEvent.
+ */
+
+namespace Drupal\Core\Path;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class PathInsertUpdateEvent extends Event {
+
+  /**
+   * Information about path alias being inserted/updated.
+   *
+   * @var array
+   */
+  protected $fields;
+
+  /**
+   * Constructs PathInsertUpdateEvent.
+   *
+   * @param array $fields
+   *   Path alias information.
+   */
+  public function __construct($fields) {
+    $this->fields = $fields;
+  }
+
+  /**
+   * Returns path alias information.
+   *
+   * @return array
+   *   Array containing path alias information.
+   */
+  public function getFields() {
+    return $this->fields;
+  }
+}
diff --git a/core/modules/path/path.api.php b/core/modules/path/path.api.php
deleted file mode 100644
index c69b97d..0000000
--- a/core/modules/path/path.api.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Hooks provided by the Path module.
- */
-
-/**
- * @addtogroup hooks
- * @{
- */
-
-/**
- * Respond to a path being inserted.
- *
- * @param $path
- *   An associative array containing the following keys:
- *   - source: The internal system path.
- *   - alias: The URL alias.
- *   - pid: Unique path alias identifier.
- *   - langcode: The language code of the alias.
- *
- * @see \Drupal\Core\Path\Path::save()
- */
-function hook_path_insert($path) {
-  db_insert('mytable')
-    ->fields(array(
-      'alias' => $path['alias'],
-      'pid' => $path['pid'],
-    ))
-    ->execute();
-}
-
-/**
- * Respond to a path being updated.
- *
- * @param $path
- *   An associative array containing the following keys:
- *   - source: The internal system path.
- *   - alias: The URL alias.
- *   - pid: Unique path alias identifier.
- *   - langcode: The language code of the alias.
- *
- * @see \Drupal\Core\Path\Path::save()
- */
-function hook_path_update($path) {
-  db_update('mytable')
-    ->fields(array('alias' => $path['alias']))
-    ->condition('pid', $path['pid'])
-    ->execute();
-}
-
-/**
- * Respond to a path being deleted.
- *
- * @param $path
- *   An associative array containing the following keys:
- *   - source: The internal system path.
- *   - alias: The URL alias.
- *   - pid: Unique path alias identifier.
- *   - langcode: The language code of the alias.
- *
- * @see \Drupal\Core\Path\Path::delete()
- */
-function hook_path_delete($path) {
-  db_delete('mytable')
-    ->condition('pid', $path['pid'])
-    ->execute();
-}
-
-/**
- * @} End of "addtogroup hooks".
- */
diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php b/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php
index fed0ef4..d155418 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php
@@ -31,10 +31,8 @@ function testCRUD() {
     $connection = Database::getConnection();
     $this->fixtures->createTables($connection);
 
-    //Create AliasManager and Path object.
-    $whitelist = new AliasWhitelist('path_alias_whitelist', $this->container->get('cache.cache'), $this->container->get('lock'), $this->container->get('state'), $connection);
-    $aliasManager = new AliasManager($connection, $whitelist, $this->container->get('language_manager'));
-    $path = new Path($connection, $aliasManager);
+    //Create Path object.
+    $path = new Path($connection, $this->container->get('event_dispatcher'));
 
     $aliases = $this->fixtures->sampleUrlAliases();
 
@@ -86,9 +84,8 @@ function testLookupPath() {
     $this->fixtures->createTables($connection);
 
     //Create AliasManager and Path object.
-    $whitelist = new AliasWhitelist('path_alias_whitelist', $this->container->get('cache.cache'), $this->container->get('lock'), $this->container->get('state'), $connection);
-    $aliasManager = new AliasManager($connection, $whitelist, $this->container->get('language_manager'));
-    $pathObject = new Path($connection, $aliasManager);
+    $aliasManager = $this->container->get('path.alias_manager');
+    $pathObject = new Path($connection, $this->container->get('event_dispatcher'));
 
     // Test the situation where the source is the same for multiple aliases.
     // Start with a language-neutral alias, which we will override.
@@ -163,10 +160,9 @@ function testWhitelist() {
     $memoryCounterBackend = new MemoryCounterBackend('cache');
 
     // Create AliasManager and Path object.
-
     $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $connection);
     $aliasManager = new AliasManager($connection, $whitelist, $this->container->get('language_manager'));
-    $path = new Path($connection, $aliasManager);
+    $path = new Path($connection, $this->container->get('event_dispatcher'));
 
     // No alias for user and admin yet, so should be NULL.
     $this->assertNull($whitelist->get('user'));
@@ -178,18 +174,21 @@ function testWhitelist() {
 
     // Add an alias for user/1, user should get whitelisted now.
     $path->save('user/1', $this->randomName());
+    $aliasManager->cacheClear();
     $this->assertTrue($whitelist->get('user'));
     $this->assertNull($whitelist->get('admin'));
     $this->assertNull($whitelist->get($this->randomName()));
 
     // Add an alias for admin, both should get whitelisted now.
     $path->save('admin/something', $this->randomName());
+    $aliasManager->cacheClear();
     $this->assertTrue($whitelist->get('user'));
     $this->assertTrue($whitelist->get('admin'));
     $this->assertNull($whitelist->get($this->randomName()));
 
     // Remove the user alias again, whitelist entry should be removed.
     $path->delete(array('source' => 'user/1'));
+    $aliasManager->cacheClear();
     $this->assertNull($whitelist->get('user'));
     $this->assertTrue($whitelist->get('admin'));
     $this->assertNull($whitelist->get($this->randomName()));
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MockAliasManager.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MockAliasManager.php
index 6f5add4..4523d70 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/MockAliasManager.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MockAliasManager.php
@@ -60,7 +60,7 @@ public function addAlias($path, $alias, $path_language = NULL) {
   }
 
   /**
-   * Implements \Drupal\Core\Path\AliasManagerInterface::getSystemPath().
+   * {@inheritdoc}
    */
   public function getSystemPath($path, $path_language = NULL) {
     $language = $path_language ?: $this->defaultLanguage;
@@ -68,7 +68,7 @@ public function getSystemPath($path, $path_language = NULL) {
   }
 
   /**
-   * Implements \Drupal\Core\Path\AliasManagerInterface::getPathAlias().
+   * {@inheritdoc}
    */
   public function getPathAlias($path, $path_language = NULL) {
     $language = $path_language ?: $this->defaultLanguage;
@@ -77,16 +77,23 @@ public function getPathAlias($path, $path_language = NULL) {
   }
 
   /**
-   * Implements \Drupal\Core\Path\AliasManagerInterface::getPathLookups().
+   * {@inheritdoc}
    */
   public function getPathLookups() {
     return array_keys($this->lookedUp);
   }
 
   /**
-   * Implements \Drupal\Core\Path\AliasManagerInterface::preloadPathLookups().
+   * {@inheritdoc}
    */
   public function preloadPathLookups(array $path_list) {
     // Not needed.
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cacheClear() {
+    // Not needed.
+  }
 }
diff --git a/core/modules/system/tests/modules/path_test/path_test.info.yml b/core/modules/system/tests/modules/path_test/path_test.info.yml
deleted file mode 100644
index 57a5893..0000000
--- a/core/modules/system/tests/modules/path_test/path_test.info.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: 'Hook path tests'
-type: module
-description: 'Support module for path hook testing.'
-package: Testing
-version: VERSION
-core: 8.x
-hidden: true
diff --git a/core/modules/system/tests/modules/path_test/path_test.module b/core/modules/system/tests/modules/path_test/path_test.module
deleted file mode 100644
index df03931..0000000
--- a/core/modules/system/tests/modules/path_test/path_test.module
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * @file
- * Helper module for the path tests.
- */
-
-/**
- * Resets the path test results.
- */
-function path_test_reset() {
-  \Drupal::state()->set('path_test.results', array());
-}
-
-/**
- * Implements hook_path_update().
- */
-function path_test_path_update($path) {
-  $results = \Drupal::state()->get('path_test.results') ?: array();
-  $results['hook_path_update'] = $path;
-  \Drupal::state()->set('path_test.results', $results);
-}
