diff --git a/core/core.services.yml b/core/core.services.yml
index f8c428f..ec7ce57 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -240,10 +240,10 @@ services:
     class: Drupal\Core\Path\AliasWhitelist
     tags:
       - { name: needs_destruction }
-    arguments: [path_alias_whitelist, '@cache.default', '@lock', '@state', '@path.alias_storage']
+    arguments: [path_alias_whitelist, '@cache.default', '@lock', '@state', '@entity.manager']
   path.alias_manager:
     class: Drupal\Core\Path\AliasManager
-    arguments: ['@path.alias_storage', '@path.alias_whitelist', '@language_manager', '@cache.data']
+    arguments: ['@entity.manager', '@path.alias_whitelist', '@language_manager', '@cache.data']
   http_client:
     class: Drupal\Core\Http\Client
     tags:
@@ -519,11 +519,6 @@ services:
     arguments: ['@lock', '@plugin.manager.menu.link']
     tags:
       - { name: event_subscriber }
-  path.alias_storage:
-    class: Drupal\Core\Path\AliasStorage
-    arguments: ['@database', '@module_handler']
-    tags:
-      - { name: backend_overridable }
   path.matcher:
     class: Drupal\Core\Path\PathMatcher
     arguments: ['@config.factory']
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index c1069a1..04ad2ff 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -1022,7 +1022,7 @@ protected function compileContainer() {
       $path = DRUPAL_ROOT . '/core/lib/Drupal/' . $parent_directory;
       $parent_namespace = 'Drupal\\' . $parent_directory;
       foreach (new \DirectoryIterator($path) as $component) {
-        if (!$component->isDot() && $component->isDir() && is_dir($component->getPathname() . '/Plugin')) {
+        if (!$component->isDot() && $component->isDir() && (is_dir($component->getPathname() . '/Plugin') || is_dir($component->getPathname() . '/Entity'))) {
           $namespaces[$parent_namespace . '\\' . $component->getFilename()] = $path . '/' . $component->getFilename();
         }
       }
diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index 0917e42..2d6ab9d 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -850,7 +850,7 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
         // the necessary database tables.
         $entity_manager = \Drupal::entityManager();
         foreach ($entity_manager->getDefinitions() as $entity_type) {
-          if ($entity_type->getProvider() == $module) {
+          if (in_array($entity_type->getProvider(), array($module, 'core'))) {
             $entity_manager->onEntityTypeCreate($entity_type);
           }
         }
diff --git a/core/lib/Drupal/Core/Path/AliasInterface.php b/core/lib/Drupal/Core/Path/AliasInterface.php
new file mode 100644
index 0000000..16efa7a
--- /dev/null
+++ b/core/lib/Drupal/Core/Path/AliasInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Path\AliasInterface.
+ */
+
+namespace Drupal\Core\Path;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+
+/**
+ * Provides an interface defining an alias entity.
+ */
+interface AliasInterface extends ContentEntityInterface {
+
+}
diff --git a/core/lib/Drupal/Core/Path/AliasManager.php b/core/lib/Drupal/Core/Path/AliasManager.php
index bca2eea..31a87cf 100644
--- a/core/lib/Drupal/Core/Path/AliasManager.php
+++ b/core/lib/Drupal/Core/Path/AliasManager.php
@@ -9,17 +9,18 @@
 
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\CacheDecorator\CacheDecoratorInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 
 class AliasManager implements AliasManagerInterface, CacheDecoratorInterface {
 
   /**
-   * The alias storage service.
+   * The entity manager service.
    *
-   * @var \Drupal\Core\Path\AliasStorageInterface
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
-  protected $storage;
+  protected $entityManager;
 
   /**
    * Cache backend service.
@@ -97,8 +98,8 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface {
   /**
    * Constructs an AliasManager.
    *
-   * @param \Drupal\Core\Path\AliasStorageInterface $storage
-   *   The alias storage service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
    * @param \Drupal\Core\Path\AliasWhitelistInterface $whitelist
    *   The whitelist implementation to use.
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
@@ -106,8 +107,8 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface {
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
    *   Cache backend.
    */
-  public function __construct(AliasStorageInterface $storage, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) {
-    $this->storage = $storage;
+  public function __construct(EntityManagerInterface $entity_manager, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) {
+    $this->entityManager = $entity_manager;
     $this->languageManager = $language_manager;
     $this->whitelist = $whitelist;
     $this->cache = $cache;
@@ -170,7 +171,7 @@ public function getPathByAlias($alias, $langcode = NULL) {
     }
 
     // Look for path in storage.
-    if ($path = $this->storage->lookupPathSource($alias, $langcode)) {
+    if ($path = $this->entityManager->getStorage('alias')->lookupPathSource($alias, $langcode)) {
       $this->lookupMap[$langcode][$path] = $alias;
       $this->cacheNeedsWriting = TRUE;
       return $path;
@@ -217,7 +218,7 @@ public function getAliasByPath($path, $langcode = NULL) {
 
       // Load paths from cache.
       if (!empty($this->preloadedPathLookups[$langcode])) {
-        $this->lookupMap[$langcode] = $this->storage->preloadPathAlias($this->preloadedPathLookups[$langcode], $langcode);
+        $this->lookupMap[$langcode] = $this->entityManager->getStorage('alias')->preloadPathAlias($this->preloadedPathLookups[$langcode], $langcode);
         // Keep a record of paths with no alias to avoid querying twice.
         $this->noAlias[$langcode] = array_flip(array_diff_key($this->preloadedPathLookups[$langcode], array_keys($this->lookupMap[$langcode])));
       }
@@ -234,7 +235,7 @@ public function getAliasByPath($path, $langcode = NULL) {
     }
 
     // Try to load alias from storage.
-    if ($alias = $this->storage->lookupPathAlias($path, $langcode)) {
+    if ($alias = $this->entityManager->getStorage('alias')->lookupPathAlias($path, $langcode)) {
       $this->lookupMap[$langcode][$path] = $alias;
       $this->cacheNeedsWriting = TRUE;
       return $alias;
diff --git a/core/lib/Drupal/Core/Path/AliasStorage.php b/core/lib/Drupal/Core/Path/AliasStorage.php
index b405a25..d5b3b3f 100644
--- a/core/lib/Drupal/Core/Path/AliasStorage.php
+++ b/core/lib/Drupal/Core/Path/AliasStorage.php
@@ -7,105 +7,13 @@
 
 namespace Drupal\Core\Path;
 
-use Drupal\Core\Database\Connection;
-use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
 use Drupal\Core\Language\LanguageInterface;
 
 /**
- * Provides a class for CRUD operations on path aliases.
+ * Alias storage class.
  */
-class AliasStorage implements AliasStorageInterface {
-  /**
-   * The database connection.
-   *
-   * @var \Drupal\Core\Database\Connection
-   */
-  protected $connection;
-
-  /**
-   * The module handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
-   * Constructs a Path CRUD object.
-   *
-   * @param \Drupal\Core\Database\Connection $connection
-   *   A database connection for reading and writing path aliases.
-   *
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   The module handler.
-   */
-  public function __construct(Connection $connection, ModuleHandlerInterface $module_handler) {
-    $this->connection = $connection;
-    $this->moduleHandler = $module_handler;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function save($source, $alias, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED, $pid = NULL) {
-
-    $fields = array(
-      'source' => $source,
-      'alias' => $alias,
-      'langcode' => $langcode,
-    );
-
-    // Insert or update the alias.
-    if (empty($pid)) {
-      $query = $this->connection->insert('url_alias')
-        ->fields($fields);
-      $pid = $query->execute();
-      $fields['pid'] = $pid;
-      $operation = 'insert';
-    }
-    else {
-      $fields['pid'] = $pid;
-      $query = $this->connection->update('url_alias')
-        ->fields($fields)
-        ->condition('pid', $pid);
-      $pid = $query->execute();
-      $operation = 'update';
-    }
-    if ($pid) {
-      // @todo Switch to using an event for this instead of a hook.
-      $this->moduleHandler->invokeAll('path_' . $operation, array($fields));
-      return $fields;
-    }
-    return FALSE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function load($conditions) {
-    $select = $this->connection->select('url_alias');
-    foreach ($conditions as $field => $value) {
-      $select->condition($field, $value);
-    }
-    return $select
-      ->fields('url_alias')
-      ->execute()
-      ->fetchAssoc();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function delete($conditions) {
-    $path = $this->load($conditions);
-    $query = $this->connection->delete('url_alias');
-    foreach ($conditions as $field => $value) {
-      $query->condition($field, $value);
-    }
-    $deleted = $query->execute();
-    // @todo Switch to using an event for this instead of a hook.
-    $this->moduleHandler->invokeAll('path_delete', array($path));
-    return $deleted;
-  }
+class AliasStorage extends SqlContentEntityStorage implements AliasStorageInterface {
 
   /**
    * {@inheritdoc}
@@ -126,13 +34,13 @@ public function preloadPathAlias($preloaded, $langcode) {
     if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
       // Prevent PDO from complaining about a token the query doesn't use.
       unset($args[':langcode']);
-      $result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode = :langcode_undetermined ORDER BY pid ASC', $args);
+      $result = $this->database->query('SELECT path, alias FROM {alias_field_data} WHERE path IN (:system) AND langcode = :langcode_undetermined ORDER BY aid ASC', $args);
     }
     elseif ($langcode < LanguageInterface::LANGCODE_NOT_SPECIFIED) {
-      $result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid ASC', $args);
+      $result = $this->database->query('SELECT path, alias FROM {alias_field_data} WHERE path IN (:system) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, aid ASC', $args);
     }
     else {
-      $result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid ASC', $args);
+      $result = $this->database->query('SELECT path, alias FROM {alias_field_data} WHERE path IN (:system) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, aid ASC', $args);
     }
 
     return $result->fetchAllKeyed();
@@ -150,13 +58,13 @@ public function lookupPathAlias($path, $langcode) {
     // See the queries above.
     if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
       unset($args[':langcode']);
-      $alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode = :langcode_undetermined ORDER BY pid DESC", $args)->fetchField();
+      $alias = $this->database->query("SELECT alias FROM {alias_field_data} WHERE path = :source AND langcode = :langcode_undetermined ORDER BY aid DESC", $args)->fetchField();
     }
     elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) {
-      $alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid DESC", $args)->fetchField();
+      $alias = $this->database->query("SELECT alias FROM {alias_field_data} WHERE path = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, aid DESC", $args)->fetchField();
     }
     else {
-      $alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid DESC", $args)->fetchField();
+      $alias = $this->database->query("SELECT alias FROM {alias_field_data} WHERE path = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, aid DESC", $args)->fetchField();
     }
 
     return $alias;
@@ -174,13 +82,13 @@ public function lookupPathSource($path, $langcode) {
     // See the queries above.
     if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
       unset($args[':langcode']);
-      $result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode = :langcode_undetermined ORDER BY pid DESC", $args);
+      $result = $this->database->query("SELECT path FROM {alias_field_data} WHERE alias = :alias AND langcode = :langcode_undetermined ORDER BY aid DESC", $args);
     }
     elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) {
-      $result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid DESC", $args);
+      $result = $this->database->query("SELECT path FROM {alias_field_data} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, aid DESC", $args);
     }
     else {
-      $result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid DESC", $args);
+      $result = $this->database->query("SELECT path FROM {alias_field_data} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, aid DESC", $args);
     }
 
     return $result->fetchField();
@@ -199,11 +107,11 @@ public function lookupPathSource($path, $langcode) {
    *   TRUE if alias already exists and FALSE otherwise.
    */
   public function aliasExists($alias, $langcode, $source = NULL) {
-    $query = $this->connection->select('url_alias')
+    $query = $this->database->select('alias_field_data')
       ->condition('alias', $alias)
       ->condition('langcode', $langcode);
     if (!empty($source)) {
-      $query->condition('source', $source, '<>');
+      $query->condition('path', $source, '<>');
     }
     $query->addExpression('1');
     $query->range(0, 1);
@@ -217,7 +125,7 @@ public function aliasExists($alias, $langcode, $source = NULL) {
    *   TRUE if aliases with language exist.
    */
   public function languageAliasExists() {
-    return (bool) $this->connection->queryRange('SELECT 1 FROM {url_alias} WHERE langcode <> :langcode', 0, 1, array(':langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED))->fetchField();
+    return (bool) $this->database->queryRange('SELECT 1 FROM {alias_field_data} WHERE langcode <> :langcode', 0, 1, array(':langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED))->fetchField();
   }
 
   /**
@@ -231,7 +139,7 @@ public function languageAliasExists() {
    *   Array of items to be displayed on the current page.
    */
   public function getAliasesForAdminListing($header, $keys = NULL) {
-    $query = $this->connection->select('url_alias')
+    $query = $this->database->select('alias_field_data')
       ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
       ->extend('Drupal\Core\Database\Query\TableSortExtender');
     if ($keys) {
@@ -239,7 +147,7 @@ public function getAliasesForAdminListing($header, $keys = NULL) {
       $query->condition('alias', '%' . preg_replace('!\*+!', '%', $keys) . '%', 'LIKE');
     }
     return $query
-      ->fields('url_alias')
+      ->fields('alias_field_data')
       ->orderByHeader($header)
       ->limit(50)
       ->execute()
@@ -256,10 +164,10 @@ public function getAliasesForAdminListing($header, $keys = NULL) {
    *   TRUE if any alias exists, FALSE otherwise.
    */
   public function pathHasMatchingAlias($initial_substring) {
-    $query = $this->connection->select('url_alias', 'u');
+    $query = $this->database->select('alias_field_data', 'u');
     $query->addExpression(1);
     return (bool) $query
-      ->condition('u.source', $this->connection->escapeLike($initial_substring) . '%', 'LIKE')
+      ->condition('u.path', $this->database->escapeLike($initial_substring) . '%', 'LIKE')
       ->range(0, 1)
       ->execute()
       ->fetchField();
diff --git a/core/lib/Drupal/Core/Path/AliasStorageInterface.php b/core/lib/Drupal/Core/Path/AliasStorageInterface.php
index a98600f..65ce950 100644
--- a/core/lib/Drupal/Core/Path/AliasStorageInterface.php
+++ b/core/lib/Drupal/Core/Path/AliasStorageInterface.php
@@ -7,58 +7,13 @@
 
 namespace Drupal\Core\Path;
 
-use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+
 
 /**
- * Provides a class for CRUD operations on path aliases.
+ * Alias storage interface.
  */
-interface AliasStorageInterface {
-
-  /**
-   * Saves a path alias to the database.
-   *
-   * @param string $source
-   *   The internal system path.
-   * @param string $alias
-   *   The URL alias.
-   * @param string $langcode
-   *   The language code of the alias.
-   * @param int|null $pid
-   *   Unique path alias identifier.
-   *
-   * @return mixed[]|bool
-   *   FALSE if the path could not be saved or an associative array containing
-   *   the following keys:
-   *   - source (string): The internal system path.
-   *   - alias (string): The URL alias.
-   *   - pid (int): Unique path alias identifier.
-   *   - langcode (string): The language code of the alias.
-   */
-  public function save($source, $alias, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED, $pid = NULL);
-
-  /**
-   * Fetches a specific URL alias from the database.
-   *
-   * @param $conditions
-   *   An array of query conditions.
-   *
-   * @return mixed[]|bool
-   *   FALSE if no alias was found or an associative array containing the
-   *   following keys:
-   *   - source (string): The internal system path.
-   *   - alias (string): The URL alias.
-   *   - pid (int): Unique path alias identifier.
-   *   - langcode (string): The language code of the alias.
-   */
-  public function load($conditions);
-
-  /**
-   * Deletes a URL alias.
-   *
-   * @param array $conditions
-   *   An array of criteria.
-   */
-  public function delete($conditions);
+interface AliasStorageInterface extends EntityStorageInterface{
 
   /**
    * Pre-loads path alias information for a given list of source paths.
diff --git a/core/lib/Drupal/Core/Path/AliasStorageSchema.php b/core/lib/Drupal/Core/Path/AliasStorageSchema.php
new file mode 100644
index 0000000..8974c39
--- /dev/null
+++ b/core/lib/Drupal/Core/Path/AliasStorageSchema.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Path\AliasStorageSchema.
+ */
+
+namespace Drupal\Core\Path;
+
+use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
+
+/**
+ * Defines the alias schema handler.
+ */
+class AliasStorageSchema extends SqlContentEntityStorageSchema {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) {
+    $schema = parent::getEntitySchema($entity_type, $reset);
+
+    $schema['alias_field_data']['indexes'] += array(
+      'alias_langcode_pid' => array('alias', 'langcode', 'aid'),
+      'path_langcode_pid' => array('path', 'langcode', 'aid'),
+    );
+
+    return $schema;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Path/AliasWhitelist.php b/core/lib/Drupal/Core/Path/AliasWhitelist.php
index 394dfa2..0754fdb 100644
--- a/core/lib/Drupal/Core/Path/AliasWhitelist.php
+++ b/core/lib/Drupal/Core/Path/AliasWhitelist.php
@@ -9,7 +9,7 @@
 
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Cache\CacheCollector;
-use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 
@@ -26,11 +26,11 @@ class AliasWhitelist extends CacheCollector implements AliasWhitelistInterface {
   protected $state;
 
   /**
-   * The Path CRUD service.
+   * The entity manager service.
    *
-   * @var \Drupal\Core\Path\AliasStorageInterface
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
-  protected $aliasStorage;
+  protected $entityManager;
 
   /**
    * Constructs an AliasWhitelist object.
@@ -43,13 +43,13 @@ class AliasWhitelist extends CacheCollector implements AliasWhitelistInterface {
    *   The lock backend.
    * @param \Drupal\Core\State\StateInterface $state
    *   The state keyvalue store.
-   * @param \Drupal\Core\Path\AliasStorageInterface $alias_storage
-   *   The alias storage service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
    */
-  public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, StateInterface $state, AliasStorageInterface $alias_storage) {
+  public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, StateInterface $state, EntityManagerInterface $entity_manager) {
     parent::__construct($cid, $cache, $lock);
     $this->state = $state;
-    $this->aliasStorage = $alias_storage;
+    $this->entityManager = $entity_manager;
   }
 
   /**
@@ -107,7 +107,7 @@ public function get($offset) {
    * {@inheritdoc}
    */
   public function resolveCacheMiss($root) {
-    $exists = $this->aliasStorage->pathHasMatchingAlias($root);
+    $exists = $this->entityManager->getStorage('alias')->pathHasMatchingAlias($root);
     $this->storage[$root] = $exists;
     $this->persist($root);
     if ($exists) {
diff --git a/core/lib/Drupal/Core/Path/Entity/Alias.php b/core/lib/Drupal/Core/Path/Entity/Alias.php
new file mode 100644
index 0000000..15b4618
--- /dev/null
+++ b/core/lib/Drupal/Core/Path/Entity/Alias.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Path\Entity\Alias.
+ */
+
+namespace Drupal\Core\Path\Entity;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Path\AliasInterface;
+
+/**
+ * Defines the alias entityclass.
+ * // TODO: Should this live in Path.module (permission, routes)?
+ *
+ * @ContentEntityType(
+ *   id = "alias",
+ *   label = @Translation("Path alias"),
+ *   handlers = {
+ *     "storage" = "Drupal\Core\Path\AliasStorage",
+ *     "storage_schema" = "Drupal\Core\Path\AliasStorageSchema",
+ *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
+ *     "access" = "Drupal\Core\Entity\EntityAccessControlHandler",
+ *     "form" = {
+ *       "default" = "Drupal\media_entity\MediaForm",
+ *       "delete" = "Drupal\media_entity\Form\MediaDeleteForm",
+ *       "edit" = "Drupal\media_entity\MediaForm"
+ *     },
+ *     "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ *   },
+ *   base_table = "alias",
+ *   data_table = "alias_field_data",
+ *   fieldable = TRUE,
+ *   translatable = TRUE,
+ *   render_cache = FALSE,
+ *   entity_keys = {
+ *     "id" = "aid",
+ *     "label" = "alias",
+ *     "uuid" = "uuid"
+ *   },
+ *   permission_granularity = "entity_type",
+ *   admin_permission = "administer url aliases",
+ *   links = {
+ *     "canonical" = "alias.view",
+ *     "delete-form" = "alias.delete_confirm",
+ *     "edit-form" = "alias.edit",
+ *   }
+ * )
+ */
+class Alias extends ContentEntityBase implements AliasInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields['aid'] = BaseFieldDefinition::create('integer')
+      ->setLabel(t('Alias ID'))
+      ->setDescription(t('The alias ID.'))
+      ->setReadOnly(TRUE)
+      ->setSetting('unsigned', TRUE);
+
+    $fields['uuid'] = BaseFieldDefinition::create('uuid')
+      ->setLabel(t('UUID'))
+      ->setDescription(t('The alias UUID.'))
+      ->setReadOnly(TRUE);
+
+    $fields['path'] = BaseFieldDefinition::create('string')
+      ->setLabel(t('Path'))
+      ->setDescription(t('The path that this alias belongs to.'))
+      ->setRequired(TRUE)
+      ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE)
+      ->setDefaultValue('')
+      ->setSetting('max_length', 255);
+
+    $fields['alias'] = BaseFieldDefinition::create('string')
+      ->setLabel(t('Alias'))
+      ->setDescription(t('The alias used with this path.'))
+      ->setRequired(TRUE)
+      ->setTranslatable(TRUE)
+      ->setRevisionable(TRUE)
+      ->setDefaultValue('')
+      ->setSetting('max_length', 255);
+
+    $fields['langcode'] = BaseFieldDefinition::create('language')
+      ->setLabel(t('Language code'))
+      ->setDescription(t('The alias language code.'))
+      ->setRevisionable(TRUE);
+
+    return $fields;
+  }
+
+}
diff --git a/core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php b/core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php
index 1b723ce..1ea674f 100644
--- a/core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php
+++ b/core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php
@@ -79,8 +79,13 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     // @todo This should be taken care of by the Path module.
     if (\Drupal::moduleHandler()->moduleExists('path')) {
       $path = $this->entity->getSystemPath();
-      $conditions = array('source' => $path, 'langcode' => $this->language->id);
-      \Drupal::service('path.alias_storage')->delete($conditions);
+
+      $aid = reset(\Drupal::entityQuery('alias')
+        ->condition('path', $path)
+        ->condition('langcode', $this->language->id)
+        ->execute());
+
+      \Drupal::entityManager()->getStorage('alias')->delete(array($aid));
     }
 
     $form_state->setRedirectUrl($this->getCancelUrl());
diff --git a/core/modules/path/src/Controller/PathController.php b/core/modules/path/src/Controller/PathController.php
index 6144b67..4acfb41 100644
--- a/core/modules/path/src/Controller/PathController.php
+++ b/core/modules/path/src/Controller/PathController.php
@@ -18,13 +18,6 @@
 class PathController extends ControllerBase {
 
   /**
-   * The path alias storage.
-   *
-   * @var \Drupal\Core\Path\AliasStorageInterface
-   */
-  protected $aliasStorage;
-
-  /**
    * The path alias manager.
    *
    * @var \Drupal\Core\Path\AliasManagerInterface
@@ -39,8 +32,7 @@ class PathController extends ControllerBase {
    * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
    *   The path alias manager.
    */
-  public function __construct(AliasStorageInterface $alias_storage, AliasManagerInterface $alias_manager) {
-    $this->aliasStorage = $alias_storage;
+  public function __construct(AliasManagerInterface $alias_manager) {
     $this->aliasManager = $alias_manager;
   }
 
@@ -49,7 +41,6 @@ public function __construct(AliasStorageInterface $alias_storage, AliasManagerIn
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('path.alias_storage'),
       $container->get('path.alias_manager')
     );
   }
@@ -59,7 +50,7 @@ public function adminOverview($keys) {
     $build['path_admin_filter_form'] = $this->formBuilder()->getForm('Drupal\path\Form\PathFilterForm', $keys);
     // Enable language column if language.module is enabled or if we have any
     // alias with a language.
-    $multilanguage = ($this->moduleHandler()->moduleExists('language') || $this->aliasStorage->languageAliasExists());
+    $multilanguage = ($this->moduleHandler()->moduleExists('language') || $this->entityManager()->getStorage('alias')->languageAliasExists());
 
     $header = array();
     $header[] = array('data' => $this->t('Alias'), 'field' => 'alias', 'sort' => 'asc');
@@ -71,7 +62,7 @@ public function adminOverview($keys) {
 
     $rows = array();
     $destination = drupal_get_destination();
-    foreach ($this->aliasStorage->getAliasesForAdminListing($header, $keys) as $data) {
+    foreach ($this->entityManager()->getStorage('alias')->getAliasesForAdminListing($header, $keys) as $data) {
       $row = array();
       $row['data']['alias'] = l(truncate_utf8($data->alias, 50, FALSE, TRUE), $data->source, array(
         'attributes' => array('title' => $data->alias),
diff --git a/core/modules/path/src/Form/DeleteForm.php b/core/modules/path/src/Form/DeleteForm.php
index 80251dc..eda690e 100644
--- a/core/modules/path/src/Form/DeleteForm.php
+++ b/core/modules/path/src/Form/DeleteForm.php
@@ -7,9 +7,9 @@
 
 namespace Drupal\path\Form;
 
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Path\AliasStorageInterface;
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -19,11 +19,11 @@
 class DeleteForm extends ConfirmFormBase {
 
   /**
-   * The alias storage service.
+   * The entity manager service.
    *
-   * @var AliasStorageInterface $path
+   * @var \Drupal\Core\Entity\EntityManagerInterface $entityManager
    */
-  protected $aliasStorage;
+  protected $entityManager;
 
   /**
    * The path alias being deleted.
@@ -35,11 +35,11 @@ class DeleteForm extends ConfirmFormBase {
   /**
    * Constructs a \Drupal\path\Form\DeleteForm object.
    *
-   * @param \Drupal\Core\Path\AliasStorageInterface $alias_storage
-   *   The alias storage service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
    */
-  public function __construct(AliasStorageInterface $alias_storage) {
-    $this->aliasStorage = $alias_storage;
+  public function __construct(EntityManagerInterface $entity_manager) {
+    $this->entityManager = $entity_manager;
   }
 
   /**
@@ -47,7 +47,7 @@ public function __construct(AliasStorageInterface $alias_storage) {
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('path.alias_storage')
+      $container->get('entity.manager')
     );
   }
 
@@ -75,8 +75,8 @@ public function getCancelUrl() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, $pid = NULL) {
-    $this->pathAlias = $this->aliasStorage->load(array('pid' => $pid));
+  public function buildForm(array $form, FormStateInterface $form_state, $aid = NULL) {
+    $this->pathAlias = $this->entityManager->getStorage('alias')->load($aid);
 
     $form = parent::buildForm($form, $form_state);
 
@@ -87,7 +87,8 @@ public function buildForm(array $form, FormStateInterface $form_state, $pid = NU
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->aliasStorage->delete(array('pid' => $this->pathAlias['pid']));
+
+    $this->entityManager->getStorage('alias')->delete(array($this->pathAlias['aid']));
 
     $form_state->setRedirect('path.admin_overview');
   }
diff --git a/core/modules/path/src/Form/PathFormBase.php b/core/modules/path/src/Form/PathFormBase.php
index 62df767..96541ca 100644
--- a/core/modules/path/src/Form/PathFormBase.php
+++ b/core/modules/path/src/Form/PathFormBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\path\Form;
 
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
@@ -27,11 +28,11 @@
   protected $path;
 
   /**
-   * The path alias storage.
+   * The entity manager service.
    *
-   * @var \Drupal\Core\Path\AliasStorageInterface
+   * @var \Drupal\Core\Entity\EntityManagerInterface $entityManager
    */
-  protected $aliasStorage;
+  protected $entityManager;
 
   /**
    * The path alias manager.
@@ -43,13 +44,13 @@
   /**
    * Constructs a new PathController.
    *
-   * @param \Drupal\Core\Path\AliasStorageInterface $alias_storage
-   *   The path alias storage.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
    * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
    *   The path alias manager.
    */
-  public function __construct(AliasStorageInterface $alias_storage, AliasManagerInterface $alias_manager) {
-    $this->aliasStorage = $alias_storage;
+  public function __construct(EntityManagerInterface $entity_manager, AliasManagerInterface $alias_manager) {
+    $this->entityManager = $entity_manager;
     $this->aliasManager = $alias_manager;
   }
 
@@ -58,7 +59,7 @@ public function __construct(AliasStorageInterface $alias_storage, AliasManagerIn
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('path.alias_storage'),
+      $container->get('entity.manager'),
       $container->get('path.alias_manager')
     );
   }
@@ -143,7 +144,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
     // languages.
     $langcode = $form_state->getValue('langcode', LanguageInterface::LANGCODE_NOT_SPECIFIED);
 
-    if ($this->aliasStorage->aliasExists($alias, $langcode, $source)) {
+    if ($this->entityManager->getStorage('alias')->aliasExists($alias, $langcode, $source)) {
       $form_state->setErrorByName('alias', t('The alias %alias is already in use in this language.', array('%alias' => $alias)));
     }
     if (!drupal_valid_path($source)) {
@@ -158,7 +159,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     // Remove unnecessary values.
     form_state_values_clean($form_state);
 
-    $pid = $form_state->getValue('pid', 0);
+    $aid = $form_state->getValue('aid', 0);
     $source = &$form_state->getValue('source');
     $source = $this->aliasManager->getPathByAlias($source);
     $alias = $form_state->getValue('alias');
@@ -166,7 +167,23 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     // languages.
     $langcode = $form_state->getValue('langcode', LanguageInterface::LANGCODE_NOT_SPECIFIED);
 
-    $this->aliasStorage->save($source, $alias, $langcode, $pid);
+    if (empty($aid)) {
+      $alias = $this->entityManager->getStorage('alias')->create(
+        array(
+          'alias' => $alias,
+          'path' => $source,
+          'langcode' => $langcode,
+        )
+      );
+      $alias->save();
+    }
+    else {
+      $alias = $this->entityManager->getStorage('alias')->load($aid);
+      $alias->path = $source;
+      $alias->alias = $alias;
+      $alias->langcode = $langcode;
+      $alias->save();
+    }
 
     drupal_set_message($this->t('The alias has been saved.'));
     $form_state->setRedirect('path.admin_overview');
diff --git a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
index cff3fa6..1eab340 100644
--- a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
+++ b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
@@ -33,8 +33,8 @@ class PathItem extends FieldItemBase {
   public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
     $properties['alias'] = DataDefinition::create('string')
       ->setLabel(t('Path alias'));
-    $properties['pid'] = DataDefinition::create('string')
-      ->setLabel(t('Path id'));
+    $properties['aid'] = DataDefinition::create('string')
+      ->setLabel(t('Alias ID'));
     return $properties;
   }
 
@@ -59,8 +59,16 @@ public function insert() {
     if ($this->alias) {
       $entity = $this->getEntity();
 
-      if ($path = \Drupal::service('path.alias_storage')->save($entity->getSystemPath(), $this->alias, $this->getLangcode())) {
-        $this->pid = $path['pid'];
+      $alias = \Drupal::entityManager()->getStorage('alias')->create(
+        array(
+          'path' => $entity->getSystemPath(),
+          'alias' => $this->alias,
+          'langcode' => $this->getLangcode(),
+        )
+      );
+
+      if ($alias->save()) {
+        $this->aid = $alias->id();
       }
     }
   }
@@ -70,14 +78,17 @@ public function insert() {
    */
   public function update() {
     // Delete old alias if user erased it.
-    if ($this->pid && !$this->alias) {
-      \Drupal::service('path.alias_storage')->delete(array('pid' => $this->pid));
+    if ($this->aid && !$this->alias) {
+      \Drupal::entityManager()->getStorage('alias')->delete(array($this->aid));
     }
     // Only save a non-empty alias.
     elseif ($this->alias) {
       $entity = $this->getEntity();
-
-      \Drupal::service('path.alias_storage')->save($entity->getSystemPath(), $this->alias, $this->getLangcode(), $this->pid);
+      $alias = \Drupal::entityManager()->getStorage('alias')->load($this->aid);
+      $alias->path = $entity->getSystemPath();
+      $alias->alias = $this->alias;
+      $alias->langcode = $this->getLangcode();
+      $alias->save();
     }
   }
 
@@ -86,8 +97,7 @@ public function update() {
    */
   public function delete() {
     // Delete all aliases associated with this entity.
-    $entity = $this->getEntity();
-    \Drupal::service('path.alias_storage')->delete(array('source' => $entity->getSystemPath()));
+    \Drupal::entityManager()->getStorage('alias')->delete(array($this->aid));
   }
 
   /**
diff --git a/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php b/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php
index 513aee4..0971839 100644
--- a/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php
+++ b/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php
@@ -31,23 +31,16 @@ class PathWidget extends WidgetBase {
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
     $entity = $items->getEntity();
-    $path = array();
+    $alias = NULL;
     if (!$entity->isNew()) {
-      $conditions = array('source' => $entity->getSystemPath());
+      $query = \Drupal::entityQuery('alias')
+        ->condition('path', $entity->getSystemPath());
       if ($items->getLangcode() != LanguageInterface::LANGCODE_NOT_SPECIFIED) {
-        $conditions['langcode'] = $items->getLangcode();
-      }
-      $path = \Drupal::service('path.alias_storage')->load($conditions);
-      if ($path === FALSE) {
-        $path = array();
+        $query->condition('langcode', $items->getLangcode());
       }
+      $alias = reset($query->execute());
+      $alias = \Drupal::entityManager()->getStorage('alias')->load($alias);
     }
-    $path += array(
-      'pid' => NULL,
-      'source' => !$entity->isNew() ? $entity->getSystemPath() : NULL,
-      'alias' => '',
-      'langcode' => $items->getLangcode(),
-    );
 
     $element += array(
       '#element_validate' => array(array(get_class($this), 'validateFormElement')),
@@ -55,22 +48,22 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     $element['alias'] = array(
       '#type' => 'textfield',
       '#title' => $element['#title'],
-      '#default_value' => $path['alias'],
+      '#default_value' => empty($alias) ? '' : $alias->alias,
       '#required' => $element['#required'],
       '#maxlength' => 255,
       '#description' => $this->t('The alternative URL for this content. Use a relative path without a trailing slash. For example, enter "about" for the about page.'),
     );
-    $element['pid'] = array(
+    $element['aid'] = array(
       '#type' => 'value',
-      '#value' => $path['pid'],
+      '#value' => empty($alias) ? NULL : $alias->id(),
     );
-    $element['source'] = array(
+    $element['path'] = array(
       '#type' => 'value',
-      '#value' => $path['source'],
+      '#value' => empty($alias) ? (!$entity->isNew() ? $entity->getSystemPath() : NULL) : $alias->path,
     );
     $element['langcode'] = array(
       '#type' => 'value',
-      '#value' => $path['langcode'],
+      '#value' => empty($alias) ? $items->getLangcode() : $alias->langcode,
     );
     return $element;
   }
@@ -90,7 +83,7 @@ public static function validateFormElement(array &$element, FormStateInterface $
       $form_state->setValueForElement($element['alias'], $alias);
 
       // Validate that the submitted alias does not exist yet.
-      $is_exists = \Drupal::service('path.alias_storage')->aliasExists($alias, $element['langcode']['#value'], $element['source']['#value']);
+      $is_exists = \Drupal::entityManager()->getStorage('alias')->aliasExists($alias, $element['langcode']['#value'], $element['source']['#value']);
       if ($is_exists) {
         $form_state->setError($element, t('The alias is already in use.'));
       }
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 8e7b534..e5b97c4 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -926,43 +926,5 @@ function system_schema() {
     ),
   );
 
-  $schema['url_alias'] = array(
-    'description' => 'A list of URL aliases for Drupal paths; a user may visit either the source or destination path.',
-    'fields' => array(
-      'pid' => array(
-        'description' => 'A unique path alias identifier.',
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-      ),
-      'source' => array(
-        'description' => 'The Drupal path this alias is for; e.g. node/12.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'alias' => array(
-        'description' => 'The alias for this path; e.g. title-of-the-story.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'langcode' => array(
-        'description' => "The language code this alias is for; if 'und', the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.",
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'primary key' => array('pid'),
-    'indexes' => array(
-      'alias_langcode_pid' => array('alias', 'langcode', 'pid'),
-      'source_langcode_pid' => array('source', 'langcode', 'pid'),
-    ),
-  );
-
   return $schema;
 }
