diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index 92782406ca04142b6270c18fffd5cf2d87b1ab57..160b526ebd527f4d9efa5eb5082a3f39a9760785 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -780,11 +780,24 @@ text_format:
       # The text format should not be translated as part of the string
       # translation system, so this is not marked as translatable.
 
-# Schema for the configuration of the Entity reference selection plugins.
-
+# Base schema for all entity reference selection handler schemas.
 entity_reference_selection:
   type: mapping
-  label: 'Entity reference selection plugin configuration'
+  label: 'Entity reference selection handler settings'
+  mapping:
+    target_type:
+      type: string
+      label: 'Type of item to reference'
+
+# Schema for all entity reference selection handlers that are not providing a
+# specific schema.
+entity_reference_selection.*:
+  type: entity_reference_selection
+
+# Schema for the entity reference 'default' selection handler settings.
+entity_reference_selection.default:
+  type: entity_reference_selection
+  label: 'Default selection handler settings'
   mapping:
     target_bundles:
       type: sequence
@@ -792,7 +805,7 @@ entity_reference_selection:
       nullable: true
       sequence:
         type: string
-        label: 'Type'
+        label: 'Bundle'
     sort:
       type: mapping
       label: 'Sort settings'
@@ -810,5 +823,7 @@ entity_reference_selection:
       type: string
       label: 'Bundle assigned to the auto-created entities.'
 
-entity_reference_selection.*:
-  type: entity_reference_selection
+# Schema for all entity reference 'default:*' selection handlers that are not
+# providing a specific schema.
+entity_reference_selection.default:*:
+  type: entity_reference_selection.default
diff --git a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
index 89f99a049f5403dd4496ca04a985c700f4fd19d6..466032d96a01c9ede7b2e800a49ba8c5173f0f18 100644
--- a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -152,10 +152,9 @@ public static function validateEntityAutocomplete(array &$element, FormStateInte
     $value = NULL;
 
     if (!empty($element['#value'])) {
-      $options = [
+      $options = $element['#selection_settings'] + [
         'target_type' => $element['#target_type'],
         'handler' => $element['#selection_handler'],
-        'handler_settings' => $element['#selection_settings'],
       ];
       /** @var /Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler */
       $handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options);
diff --git a/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php b/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php
index 55372f9e7185044d3e7469e17c1f5b9ae250bf40..2cede4dffaa5717820a35b56c5142108c2f0666d 100644
--- a/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php
+++ b/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php
@@ -52,10 +52,9 @@ public function __construct(SelectionPluginManagerInterface $selection_manager)
   public function getMatches($target_type, $selection_handler, $selection_settings, $string = '') {
     $matches = [];
 
-    $options = [
+    $options = $selection_settings + [
       'target_type' => $target_type,
       'handler' => $selection_handler,
-      'handler_settings' => $selection_settings,
     ];
     $handler = $this->selectionManager->getInstance($options);
 
diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..b62dc3ce2d89d41e682da12a1a0f3fa4873f7fa5
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginBase.php
@@ -0,0 +1,161 @@
+<?php
+
+namespace Drupal\Core\Entity\EntityReferenceSelection;
+
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\PluginBase;
+
+/**
+ * Provides a base class for configurable selection handlers.
+ */
+abstract class SelectionPluginBase extends PluginBase implements SelectionInterface, ConfigurablePluginInterface {
+
+  /**
+   * Constructs a new selection object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->setConfiguration($configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'target_type' => NULL,
+      // @todo Remove this key in Drupal 9.0.x.
+      'handler' => $this->getPluginId(),
+      'entity' => NULL,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+    // Resolve backward compatibility level configurations, if any.
+    $this->resolveBackwardCompatibilityConfiguration($configuration);
+
+    // Merge in defaults.
+    $this->configuration = NestedArray::mergeDeep(
+      $this->defaultConfiguration(),
+      $configuration
+    );
+
+    // Ensure a backward compatibility level configuration.
+    $this->ensureBackwardCompatibilityConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function entityQueryAlter(SelectInterface $query) { }
+
+  /**
+   * Moves the backward compatibility level configurations in the right place.
+   *
+   * In order to keep backward compatibility, we copy all settings, except
+   * 'target_type', 'handler' and 'entity' under 'handler_settings', following
+   * the structure from the field config. If the plugin was instantiated using
+   * the 'handler_settings' level, those values will be used. In case of
+   * conflict, the root level settings will take precedence. The backward
+   * compatibility aware configuration will have the next structure:
+   * - target_type
+   * - handler (will be removed in Drupal 9.0.x, it's the plugin id)
+   * - entity
+   * - setting_1
+   * - setting_2
+   *   ...
+   * - setting_N
+   * - handler_settings: (will be removed in Drupal 9.0.x)
+   *   - setting_1
+   *   - setting_2
+   *     ...
+   *   - setting_N
+   *
+   * @param array $configuration
+   *   The configuration array to be altered.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.x.
+   *
+   * @see https://www.drupal.org/node/2870971
+   */
+  protected function resolveBackwardCompatibilityConfiguration(array &$configuration) {
+    if (isset($this->defaultConfiguration()['handler_settings'])) {
+      throw new \InvalidArgumentException("{$this->getPluginDefinition()['class']}::defaultConfiguration() should not contain a 'handler_settings' key. All settings should be placed in the root level.");
+    }
+
+    // Extract the BC level from the passed configuration, if any.
+    if (array_key_exists('handler_settings', $configuration)) {
+      if (!is_array($configuration['handler_settings'])) {
+        throw new \InvalidArgumentException("The setting 'handler_settings' is reserved and cannot be used.");
+      }
+      @trigger_error("Providing settings under 'handler_settings' is deprecated and will be removed before 9.0.0. Move the settings in the root of the configuration array. See https://www.drupal.org/node/2870971.", E_USER_DEPRECATED);
+
+      // Settings passed in the root level take precedence over BC settings.
+      $configuration += $configuration['handler_settings'];
+      unset($configuration['handler_settings']);
+    }
+  }
+
+  /**
+   * Ensures a backward compatibility level configuration.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.x.
+   *
+   * @see https://www.drupal.org/node/2870971
+   */
+  protected function ensureBackwardCompatibilityConfiguration() {
+    $keys = ['handler', 'target_type', 'entity', 'handler_settings'];
+    // Synchronize back 'handler_settings'.
+    foreach ($this->configuration as $key => $value) {
+      // Filter out keys that belong strictly to the root level.
+      if (!in_array($key, $keys, TRUE)) {
+        $this->configuration['handler_settings'][$key] = $value;
+      }
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php
index 53b43fdc68926f8d98b96fe8f3e0040f569e08d8..cf9bb7e49de8963dd4ff9b114cb67e02fc25adfe 100644
--- a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionPluginManager.php
@@ -39,7 +39,6 @@ public function getInstance(array $options) {
     // Initialize default options.
     $options += [
       'handler' => $this->getPluginId($options['target_type'], 'default'),
-      'handler_settings' => [],
     ];
 
     // A specific selection plugin ID was already specified.
@@ -50,6 +49,7 @@ public function getInstance(array $options) {
     else {
       $plugin_id = $this->getPluginId($options['target_type'], $options['handler']);
     }
+    unset($options['handler']);
 
     return $this->createInstance($plugin_id, $options);
   }
@@ -92,10 +92,10 @@ public function getSelectionGroups($entity_type_id) {
    * {@inheritdoc}
    */
   public function getSelectionHandler(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
-    $options = [
+    $options = $field_definition->getSetting('handler_settings') ?: [];
+    $options += [
       'target_type' => $field_definition->getFieldStorageDefinition()->getSetting('target_type'),
       'handler' => $field_definition->getSetting('handler'),
-      'handler_settings' => $field_definition->getSetting('handler_settings') ?: [],
       'entity' => $entity,
     ];
     return $this->getInstance($options);
diff --git a/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionTrait.php b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..1d7947df21d2ae490581d842e31829b9406b2512
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityReferenceSelection/SelectionTrait.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Drupal\Core\Entity\EntityReferenceSelection;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides common methods and injects services for core selection handlers.
+ */
+trait SelectionTrait {
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The module handler service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Constructs a new selection object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler service.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->entityManager = $entity_manager;
+    $this->moduleHandler = $module_handler;
+    $this->currentUser = $current_user;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity.manager'),
+      $container->get('module_handler'),
+      $container->get('current_user')
+    );
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/Broken.php b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/Broken.php
index f53eeb618902a091e556e6521faca9ecb707ce18..e8251e631f74a77425e0970d6f1eb2e8ad3c1597 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/Broken.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/Broken.php
@@ -2,8 +2,7 @@
 
 namespace Drupal\Core\Entity\Plugin\EntityReferenceSelection;
 
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase;
 use Drupal\Core\Form\FormStateInterface;
 
 /**
@@ -14,28 +13,19 @@
  *   label = @Translation("Broken/Missing")
  * )
  */
-class Broken implements SelectionInterface {
+class Broken extends SelectionPluginBase {
 
   /**
    * {@inheritdoc}
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
     $form['selection_handler'] = [
       '#markup' => t('The selected selection handler is broken.'),
     ];
     return $form;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }
-
   /**
    * {@inheritdoc}
    */
@@ -57,9 +47,4 @@ public function validateReferenceableEntities(array $ids) {
     return [];
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function entityQueryAlter(SelectInterface $query) { }
-
 }
diff --git a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php
index e3b41b1bbe36c6978e631a7d19b6b5c8d4462dee..218131c2cdf176fb2a93fc6b658dcde75cfb1b8b 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php
@@ -4,19 +4,17 @@
 
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Database\Query\AlterableInterface;
-use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionTrait;
 use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Plugin\PluginBase;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\user\EntityOwnerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Default plugin implementation of the Entity Reference Selection plugin.
@@ -38,88 +36,51 @@
  *   deriver = "Drupal\Core\Entity\Plugin\Derivative\DefaultSelectionDeriver"
  * )
  */
-class DefaultSelection extends PluginBase implements SelectionInterface, SelectionWithAutocreateInterface, ContainerFactoryPluginInterface {
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * The module handler service.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
-   * The current user.
-   *
-   * @var \Drupal\Core\Session\AccountInterface
-   */
-  protected $currentUser;
-
-  /**
-   * Constructs a new SelectionBase object.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager service.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   The module handler service.
-   * @param \Drupal\Core\Session\AccountInterface $current_user
-   *   The current user.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-
-    $this->entityManager = $entity_manager;
-    $this->moduleHandler = $module_handler;
-    $this->currentUser = $current_user;
+class DefaultSelection extends SelectionPluginBase implements ContainerFactoryPluginInterface, SelectionWithAutocreateInterface {
+
+  use SelectionTrait {
+    // PHP 5.5.9 gets confused between SelectionPluginBase::__construct() and
+    // SelectionTrait::__construct() that's why we are renaming the
+    // SelectionTrait::__construct() to avoid the confusion.
+    // @todo Remove this in https://www.drupal.org/node/2670966.
+    SelectionTrait::__construct as private initialize;
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('entity.manager'),
-      $container->get('module_handler'),
-      $container->get('current_user')
-    );
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
+    $this->initialize($configuration, $plugin_id, $plugin_definition, $entity_manager, $module_handler, $current_user);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
-    $entity_type_id = $this->configuration['target_type'];
-    $selection_handler_settings = $this->configuration['handler_settings'];
-    $entity_type = $this->entityManager->getDefinition($entity_type_id);
-    $bundles = $this->entityManager->getBundleInfo($entity_type_id);
-
-    // Merge-in default values.
-    $selection_handler_settings += [
+  public function defaultConfiguration() {
+    return [
       // For the 'target_bundles' setting, a NULL value is equivalent to "allow
       // entities from any bundle to be referenced" and an empty array value is
       // equivalent to "no entities from any bundle can be referenced".
       'target_bundles' => NULL,
       'sort' => [
         'field' => '_none',
+        'direction' => 'ASC',
       ],
       'auto_create' => FALSE,
       'auto_create_bundle' => NULL,
-    ];
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    $configuration = $this->getConfiguration();
+    $entity_type_id = $configuration['target_type'];
+    $entity_type = $this->entityManager->getDefinition($entity_type_id);
+    $bundles = $this->entityManager->getBundleInfo($entity_type_id);
 
     if ($entity_type->hasKey('bundle')) {
       $bundle_options = [];
@@ -132,7 +93,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
         '#type' => 'checkboxes',
         '#title' => $this->t('Bundles'),
         '#options' => $bundle_options,
-        '#default_value' => (array) $selection_handler_settings['target_bundles'],
+        '#default_value' => (array) $configuration['target_bundles'],
         '#required' => TRUE,
         '#size' => 6,
         '#multiple' => TRUE,
@@ -189,7 +150,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
         ] + $fields,
         '#ajax' => TRUE,
         '#limit_validation_errors' => [],
-        '#default_value' => $selection_handler_settings['sort']['field'],
+        '#default_value' => $configuration['sort']['field'],
       ];
 
       $form['sort']['settings'] = [
@@ -198,12 +159,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
         '#process' => [[EntityReferenceItem::class, 'formProcessMergeParent']],
       ];
 
-      if ($selection_handler_settings['sort']['field'] != '_none') {
-        // Merge-in default values.
-        $selection_handler_settings['sort'] += [
-          'direction' => 'ASC',
-        ];
-
+      if ($configuration['sort']['field'] != '_none') {
         $form['sort']['settings']['direction'] = [
           '#type' => 'select',
           '#title' => $this->t('Sort direction'),
@@ -212,7 +168,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
             'ASC' => $this->t('Ascending'),
             'DESC' => $this->t('Descending'),
           ],
-          '#default_value' => $selection_handler_settings['sort']['direction'],
+          '#default_value' => $configuration['sort']['direction'],
         ];
       }
     }
@@ -220,17 +176,17 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
     $form['auto_create'] = [
       '#type' => 'checkbox',
       '#title' => $this->t("Create referenced entities if they don't already exist"),
-      '#default_value' => $selection_handler_settings['auto_create'],
+      '#default_value' => $configuration['auto_create'],
       '#weight' => -2,
     ];
 
     if ($entity_type->hasKey('bundle')) {
-      $bundles = array_intersect_key($bundle_options, array_filter((array) $selection_handler_settings['target_bundles']));
+      $bundles = array_intersect_key($bundle_options, array_filter((array) $configuration['target_bundles']));
       $form['auto_create_bundle'] = [
         '#type' => 'select',
         '#title' => $this->t('Store new items in'),
         '#options' => $bundles,
-        '#default_value' => $selection_handler_settings['auto_create_bundle'],
+        '#default_value' => $configuration['auto_create_bundle'],
         '#access' => count($bundles) > 1,
         '#states' => [
           'visible' => [
@@ -248,6 +204,8 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    * {@inheritdoc}
    */
   public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+    parent::validateConfigurationForm($form, $form_state);
+
     // If no checkboxes were checked for 'target_bundles', store NULL ("all
     // bundles are referenceable") rather than empty array ("no bundle is
     // referenceable" - typically happens when all referenceable bundles have
@@ -261,11 +219,6 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
     $form_state->unsetValue(['settings', 'handler_settings', 'target_bundles_update']);
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }
-
   /**
    * Form element validation handler; Filters the #value property of an element.
    */
@@ -278,7 +231,7 @@ public static function elementValidateFilter(&$element, FormStateInterface $form
    * {@inheritdoc}
    */
   public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
-    $target_type = $this->configuration['target_type'];
+    $target_type = $this->getConfiguration()['target_type'];
 
     $query = $this->buildEntityQuery($match, $match_operator);
     if ($limit > 0) {
@@ -353,8 +306,9 @@ public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
    */
   public function validateReferenceableNewEntities(array $entities) {
     return array_filter($entities, function ($entity) {
-      if (isset($this->configuration['handler_settings']['target_bundles'])) {
-        return in_array($entity->bundle(), $this->configuration['handler_settings']['target_bundles']);
+      $target_bundles = $this->getConfiguration()['target_bundles'];
+      if (isset($target_bundles)) {
+        return in_array($entity->bundle(), $target_bundles);
       }
       return TRUE;
     });
@@ -374,23 +328,23 @@ public function validateReferenceableNewEntities(array $entities) {
    *   it.
    */
   protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
-    $target_type = $this->configuration['target_type'];
-    $handler_settings = $this->configuration['handler_settings'];
+    $configuration = $this->getConfiguration();
+    $target_type = $configuration['target_type'];
     $entity_type = $this->entityManager->getDefinition($target_type);
 
     $query = $this->entityManager->getStorage($target_type)->getQuery();
 
     // If 'target_bundles' is NULL, all bundles are referenceable, no further
     // conditions are needed.
-    if (isset($handler_settings['target_bundles']) && is_array($handler_settings['target_bundles'])) {
+    if (is_array($configuration['target_bundles'])) {
       // If 'target_bundles' is an empty array, no bundle is referenceable,
       // force the query to never return anything and bail out early.
-      if ($handler_settings['target_bundles'] === []) {
+      if ($configuration['target_bundles'] === []) {
         $query->condition($entity_type->getKey('id'), NULL, '=');
         return $query;
       }
       else {
-        $query->condition($entity_type->getKey('bundle'), $handler_settings['target_bundles'], 'IN');
+        $query->condition($entity_type->getKey('bundle'), $configuration['target_bundles'], 'IN');
       }
     }
 
@@ -406,21 +360,13 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
     $query->addMetaData('entity_reference_selection_handler', $this);
 
     // Add the sort option.
-    if (!empty($handler_settings['sort'])) {
-      $sort_settings = $handler_settings['sort'];
-      if ($sort_settings['field'] != '_none') {
-        $query->sort($sort_settings['field'], $sort_settings['direction']);
-      }
+    if ($configuration['sort']['field'] !== '_none') {
+      $query->sort($configuration['sort']['field'], $configuration['sort']['direction']);
     }
 
     return $query;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function entityQueryAlter(SelectInterface $query) { }
-
   /**
    * Helper method: Passes a query to the alteration system again.
    *
diff --git a/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php b/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php
index 2492fc4336baa045b83a08ae8714e96057f32d39..414c5b23111a29a53d22e733033fdc7fa7556a74 100644
--- a/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php
+++ b/core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php
@@ -66,6 +66,8 @@ public function validateReferenceableNewEntities(array $entities) {
    * {@inheritdoc}
    */
   public function entityQueryAlter(SelectInterface $query) {
+    parent::entityQueryAlter($query);
+
     $tables = $query->getTables();
     $data_table = 'comment_field_data';
     if (!isset($tables['comment_field_data']['alias'])) {
diff --git a/core/modules/file/config/schema/file.schema.yml b/core/modules/file/config/schema/file.schema.yml
index 42d57d9a64ec70c0d0a43f8682c09b0e3569ed77..336550904b913ee4d327ce6dcaedf6f7847f18dc 100644
--- a/core/modules/file/config/schema/file.schema.yml
+++ b/core/modules/file/config/schema/file.schema.yml
@@ -51,7 +51,7 @@ base_file_field_field_settings:
       label: 'Reference method'
     handler_settings:
       type: entity_reference_selection.[%parent.handler]
-      label: 'Entity reference selection settings'
+      label: 'File selection handler settings'
     file_directory:
       type: string
       label: 'File directory'
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php b/core/modules/system/tests/src/Functional/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php
index df33102811fc690309b0bfb88a184651d0214218..3772f3a97b4f5c7c2ce55289d9117c58ecf0143e 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php
@@ -74,9 +74,7 @@ public function testNodeHandler() {
     $selection_options = [
       'target_type' => 'node',
       'handler' => 'default',
-      'handler_settings' => [
-        'target_bundles' => NULL,
-      ],
+      'target_bundles' => NULL,
     ];
 
     // Build a set of test data.
@@ -200,10 +198,8 @@ public function testUserHandler() {
     $selection_options = [
       'target_type' => 'user',
       'handler' => 'default',
-      'handler_settings' => [
-        'target_bundles' => NULL,
-        'include_anonymous' => TRUE,
-      ],
+      'target_bundles' => NULL,
+      'include_anonymous' => TRUE,
     ];
 
     // Build a set of test data.
@@ -322,7 +318,7 @@ public function testUserHandler() {
     $this->assertReferenceable($selection_options, $referenceable_tests, 'User handler (admin)');
 
     // Test the 'include_anonymous' option.
-    $selection_options['handler_settings']['include_anonymous'] = FALSE;
+    $selection_options['include_anonymous'] = FALSE;
     $referenceable_tests = [
       [
         'arguments' => [
@@ -361,9 +357,7 @@ public function testCommentHandler() {
     $selection_options = [
       'target_type' => 'comment',
       'handler' => 'default',
-      'handler_settings' => [
-        'target_bundles' => NULL,
-      ],
+      'target_bundles' => NULL,
     ];
 
     // Build a set of test data.
diff --git a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
index b8f4d14fbe1d334b7fcbf8315998f37715b25ae8..a66eaf51c609dd299ee69299bd23f49b89916f99 100644
--- a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
+++ b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
@@ -3,7 +3,6 @@
 namespace Drupal\taxonomy\Plugin\EntityReferenceSelection;
 
 use Drupal\Component\Utility\Html;
-use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\taxonomy\Entity\Vocabulary;
@@ -24,8 +23,13 @@ class TermSelection extends DefaultSelection {
   /**
    * {@inheritdoc}
    */
-  public function entityQueryAlter(SelectInterface $query) {
-    // @todo: How to set access, as vocabulary is now config?
+  public function defaultConfiguration() {
+    return [
+      'sort' => [
+        'field' => 'name',
+        'direction' => 'asc',
+      ]
+    ] + parent::defaultConfiguration();
   }
 
   /**
@@ -49,15 +53,13 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    */
   public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
     if ($match || $limit) {
-      $this->configuration['handler_settings']['sort'] = ['field' => 'name', 'direction' => 'asc'];
       return parent::getReferenceableEntities($match, $match_operator, $limit);
     }
 
     $options = [];
 
     $bundles = $this->entityManager->getBundleInfo('taxonomy_term');
-    $handler_settings = $this->configuration['handler_settings'];
-    $bundle_names = !empty($handler_settings['target_bundles']) ? $handler_settings['target_bundles'] : array_keys($bundles);
+    $bundle_names = $this->getConfiguration()['target_bundles'] ?: array_keys($bundles);
 
     foreach ($bundle_names as $bundle) {
       if ($vocabulary = Vocabulary::load($bundle)) {
diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml
index 627d8a69608050e7f51f7ce7934c7791344e5857..2f9bda44f28decd5fd9b8a0cf9a522d8fcb47c6e 100644
--- a/core/modules/user/config/schema/user.schema.yml
+++ b/core/modules/user/config/schema/user.schema.yml
@@ -166,8 +166,10 @@ condition.plugin.user_role:
       sequence:
         type: string
 
+# Schema for the entity reference 'default:user' selection handler settings.
 entity_reference_selection.default:user:
-  type: entity_reference_selection
+  type: entity_reference_selection.default
+  label: 'User selection handler settings'
   mapping:
     filter:
       type: mapping
diff --git a/core/modules/user/src/Plugin/EntityReferenceSelection/UserSelection.php b/core/modules/user/src/Plugin/EntityReferenceSelection/UserSelection.php
index feec81b46828f21832fca80f952857ea76eeadd8..d06a81c41e2db684283618df2316b6128d23bdbe 100644
--- a/core/modules/user/src/Plugin/EntityReferenceSelection/UserSelection.php
+++ b/core/modules/user/src/Plugin/EntityReferenceSelection/UserSelection.php
@@ -83,21 +83,26 @@ public static function create(ContainerInterface $container, array $configuratio
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
-    $selection_handler_settings = $this->configuration['handler_settings'];
-
-    // Merge in default values.
-    $selection_handler_settings += [
+  public function defaultConfiguration() {
+    return [
       'filter' => [
         'type' => '_none',
+        'role' => NULL,
       ],
       'include_anonymous' => TRUE,
-    ];
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $configuration = $this->getConfiguration();
 
     $form['include_anonymous'] = [
       '#type' => 'checkbox',
       '#title' => $this->t('Include the anonymous user.'),
-      '#default_value' => $selection_handler_settings['include_anonymous'],
+      '#default_value' => $configuration['include_anonymous'],
     ];
 
     // Add user specific filter options.
@@ -110,7 +115,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
       ],
       '#ajax' => TRUE,
       '#limit_validation_errors' => [],
-      '#default_value' => $selection_handler_settings['filter']['type'],
+      '#default_value' => $configuration['filter']['type'],
     ];
 
     $form['filter']['settings'] = [
@@ -119,18 +124,13 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
       '#process' => [['\Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', 'formProcessMergeParent']],
     ];
 
-    if ($selection_handler_settings['filter']['type'] == 'role') {
-      // Merge in default values.
-      $selection_handler_settings['filter'] += [
-        'role' => NULL,
-      ];
-
+    if ($configuration['filter']['type'] == 'role') {
       $form['filter']['settings']['role'] = [
         '#type' => 'checkboxes',
         '#title' => $this->t('Restrict to the selected roles'),
         '#required' => TRUE,
         '#options' => array_diff_key(user_role_names(TRUE), [RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID]),
-        '#default_value' => $selection_handler_settings['filter']['role'],
+        '#default_value' => $configuration['filter']['role'],
       ];
     }
 
@@ -144,11 +144,12 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    */
   protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
     $query = parent::buildEntityQuery($match, $match_operator);
-    $handler_settings = $this->configuration['handler_settings'];
+
+    $configuration = $this->getConfiguration();
 
     // Filter out the Anonymous user if the selection handler is configured to
     // exclude it.
-    if (isset($handler_settings['include_anonymous']) && !$handler_settings['include_anonymous']) {
+    if (!$configuration['include_anonymous']) {
       $query->condition('uid', 0, '<>');
     }
 
@@ -158,8 +159,8 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
     }
 
     // Filter by role.
-    if (!empty($handler_settings['filter']['role'])) {
-      $query->condition('roles', $handler_settings['filter']['role'], 'IN');
+    if (!empty($configuration['filter']['role'])) {
+      $query->condition('roles', $configuration['filter']['role'], 'IN');
     }
 
     // Adding the permission check is sadly insufficient for users: core
@@ -191,10 +192,10 @@ public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
   public function validateReferenceableNewEntities(array $entities) {
     $entities = parent::validateReferenceableNewEntities($entities);
     // Mirror the conditions checked in buildEntityQuery().
-    if (!empty($this->configuration['handler_settings']['filter']['role'])) {
-      $entities = array_filter($entities, function ($user) {
+    if ($role = $this->getConfiguration()['filter']['role']) {
+      $entities = array_filter($entities, function ($user) use ($role) {
         /** @var \Drupal\user\UserInterface $user */
-        return !empty(array_intersect($user->getRoles(), $this->configuration['handler_settings']['filter']['role']));
+        return !empty(array_intersect($user->getRoles(), $role));
       });
     }
     if (!$this->currentUser->hasPermission('administer users')) {
@@ -210,9 +211,10 @@ public function validateReferenceableNewEntities(array $entities) {
    * {@inheritdoc}
    */
   public function entityQueryAlter(SelectInterface $query) {
+    parent::entityQueryAlter($query);
+
     // Bail out early if we do not need to match the Anonymous user.
-    $handler_settings = $this->configuration['handler_settings'];
-    if (isset($handler_settings['include_anonymous']) && !$handler_settings['include_anonymous']) {
+    if (!$this->getConfiguration()['include_anonymous']) {
       return;
     }
 
diff --git a/core/modules/views/config/schema/views.entity_reference.schema.yml b/core/modules/views/config/schema/views.entity_reference.schema.yml
index 027c62fa46c1c79e797307ca46e1ade0efe1c3ea..f13645ab4320cbdc750756e1a57c48f9a5bbff69 100644
--- a/core/modules/views/config/schema/views.entity_reference.schema.yml
+++ b/core/modules/views/config/schema/views.entity_reference.schema.yml
@@ -1,8 +1,8 @@
-# Schema for the views entity reference selection plugins.
+# Schema for the entity reference 'views' selection handler settings.
 
 entity_reference_selection.views:
-  type: mapping
-  label: 'View handler settings'
+  type: entity_reference_selection
+  label: 'Views selection handler settings'
   mapping:
     view:
       type: mapping
diff --git a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php
index 05ac9b99f14e159a32193de58e4bcac30c201417..9911f61a0c878752d7ad577f8dba46ee2b27a5ce 100644
--- a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php
+++ b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php
@@ -2,17 +2,12 @@
 
 namespace Drupal\views\Plugin\EntityReferenceSelection;
 
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionTrait;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Plugin\PluginBase;
-use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Url;
 use Drupal\views\Views;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Plugin implementation of the 'selection' entity_reference.
@@ -24,80 +19,37 @@
  *   weight = 0
  * )
  */
-class ViewsSelection extends PluginBase implements SelectionInterface, ContainerFactoryPluginInterface {
+class ViewsSelection extends SelectionPluginBase implements ContainerFactoryPluginInterface {
 
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * The module handler service.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
-   * The current user.
-   *
-   * @var \Drupal\Core\Session\AccountInterface
-   */
-  protected $currentUser;
+  use SelectionTrait;
 
   /**
-   * Constructs a new SelectionBase object.
+   * The loaded View object.
    *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager service.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   The module handler service.
-   * @param \Drupal\Core\Session\AccountInterface $current_user
-   *   The current user.
+   * @var \Drupal\views\ViewExecutable;
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-
-    $this->entityManager = $entity_manager;
-    $this->moduleHandler = $module_handler;
-    $this->currentUser = $current_user;
-  }
+  protected $view;
 
   /**
    * {@inheritdoc}
    */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('entity.manager'),
-      $container->get('module_handler'),
-      $container->get('current_user')
-    );
+  public function defaultConfiguration() {
+    return [
+      'view' => [
+        'view_name' => NULL,
+        'display_name' => NULL,
+        'arguments' => [],
+      ],
+    ] + parent::defaultConfiguration();
   }
 
-  /**
-   * The loaded View object.
-   *
-   * @var \Drupal\views\ViewExecutable;
-   */
-  protected $view;
-
   /**
    * {@inheritdoc}
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
-    $selection_handler_settings = $this->configuration['handler_settings'];
-    $view_settings = !empty($selection_handler_settings['view']) ? $selection_handler_settings['view'] : [];
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    $view_settings = $this->getConfiguration()['view'];
     $displays = Views::getApplicableViews('entity_reference_display');
     // Filter views that list the entity type we want, and group the separate
     // displays by view.
@@ -156,16 +108,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
     return $form;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }
-
   /**
    * Initializes a view.
    *
@@ -184,9 +126,8 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
    *   Return TRUE if the view was initialized, FALSE otherwise.
    */
   protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $limit = 0, $ids = NULL) {
-    $handler_settings = $this->configuration['handler_settings'];
-    $view_name = $handler_settings['view']['view_name'];
-    $display_name = $handler_settings['view']['display_name'];
+    $view_name = $this->getConfiguration()['view']['view_name'];
+    $display_name = $this->getConfiguration()['view']['display_name'];
 
     // Check that the view is valid and the display still exists.
     $this->view = Views::getView($view_name);
@@ -211,9 +152,8 @@ protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $
    * {@inheritdoc}
    */
   public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
-    $handler_settings = $this->configuration['handler_settings'];
-    $display_name = $handler_settings['view']['display_name'];
-    $arguments = $handler_settings['view']['arguments'];
+    $display_name = $this->getConfiguration()['view']['display_name'];
+    $arguments = $this->getConfiguration()['view']['arguments'];
     $result = [];
     if ($this->initializeView($match, $match_operator, $limit)) {
       // Get the results.
@@ -242,9 +182,8 @@ public function countReferenceableEntities($match = NULL, $match_operator = 'CON
    * {@inheritdoc}
    */
   public function validateReferenceableEntities(array $ids) {
-    $handler_settings = $this->configuration['handler_settings'];
-    $display_name = $handler_settings['view']['display_name'];
-    $arguments = $handler_settings['view']['arguments'];
+    $display_name = $this->getConfiguration()['view']['display_name'];
+    $arguments = $this->getConfiguration()['view']['arguments'];
     $result = [];
     if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) {
       // Get the results.
@@ -283,9 +222,4 @@ public static function settingsFormValidate($element, FormStateInterface $form_s
     $form_state->setValueForElement($element, $value);
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function entityQueryAlter(SelectInterface $query) { }
-
 }
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php
index eb13d0e491267e529007379c5e7aa906ee984c41..b2c72b99d92c57c8b2888bc96b7d300837cb9063 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php
@@ -96,13 +96,11 @@ public function testSort() {
     $selection_options = [
       'target_type' => 'node',
       'handler' => 'default',
-      'handler_settings' => [
-        'target_bundles' => NULL,
-        // Add sorting.
-        'sort' => [
-          'field' => 'field_text.value',
-          'direction' => 'DESC',
-        ],
+      'target_bundles' => NULL,
+      // Add sorting.
+      'sort' => [
+        'field' => 'field_text.value',
+        'direction' => 'DESC',
       ],
     ];
     $handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options);
@@ -117,7 +115,7 @@ public function testSort() {
     $this->assertIdentical($result['article'], $expected_result, 'Query sorted by field returned expected values.');
 
     // Assert sort by base field.
-    $selection_options['handler_settings']['sort'] = [
+    $selection_options['sort'] = [
       'field' => 'nid',
       'direction' => 'ASC',
     ];
diff --git a/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php b/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..eec5b0f0dce2c012a42e2507a146fc11a98f27d6
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/EntityReferenceSelection/EntityReferenceSelectionUnitTest.php
@@ -0,0 +1,235 @@
+<?php
+
+namespace Drupal\Tests\Core\EntityReferenceSelection;
+
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Provides unit testing for selection handlers.
+ *
+ * @coversDefaultClass \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase
+ *
+ * @group entity_reference
+ * @group legacy
+ */
+class EntityReferenceSelectionUnitTest extends UnitTestCase {
+
+  /**
+   * Tests invalid default configuration.
+   *
+   * @covers ::defaultConfiguration
+   * @covers ::resolveBackwardCompatibilityConfiguration
+   */
+  public function testInvalidDefaultConfiguration() {
+    $this->setExpectedException(\InvalidArgumentException::class, "TestSelectionWithInvalidDefaultConfiguration::defaultConfiguration() should not contain a 'handler_settings' key. All settings should be placed in the root level.");
+    new TestSelectionWithInvalidDefaultConfiguration(
+      [],
+      'test_selector',
+      ['class' => 'TestSelectionWithInvalidDefaultConfiguration']
+    );
+  }
+
+  /**
+   * Tests the selection handler with malformed 'handler_settings' value.
+   *
+   * @covers ::setConfiguration
+   * @covers ::resolveBackwardCompatibilityConfiguration
+   */
+  public function testMalformedHandlerSettingsValue() {
+    $this->setExpectedException(\InvalidArgumentException::class, "The setting 'handler_settings' is reserved and cannot be used.");
+    new TestSelection(
+      // The deprecated 'handler_setting' should be an array.
+      ['handler_settings' => FALSE],
+      'test_selector',
+      ['class' => 'TestSelectionWithInvalidDefaultConfiguration']
+    );
+  }
+
+  /**
+   * Provides test data for ::testSetConfiguration()
+   *
+   * @return array
+   *
+   * @see \Drupal\Tests\Core\EntityReferenceSelection\testSetConfiguration
+   */
+  public function providerTestSetConfiguration() {
+    return [
+      [
+        [
+          'setting1' => 'foo',
+          'setting2' => [
+            'bar' => 'bar value',
+            'baz' => 'baz value',
+          ],
+        ],
+      ],
+      [
+        [
+          'handler_settings' => [
+            'setting1' => 'foo',
+            'setting2' => [
+              'bar' => 'bar value',
+              'baz' => 'baz value',
+            ],
+          ],
+        ],
+      ],
+      [
+        [
+          'setting1' => 'foo',
+          'handler_settings' => [
+            'setting2' => [
+              'bar' => 'bar value',
+              'baz' => 'baz value',
+            ],
+          ]
+        ],
+      ],
+      [
+        [
+          'setting1' => 'foo',
+          'setting2' => [
+            'bar' => 'bar value',
+            'baz' => 'baz value',
+          ],
+          'handler_settings' => [
+            // Same setting from root level takes precedence.
+            'setting2' => 'this will be overwritten',
+          ]
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * Tests selection handler plugin configuration set.
+   *
+   * @dataProvider providerTestSetConfiguration
+   * @covers ::setConfiguration
+   * @covers ::resolveBackwardCompatibilityConfiguration
+   * @covers ::ensureBackwardCompatibilityConfiguration
+   *
+   * @param array $options
+   *   The configuration passed to the plugin.
+   */
+  public function testSetConfiguration($options) {
+    $selection = new TestSelection($options, 'test_selector', []);
+
+    $expected = [
+      'target_type' => NULL,
+      'handler' => 'test_selector',
+      'entity' => NULL,
+      'setting1' => 'foo',
+      'setting2' => [
+        'qux' => 'qux value',
+        'bar' => 'bar value',
+        'baz' => 'baz value',
+      ],
+      'setting3' => 'foobar',
+      'handler_settings' => [
+        'setting1' => 'foo',
+        'setting2' => [
+          'qux' => 'qux value',
+          'bar' => 'bar value',
+          'baz' => 'baz value',
+        ],
+        'setting3' => 'foobar',
+      ],
+    ];
+
+    $this->assertArrayEquals($expected, $selection->getConfiguration());
+  }
+
+  /**
+   * Tests the selection handler plugin BC structure.
+   *
+   * @covers ::setConfiguration
+   * @covers ::resolveBackwardCompatibilityConfiguration
+   * @covers ::ensureBackwardCompatibilityConfiguration
+   */
+  public function testSetConfigurationBcLevel() {
+    $config = [
+      'target_type' => 'some_entity_type_id',
+      'handler' => 'test_selector',
+      'setting1' => 'foo',
+    ];
+    $selection = new TestSelection($config, 'test_selector', []);
+
+    $expected = [
+      'target_type' => 'some_entity_type_id',
+      'handler' => 'test_selector',
+      'entity' => NULL,
+      'setting1' => 'foo',
+      'setting2' => ['qux' => 'qux value'],
+      'setting3' => 'foobar',
+      'handler_settings' => [
+        'setting1' => 'foo',
+        'setting2' => ['qux'  => 'qux value'],
+        'setting3' => 'foobar',
+      ],
+    ];
+
+    $this->assertArrayEquals($expected, $selection->getConfiguration());
+
+    // Read the stored values and override a setting.
+    $config = $selection->getConfiguration();
+    $config['setting1'] = 'bar';
+    $selection->setConfiguration($config);
+    $expected['setting1'] = 'bar';
+    $expected['handler_settings']['setting1'] = 'bar';
+
+    $this->assertArrayEquals($expected, $selection->getConfiguration());
+  }
+
+  /**
+   * Tests deprecation error triggering.
+   *
+   * @covers ::setConfiguration
+   * @covers ::resolveBackwardCompatibilityConfiguration
+   * @expectedDeprecation Providing settings under 'handler_settings' is deprecated and will be removed before 9.0.0. Move the settings in the root of the configuration array. See https://www.drupal.org/node/2870971.
+   */
+  public function testDeprecationErrorTriggering() {
+    // Configuration with BC level.
+    $config = ['handler_settings' => ['setting1' => TRUE]];
+    new TestSelection($config, 'test_selector', []);
+    // Ensure at least one assertion.
+    $this->assertTrue(TRUE);
+  }
+
+}
+
+/**
+ * Provides a testing plugin.
+ */
+class TestSelection extends SelectionPluginBase {
+
+  public function defaultConfiguration() {
+    return [
+      'setting2' => [
+        'qux' => 'qux value',
+      ],
+      'setting3' => 'foobar',
+    ] + parent::defaultConfiguration();
+  }
+
+  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { }
+
+  public function validateReferenceableEntities(array $ids) { }
+
+  public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') { }
+
+}
+
+/**
+ * Provides a testing plugin with invalid default configuration.
+ */
+class TestSelectionWithInvalidDefaultConfiguration extends TestSelection {
+
+  public function defaultConfiguration() {
+    return [
+      'handler_settings' => ['foo' => 'bar'],
+    ];
+  }
+
+}