diff --git a/core/modules/statistics/src/StatisticsStorage.php b/core/modules/statistics/src/StatisticsStorage.php
new file mode 100644
index 0000000..04ab1a4
--- /dev/null
+++ b/core/modules/statistics/src/StatisticsStorage.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\statistics;
+
+use Drupal\Core\Database\Connection;
+
+class StatisticsStorage implements StatisticsStorageInterface {
+
+  /**
+   * The database connection used.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * Construct the statistics storage.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection for the node view storage.
+   */
+  public function __construct(Connection $connection) {
+    $this->connection = $connection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function recordHit($nid) {
+    return (bool) $this->connection->merge('node_counter')
+      ->key('nid', $nid)
+      ->fields(array(
+        'daycount' => 1,
+        'totalcount' => 1,
+        'timestamp' => REQUEST_TIME,
+      ))
+      ->expression('daycount', 'daycount + 1')
+      ->expression('totalcount', 'totalcount + 1')
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetchViews($nid) {
+    if ($nid > 0) {
+      // Retrieve an array with both totalcount and daycount.
+      return $this->connection->select('node_counter', 'nc')
+        ->fields('nc', array('totalcount', 'daycount', 'timestamp'))
+        ->condition('nid', $nid, '=')->execute()->fetchAssoc();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clean($nid) {
+    return (bool) $this->connection->delete('node_counter')
+      ->condition('nid', $nid)
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetDayCount() {
+    return (bool) $this->connection->update('node_counter')
+      ->fields(array('daycount' => 0))
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function maxTotalCount() {
+    $q = $this->connection->select('node_counter', 'nc');
+    $q->addExpression('MAX(totalcount)');
+    return (int) $q->execute()->fetchField();
+  }
+}
\ No newline at end of file
diff --git a/core/modules/statistics/src/StatisticsStorageInterface.php b/core/modules/statistics/src/StatisticsStorageInterface.php
new file mode 100644
index 0000000..601cf2b
--- /dev/null
+++ b/core/modules/statistics/src/StatisticsStorageInterface.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\statistics\StatisticsStorageInterface.
+ */
+
+namespace Drupal\statistics;
+
+/**
+ * Provides an interface defining Statistics Storage
+ */
+interface StatisticsStorageInterface {
+
+  /**
+   * Returns if node view is counted
+   *
+   * @param int $nid
+   *   The id of the node to count
+   *
+   * @return bool
+   *   TRUE if the node view has been counted
+   */
+  public function recordHit($nid);
+  
+  /**
+   * Returns the number of times a node has been viewed
+   *
+   * @param int $nid
+   *   The id of the node to fetch the views for
+   *
+ * @return array
+ *   An associative array containing:
+ *   - totalcount: Integer for the total number of times the node has been
+ *     viewed.
+ *   - daycount: Integer for the total number of times the node has been viewed
+ *     "today". For the daycount to be reset, cron must be enabled.
+ *   - timestamp: Integer for the timestamp of when the node was last viewed.
+   */
+  public function fetchViews($nid);
+
+  /**
+   * Returns if node view counts are delete
+   *
+   * @param int $nid
+   *   The id of the node which views to delete
+   *
+   * @return bool
+   *   TRUE if the node views have been deleted
+   */
+  public function clean($nid);
+
+  /**
+   * Returns if day count is reset
+   *
+   * @return bool
+   *  TRUE if the day count is reset
+   */
+  public function resetDayCount();
+
+  /**
+   * Returns the highest 'totalcount' value
+   *
+   * @return int
+   *   The highest 'totalcount' value
+   */
+  public function maxTotalCount();
+}
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index 9b3be36..7e94d25 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -68,18 +68,18 @@ function statistics_node_links_alter(array &$node_links, NodeInterface $entity,
  * Implements hook_cron().
  */
 function statistics_cron() {
+  $storage = \Drupal::service('statistics.statistics_storage');
   $statistics_timestamp = \Drupal::state()->get('statistics.day_timestamp') ?: 0;
 
   if ((REQUEST_TIME - $statistics_timestamp) >= 86400) {
-    // Reset day counts.
-    db_update('node_counter')
-      ->fields(array('daycount' => 0))
-      ->execute();
+    // Reset day counts
+    $storage->resetDayCount();
     \Drupal::state()->set('statistics.day_timestamp', REQUEST_TIME);
   }
 
   // Calculate the maximum of node views, for node search ranking.
-  \Drupal::state()->set('statistics.node_counter_scale', 1.0 / max(1.0, db_query('SELECT MAX(totalcount) FROM {node_counter}')->fetchField()));
+  $max_total_count = $storage->maxTotalCount();
+  \Drupal::state()->set('statistics.node_counter_scale', 1.0 / max(1.0, $max_total_count));
 }
 
 /**
@@ -137,10 +137,9 @@ function statistics_title_list($dbfield, $dbrows) {
  *   - timestamp: Integer for the timestamp of when the node was last viewed.
  */
 function statistics_get($nid) {
-
   if ($nid > 0) {
-    // Retrieve an array with both totalcount and daycount.
-    return db_query('SELECT totalcount, daycount, timestamp FROM {node_counter} WHERE nid = :nid', array(':nid' => $nid), array('target' => 'replica'))->fetchAssoc();
+    $storage = \Drupal::service('statistics.statistics_storage');
+    return $storage->fetchViews($nid);
   }
 }
 
@@ -149,9 +148,9 @@ function statistics_get($nid) {
  */
 function statistics_node_predelete(EntityInterface $node) {
   // Clean up statistics table when node is deleted.
-  db_delete('node_counter')
-    ->condition('nid', $node->id())
-    ->execute();
+  $nid = $node->id();
+  $storage = \Drupal::service('statistics.statistics_storage');
+  return $storage->clean($nid);
 }
 
 /**
diff --git a/core/modules/statistics/statistics.php b/core/modules/statistics/statistics.php
index fe1b9fd..b83a10f 100644
--- a/core/modules/statistics/statistics.php
+++ b/core/modules/statistics/statistics.php
@@ -23,16 +23,7 @@
 if ($views) {
   $nid = filter_input(INPUT_POST, 'nid', FILTER_VALIDATE_INT);
   if ($nid) {
-    \Drupal::database()->merge('node_counter')
-      ->key('nid', $nid)
-      ->fields(array(
-        'daycount' => 1,
-        'totalcount' => 1,
-        'timestamp' => REQUEST_TIME,
-      ))
-      ->expression('daycount', 'daycount + 1')
-      ->expression('totalcount', 'totalcount + 1')
-      ->execute();
+    $kernel->getContainer()->get('statistics.statistics_storage')->recordHit($nid);
   }
 }
 
diff --git a/core/modules/statistics/statistics.services.yml b/core/modules/statistics/statistics.services.yml
new file mode 100644
index 0000000..4f553e0
--- /dev/null
+++ b/core/modules/statistics/statistics.services.yml
@@ -0,0 +1,6 @@
+services:
+  statistics.statistics_storage:
+    class: Drupal\statistics\StatisticsStorage
+    arguments: ['@database']
+    tags:
+      - { name: backend_overridable }
\ No newline at end of file
