diff --git a/core/core.services.yml b/core/core.services.yml
index 9ab7af7..1073dd0 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -187,7 +187,11 @@ services:
     arguments: [default]
   form_builder:
     class: Drupal\Core\Form\FormBuilder
-    arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@theme.manager', '@?csrf_token']
+    arguments: ['@form_validator', '@form_submitter', '@form_cache', '@form_builder.alter', '@event_dispatcher', '@request_stack', '@class_resolver', '@?csrf_token']
+  form_builder.alter:
+    class: Drupal\Core\Form\FormAlter
+    arguments: ['@module_handler', '@theme.manager']
+    public: false  # Private to form_builder
   form_validator:
     class: Drupal\Core\Form\FormValidator
     arguments: ['@request_stack', '@string_translation', '@csrf_token', '@logger.channel.form']
diff --git a/core/lib/Drupal/Core/Form/FormAlter.php b/core/lib/Drupal/Core/Form/FormAlter.php
new file mode 100644
index 0000000..4b81d4b
--- /dev/null
+++ b/core/lib/Drupal/Core/Form/FormAlter.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Form\FormAlter.
+ */
+
+namespace Drupal\Core\Form;
+
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Theme\ThemeManagerInterface;
+
+/**
+ * Allows the module handler and theme manager to alter forms.
+ */
+class FormAlter implements FormAlterInterface {
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The theme manager.
+   *
+   * @var \Drupal\Core\Theme\ThemeManagerInterface
+   */
+  protected $themeManager;
+
+  /**
+   * Constructs a new FormAlter.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
+   *   The theme manager.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager) {
+    $this->moduleHandler = $module_handler;
+    $this->themeManager = $theme_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alterForm($form_id, &$form, FormStateInterface $form_state) {
+    // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
+    // hook_form_FORM_ID_alter() implementations.
+    $hooks = array('form');
+    $build_info = $form_state->getBuildInfo();
+    if (isset($build_info['base_form_id'])) {
+      $hooks[] = 'form_' . $build_info['base_form_id'];
+    }
+    $hooks[] = 'form_' . $form_id;
+    $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
+    $this->themeManager->alter($hooks, $form, $form_state, $form_id);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Form/FormAlterInterface.php b/core/lib/Drupal/Core/Form/FormAlterInterface.php
new file mode 100644
index 0000000..5631097
--- /dev/null
+++ b/core/lib/Drupal/Core/Form/FormAlterInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Form\FormAlterInterface.
+ */
+
+namespace Drupal\Core\Form;
+
+/**
+ * Allows the module handler and theme manager to alter forms.
+ */
+interface FormAlterInterface {
+
+  /**
+   * Allows the module handler and theme manager to alter forms.
+   */
+  public function alterForm($form_id, &$form, FormStateInterface $form_state);
+
+}
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index d16d851..1305f87 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -14,9 +14,7 @@
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Access\CsrfTokenGenerator;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Render\Element;
-use Drupal\Core\Theme\ThemeManagerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpFoundation\Response;
@@ -29,13 +27,6 @@
 class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormSubmitterInterface, FormCacheInterface {
 
   /**
-   * The module handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
    * The event dispatcher.
    *
    * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
@@ -71,13 +62,6 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
   protected $currentUser;
 
   /**
-   * The theme manager.
-   *
-   * @var \Drupal\Core\Theme\ThemeManagerInterface
-   */
-  protected $themeManager;
-
-  /**
    * @var \Drupal\Core\Form\FormValidatorInterface
    */
   protected $formValidator;
@@ -95,6 +79,13 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
   protected $formCache;
 
   /**
+   * The form alter service.
+   *
+   * @var \Drupal\Core\Form\FormAlterInterface
+   */
+  protected $formAlter;
+
+  /**
    * Constructs a new FormBuilder.
    *
    * @param \Drupal\Core\Form\FormValidatorInterface $form_validator
@@ -103,29 +94,26 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
    *   The form submission processor.
    * @param \Drupal\Core\Form\FormCacheInterface $form_cache
    *   The form cache.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   The module handler.
+   * @param \Drupal\Core\Form\FormAlterInterface $form_alter
+   *   The form alter service.
    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
    *   The event dispatcher.
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
    *   The request stack.
    * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
    *   The class resolver.
-   * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
-   *   The theme manager.
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
    *   The CSRF token generator.
    */
-  public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL) {
+  public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, FormAlterInterface $form_alter, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, CsrfTokenGenerator $csrf_token = NULL) {
     $this->formValidator = $form_validator;
     $this->formSubmitter = $form_submitter;
     $this->formCache = $form_cache;
-    $this->moduleHandler = $module_handler;
+    $this->formAlter = $form_alter;
     $this->eventDispatcher = $event_dispatcher;
     $this->requestStack = $request_stack;
     $this->classResolver = $class_resolver;
     $this->csrfToken = $csrf_token;
-    $this->themeManager = $theme_manager;
   }
 
   /**
@@ -622,16 +610,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
         $form['#theme'][] = $build_info['base_form_id'];
       }
     }
-
-    // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
-    // hook_form_FORM_ID_alter() implementations.
-    $hooks = array('form');
-    if (isset($build_info['base_form_id'])) {
-      $hooks[] = 'form_' . $build_info['base_form_id'];
-    }
-    $hooks[] = 'form_' . $form_id;
-    $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
-    $this->themeManager->alter($hooks, $form, $form_state, $form_id);
+    $this->formAlter->alterForm($form_id, $form, $form_state);
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Form/FormAlterTest.php b/core/tests/Drupal/Tests/Core/Form/FormAlterTest.php
new file mode 100644
index 0000000..05bafdd
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Form/FormAlterTest.php
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Form\FormAlterTest.
+ */
+
+namespace Drupal\Tests\Core\Form;
+
+use Drupal\Core\Form\FormAlter;
+use Drupal\Core\Form\FormState;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Form\FormAlter
+ * @group Form
+ */
+class FormAlterTest extends UnitTestCase {
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $moduleHandler;
+
+  /**
+   * The theme manager.
+   *
+   * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $themeManager;
+
+  /**
+   * The form alter service.
+   *
+   * @var \Drupal\Core\Form\FormAlter
+   */
+  protected $formAlter;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+    $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
+    $this->formAlter = new FormAlter($this->moduleHandler, $this->themeManager);
+  }
+
+  /**
+   * @covers ::alterForm
+   *
+   * @dataProvider providerTestAlterForm
+   */
+  public function testAlterForm($form_id, $expected_hooks, $form_state_additions = []) {
+    $form = [];
+    $form_state = (new FormState())->setFormState($form_state_additions);
+
+    $this->moduleHandler->expects($this->once())
+      ->method('alter')
+      ->with($expected_hooks, $form, $form_state, $form_id);
+    $this->themeManager->expects($this->once())
+      ->method('alter')
+      ->with($expected_hooks, $form, $form_state, $form_id);
+
+    $this->formAlter->alterForm($form_id, $form, $form_state);
+  }
+
+  /**
+   * Provides test data for testAlterForm().
+   */
+  public function providerTestAlterForm() {
+    $data = [];
+    $data[] = [
+      'red',
+      [
+        'form',
+        'form_red',
+      ],
+    ];
+    $data[] = [
+      'red',
+      [
+        'form',
+        'form_blue',
+        'form_red',
+      ],
+      [
+        'build_info' => [
+          'base_form_id' => 'blue',
+        ],
+      ],
+    ];
+    return $data;
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
index 2386eb2..e103bdf 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
@@ -49,18 +49,18 @@
   protected $urlGenerator;
 
   /**
-   * The mocked module handler.
+   * The form cache.
    *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Form\FormCacheInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $moduleHandler;
+  protected $formCache;
 
   /**
-   * The form cache.
+   * The form alter service.
    *
-   * @var \Drupal\Core\Form\FormCacheInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Form\FormAlterInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $formCache;
+  protected $formAlter;
 
   /**
    * The current user.
@@ -117,36 +117,19 @@
   protected $translationManager;
 
   /**
-   * @var \Drupal\Core\DrupalKernelInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $kernel;
-
-  /**
    * @var \PHPUnit_Framework_MockObject_MockObject|\Psr\Log\LoggerInterface
    */
   protected $logger;
 
-  /**
-   * The mocked theme manager.
-   *
-   * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $themeManager;
-
   protected function setUp() {
-    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-
     $this->formCache = $this->getMock('Drupal\Core\Form\FormCacheInterface');
+    $this->formAlter = $this->getMock('Drupal\Core\Form\FormAlterInterface');
     $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
     $this->classResolver = $this->getClassResolverStub();
     $this->csrfToken = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
       ->disableOriginalConstructor()
       ->getMock();
-    $this->kernel = $this->getMockBuilder('\Drupal\Core\DrupalKernel')
-      ->disableOriginalConstructor()
-      ->getMock();
     $this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
     $this->request = new Request();
     $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
     $this->requestStack = new RequestStack();
@@ -162,7 +145,7 @@ protected function setUp() {
       ->getMock();
     $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
 
-    $this->formBuilder = new TestFormBuilder($this->formValidator, $this->formSubmitter, $this->formCache, $this->moduleHandler, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->themeManager, $this->csrfToken, $this->kernel);
+    $this->formBuilder = new TestFormBuilder($this->formValidator, $this->formSubmitter, $this->formCache, $this->formAlter, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->csrfToken);
     $this->formBuilder->setCurrentUser($this->account);
   }
 
