diff --git a/core/core.services.yml b/core/core.services.yml
index 9a36139..89523a8 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -424,3 +424,6 @@ services:
   token:
     class: Drupal\Core\Utility\Token
     arguments: ['@module_handler']
+  batch.storage:
+    class: Drupal\Core\Utility\BatchStorage
+    arguments: ['@database', '@request']
diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index ff0b4cb..2152725 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -17,27 +17,6 @@
 use \Symfony\Component\HttpFoundation\JsonResponse;
 
 /**
- * Loads a batch from the database.
- *
- * @param $id
- *   The ID of the batch to load. When a progressive batch is being processed,
- *   the relevant ID is found in $_REQUEST['id'].
- *
- * @return
- *   An array representing the batch, or FALSE if no batch was found.
- */
-function batch_load($id) {
-  $batch = db_query("SELECT batch FROM {batch} WHERE bid = :bid AND token = :token", array(
-    ':bid' => $id,
-    ':token' => drupal_get_token($id),
-  ))->fetchField();
-  if ($batch) {
-    return unserialize($batch);
-  }
-  return FALSE;
-}
-
-/**
  * Renders the batch processing page based on the current state of the batch.
  *
  * @see _batch_shutdown()
@@ -51,7 +30,7 @@ function _batch_page() {
 
   // Retrieve the current state of the batch.
   if (!$batch) {
-    $batch = batch_load($_REQUEST['id']);
+    $batch = Drupal::service('batch.storage')->load($_REQUEST['id']);
     if (!$batch) {
       drupal_set_message(t('No active batch.'), 'error');
       drupal_goto();
@@ -431,9 +410,7 @@ function _batch_finished() {
 
   // Clean up the batch table and unset the static $batch variable.
   if ($batch['progressive']) {
-    db_delete('batch')
-      ->condition('bid', $batch['id'])
-      ->execute();
+    Drupal::service('batch.storage')->delete($batch['id']);
     foreach ($batch['sets'] as $batch_set) {
       if ($queue = _batch_queue($batch_set)) {
         $queue->deleteQueue();
@@ -491,9 +468,6 @@ function _batch_finished() {
  */
 function _batch_shutdown() {
   if ($batch = batch_get()) {
-    db_update('batch')
-      ->fields(array('batch' => serialize($batch)))
-      ->condition('bid', $batch['id'])
-      ->execute();
+    Drupal::service('batch.storage')->update($batch);
   }
 }
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 23f66c2..c5345ff 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -5142,14 +5142,7 @@ function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = 'd
       }
 
       // Store the batch.
-      db_insert('batch')
-        ->fields(array(
-          'bid' => $batch['id'],
-          'timestamp' => REQUEST_TIME,
-          'token' => drupal_get_token($batch['id']),
-          'batch' => serialize($batch),
-        ))
-        ->execute();
+      Drupal::service('batch.storage')->create($batch);
 
       // Set the batch number in the session to guarantee that it will stay alive.
       $_SESSION['batches'][$batch['id']] = TRUE;
diff --git a/core/lib/Drupal/Core/Utility/BatchStorage.php b/core/lib/Drupal/Core/Utility/BatchStorage.php
new file mode 100644
index 0000000..c8efa1f
--- /dev/null
+++ b/core/lib/Drupal/Core/Utility/BatchStorage.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Utility\BatchStorage.
+ */
+
+namespace Drupal\Core\Utility;
+
+use Drupal\Core\Database\Connection;
+use Symfony\Component\HttpFoundation\Request;
+
+class BatchStorage implements BatchStorageInterface {
+
+  /**
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  public function __construct(Connection $connection, Request $request) {
+    $this->connection = $connection;
+    $this->request = $request;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function load($id) {
+    $batch = $this->connection->query("SELECT batch FROM {batch} WHERE bid = :bid AND token = :token", array(
+      ':bid' => $id,
+      ':token' => drupal_get_token($id),
+    ))->fetchField();
+    if ($batch) {
+      return unserialize($batch);
+    }
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($id) {
+    $this->connection->delete('batch')
+      ->condition('bid', $id)
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function update(array $batch) {
+    $this->connection->update('batch')
+      ->fields(array('batch' => serialize($batch)))
+      ->condition('bid', $batch['id'])
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cleanup() {
+    // Cleanup the batch table and the queue for failed batches.
+    $this->connection->delete('batch')
+      ->condition('timestamp', REQUEST_TIME - 864000, '<')
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function create(array $batch) {
+    $this->connection->insert('batch')
+      ->fields(array(
+        'bid' => $batch['id'],
+        'timestamp' => REQUEST_TIME,
+        'token' => drupal_get_token($batch['id']),
+        'batch' => serialize($batch),
+      ))
+      ->execute();
+  }
+}
diff --git a/core/lib/Drupal/Core/Utility/BatchStorageInterface.php b/core/lib/Drupal/Core/Utility/BatchStorageInterface.php
new file mode 100644
index 0000000..5d310df
--- /dev/null
+++ b/core/lib/Drupal/Core/Utility/BatchStorageInterface.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\Core\Utility\BatchStorageInterface.
+ */
+
+namespace Drupal\Core\Utility;
+
+
+interface BatchStorageInterface {
+
+  /**
+   * Loads a batch.
+   *
+   * @param int $id
+   *   The ID of the batch to load.
+   *
+   * @return array
+   *   An array representing the batch, or FALSE if no batch was found.
+   */
+  public function load($id);
+
+  /**
+   * Creates and saves a batch.
+   *
+   * @param array $batch
+   *   The array representing the batch to create.
+   */
+  public function create(array $batch);
+
+  /**
+   * Updates a batch.
+   *
+   * @param array $batch
+   *   The array representing the batch to update.
+   */
+  public function update(array $batch);
+
+  /**
+   * Loads a batch.
+   *
+   * @param int $id
+   *   The ID of the batch to delete.
+   */
+  public function delete($id);
+
+  /**
+   * Cleans up failed or old batches.
+   */
+  public function cleanup();
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 0d472e5..3eee2fc 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1168,8 +1168,7 @@ function _system_batch_theme() {
   // Retrieve the current state of the batch.
   $batch = &batch_get();
   if (!$batch && isset($_REQUEST['id'])) {
-    require_once __DIR__ . '/../../includes/batch.inc';
-    $batch = batch_load($_REQUEST['id']);
+    $batch = Drupal::service('batch.storage')->load($_REQUEST['id']);
   }
   // Use the same theme as the page that started the batch.
   if (!empty($batch['theme'])) {
@@ -3517,10 +3516,7 @@ function system_cron() {
     $cache_backend->garbageCollection();
   }
 
-  // Cleanup the batch table and the queue for failed batches.
-  db_delete('batch')
-    ->condition('timestamp', REQUEST_TIME - 864000, '<')
-    ->execute();
+  // Cleanup the queue for failed batches.
   db_delete('queue')
     ->condition('created', REQUEST_TIME - 864000, '<')
     ->condition('name', 'drupal_batch:%', 'LIKE')
