diff --git a/modules/search_api_db/search_api_db.module b/modules/search_api_db/search_api_db.module
new file mode 100644
index 0000000..adc9f5a
--- /dev/null
+++ b/modules/search_api_db/search_api_db.module
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Contains hook implementations for the Database Search module.
+ */
+
+/**
+ * Implements hook_hook_info().
+ */
+function search_api_db_hook_info() {
+  return [
+    'search_api_db_query_alter' => [
+      'group' => 'search_api',
+    ],
+  ];
+}
diff --git a/search_api.module b/search_api.module
index 0eb6bb1..3faebf2 100644
--- a/search_api.module
+++ b/search_api.module
@@ -135,6 +135,38 @@ function search_api_cron() {
 }
 
 /**
+ * Implements hook_hook_info().
+ */
+function search_api_hook_info() {
+  $hooks = [
+    'search_api_backend_info_alter',
+    'search_api_server_features_alter',
+    'search_api_datasource_info_alter',
+    'search_api_processor_info_alter',
+    'search_api_data_type_info_alter',
+    'search_api_parse_mode_info_alter',
+    'search_api_tracker_info_alter',
+    'search_api_displays_alter',
+    'search_api_field_type_mapping_alter',
+    'search_api_views_handler_mapping_alter',
+    'search_api_views_field_handler_mapping_alter',
+    'search_api_index_items_alter',
+    'search_api_items_indexed',
+    'search_api_query_alter',
+    // Unfortunately, it's not possible to add hook infos for hooks with
+    // "wildcards".
+//    'search_api_query_TAG_alter',
+    'search_api_results_alter',
+//    'search_api_results_TAG_alter',
+    'search_api_index_reindex',
+  ];
+  $info = [
+    'group' => 'search_api',
+  ];
+  return array_fill_keys($hooks, $info);
+}
+
+/**
  * Implements hook_config_import_steps_alter().
  */
 function search_api_config_import_steps_alter(&$sync_steps, ConfigImporter $config_importer) {
@@ -372,7 +404,7 @@ function search_api_theme() {
 }
 
 /**
- * Implements hook_search_api_index_update().
+ * Implements hook_ENTITY_TYPE_update() for type "search_api_index".
  *
  * Implemented on behalf of the "entity" datasource plugin.
  *
diff --git a/tests/search_api_test_db/search_api_test_db.module b/tests/search_api_test_db/search_api_test_db.module
deleted file mode 100644
index 3831c16..0000000
--- a/tests/search_api_test_db/search_api_test_db.module
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/**
- * @file
- * Provides a setup for testing Database Search.
- */
-
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\search_api\Query\QueryInterface;
-
-/**
- * Implements hook_search_api_db_query_alter().
- */
-function search_api_test_db_search_api_db_query_alter(SelectInterface &$db_query, QueryInterface $query) {
-  if ($query->getOption('search_api_test_db_search_api_db_query_alter')) {
-    $db_query->where('0 = 1');
-  }
-}
diff --git a/tests/search_api_test_db/search_api_test_db.search_api.inc b/tests/search_api_test_db/search_api_test_db.search_api.inc
new file mode 100644
index 0000000..7854ff4
--- /dev/null
+++ b/tests/search_api_test_db/search_api_test_db.search_api.inc
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Contains hook implementations for testing Database Search.
+ */
+
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\search_api\Query\QueryInterface;
+
+/**
+ * Implements hook_search_api_db_query_alter().
+ */
+function search_api_test_db_search_api_db_query_alter(SelectInterface &$db_query, QueryInterface $query) {
+  if ($query->getOption('search_api_test_db_search_api_db_query_alter')) {
+    $db_query->where('0 = 1');
+  }
+}
diff --git a/tests/search_api_test_hooks/search_api_test_hooks.module b/tests/search_api_test_hooks/search_api_test_hooks.module
index 15c1b07..6d3a225 100644
--- a/tests/search_api_test_hooks/search_api_test_hooks.module
+++ b/tests/search_api_test_hooks/search_api_test_hooks.module
@@ -5,92 +5,11 @@
  * Tests all the hooks defined by the Search API module.
  */
 
-use Drupal\search_api\IndexInterface;
 use Drupal\search_api\Query\QueryInterface;
 use Drupal\search_api\Query\ResultSetInterface;
-use Drupal\search_api\ServerInterface;
 use Drupal\search_api\Utility\Utility;
 
 /**
- * Implements hook_search_api_backend_info_alter().
- */
-function search_api_test_hooks_search_api_backend_info_alter(array &$backend_info) {
-  $backend_info['search_api_test']['label'] = 'Slims return';
-}
-
-/**
- * Implements hook_search_api_datasource_info_alter().
- */
-function search_api_test_hooks_search_api_datasource_info_alter(array &$infos) {
-  if (isset($infos['entity:node'])) {
-    $infos['entity:node']['label'] = 'Distant land';
-  }
-}
-
-/**
- * Implements hook_search_api_parse_mode_info_alter().
- */
-function search_api_test_hooks_search_api_parse_mode_info_alter(array &$parse_modes) {
-  $parse_modes['direct']['label'] = 'Song for My Father';
-}
-
-/**
- * Implements hook_search_api_server_features_alter().
- */
-function search_api_test_hooks_search_api_server_features_alter(array &$features, ServerInterface $server) {
-  if ($server->id() == 'webtest_server') {
-    $features[] = 'welcome_to_the_jungle';
-  }
-}
-
-/**
- * Implements hook_search_api_processor_info_alter().
- */
-function search_api_test_hooks_search_api_processor_info_alter(array &$processors) {
-  $processors['content_access']['label'] = 'Mystic bounce';
-}
-
-/**
- * Implements hook_search_api_tracker_info_alter.
- */
-function search_api_test_hooks_search_api_tracker_info_alter(array &$tracker_info) {
-  $tracker_info['search_api_test']['label'] = 'Good luck';
-}
-
-/**
- * Implements hook_search_api_displays_alter
- */
-function search_api_test_hooks_search_api_displays_alter(array &$displays) {
-  if (isset($displays['views_page:search_api_test_view__page_1'])) {
-    $displays['views_page:search_api_test_view__page_1']['label'] = t('Some funny label for testing');
-  }
-}
-
-/**
- * Implements hook_search_api_index_items_alter().
- */
-function search_api_test_hooks_search_api_index_items_alter(IndexInterface $index, array &$items) {
-  unset($items['entity:node/1:en']);
-  drupal_set_message('Stormy');
-}
-
-/**
- * Implements hook_search_api_items_indexed().
- */
-function search_api_test_hooks_search_api_items_indexed(IndexInterface $index, array $item_ids) {
-  drupal_set_message('Please set me at ease');
-}
-
-/**
- * Implements hook_search_api_query_alter().
- */
-function search_api_test_hooks_search_api_query_alter(QueryInterface &$query) {
-  drupal_set_message('Funky blue note');
-  drupal_set_message(sprintf('Search id: %s', $query->getSearchId(FALSE)));
-  $query->addTag('andrew_hill');
-}
-
-/**
  * Implements hook_search_api_query_TAG_alter().
  */
 function search_api_test_hooks_search_api_query_andrew_hill_alter(QueryInterface &$query) {
@@ -107,41 +26,9 @@ function search_api_test_hooks_search_api_query_andrew_hill_alter(QueryInterface
     }
   }
 }
-
-/**
- * Implements hook_search_api_results_alter().
- */
-function search_api_test_hooks_search_api_results_alter(ResultSetInterface &$results) {
-  drupal_set_message('Stepping into tomorrow');
-}
-
 /**
  * Implements hook_search_api_results_TAG_alter().
  */
 function search_api_test_hooks_search_api_results_andrew_hill_alter(ResultSetInterface &$results) {
   drupal_set_message('Llama');
 }
-
-/**
- * Implements hook_search_api_index_reindex().
- */
-function search_api_test_hooks_search_api_index_reindex(IndexInterface $index, $clear = FALSE) {
-  drupal_set_message('Montara');
-}
-
-/**
- * Implements hook_search_api_data_type_info_alter().
- */
-function search_api_test_hooks_search_api_data_type_info_alter(array &$data_type_definitions) {
-  if (isset($data_type_definitions['text'])) {
-    $data_type_definitions['text']['label'] = 'Peace/Dolphin dance';
-  }
-}
-
-/**
- * Implements hook_search_api_field_type_mapping_alter().
- */
-function search_api_test_hooks_search_api_field_type_mapping_alter(array &$mapping) {
-  $mapping['datetime_iso8601'] = FALSE;
-  $mapping['timestamp'] = FALSE;
-}
diff --git a/tests/search_api_test_hooks/search_api_test_hooks.search_api.inc b/tests/search_api_test_hooks/search_api_test_hooks.search_api.inc
new file mode 100644
index 0000000..73a8d2e
--- /dev/null
+++ b/tests/search_api_test_hooks/search_api_test_hooks.search_api.inc
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * @file
+ * Tests all the hooks defined by the Search API module.
+ */
+
+use Drupal\search_api\IndexInterface;
+use Drupal\search_api\Query\QueryInterface;
+use Drupal\search_api\Query\ResultSetInterface;
+use Drupal\search_api\ServerInterface;
+use Drupal\search_api\Utility\Utility;
+
+/**
+ * Implements hook_search_api_backend_info_alter().
+ */
+function search_api_test_hooks_search_api_backend_info_alter(array &$backend_info) {
+  $backend_info['search_api_test']['label'] = 'Slims return';
+}
+
+/**
+ * Implements hook_search_api_datasource_info_alter().
+ */
+function search_api_test_hooks_search_api_datasource_info_alter(array &$infos) {
+  if (isset($infos['entity:node'])) {
+    $infos['entity:node']['label'] = 'Distant land';
+  }
+}
+
+/**
+ * Implements hook_search_api_parse_mode_info_alter().
+ */
+function search_api_test_hooks_search_api_parse_mode_info_alter(array &$parse_modes) {
+  $parse_modes['direct']['label'] = 'Song for My Father';
+}
+
+/**
+ * Implements hook_search_api_server_features_alter().
+ */
+function search_api_test_hooks_search_api_server_features_alter(array &$features, ServerInterface $server) {
+  if ($server->id() == 'webtest_server') {
+    $features[] = 'welcome_to_the_jungle';
+  }
+}
+
+/**
+ * Implements hook_search_api_processor_info_alter().
+ */
+function search_api_test_hooks_search_api_processor_info_alter(array &$processors) {
+  $processors['content_access']['label'] = 'Mystic bounce';
+}
+
+/**
+ * Implements hook_search_api_tracker_info_alter.
+ */
+function search_api_test_hooks_search_api_tracker_info_alter(array &$tracker_info) {
+  $tracker_info['search_api_test']['label'] = 'Good luck';
+}
+
+/**
+ * Implements hook_search_api_displays_alter
+ */
+function search_api_test_hooks_search_api_displays_alter(array &$displays) {
+  if (isset($displays['views_page:search_api_test_view__page_1'])) {
+    $displays['views_page:search_api_test_view__page_1']['label'] = t('Some funny label for testing');
+  }
+}
+
+/**
+ * Implements hook_search_api_index_items_alter().
+ */
+function search_api_test_hooks_search_api_index_items_alter(IndexInterface $index, array &$items) {
+  unset($items['entity:node/1:en']);
+  drupal_set_message('Stormy');
+}
+
+/**
+ * Implements hook_search_api_items_indexed().
+ */
+function search_api_test_hooks_search_api_items_indexed(IndexInterface $index, array $item_ids) {
+  drupal_set_message('Please set me at ease');
+}
+
+/**
+ * Implements hook_search_api_query_alter().
+ */
+function search_api_test_hooks_search_api_query_alter(QueryInterface &$query) {
+  drupal_set_message('Funky blue note');
+  drupal_set_message(sprintf('Search id: %s', $query->getSearchId(FALSE)));
+  $query->addTag('andrew_hill');
+}
+
+/**
+ * Implements hook_search_api_results_alter().
+ */
+function search_api_test_hooks_search_api_results_alter(ResultSetInterface &$results) {
+  drupal_set_message('Stepping into tomorrow');
+}
+
+/**
+ * Implements hook_search_api_index_reindex().
+ */
+function search_api_test_hooks_search_api_index_reindex(IndexInterface $index, $clear = FALSE) {
+  drupal_set_message('Montara');
+}
+
+/**
+ * Implements hook_search_api_data_type_info_alter().
+ */
+function search_api_test_hooks_search_api_data_type_info_alter(array &$data_type_definitions) {
+  if (isset($data_type_definitions['text'])) {
+    $data_type_definitions['text']['label'] = 'Peace/Dolphin dance';
+  }
+}
+
+/**
+ * Implements hook_search_api_field_type_mapping_alter().
+ */
+function search_api_test_hooks_search_api_field_type_mapping_alter(array &$mapping) {
+  $mapping['datetime_iso8601'] = FALSE;
+  $mapping['timestamp'] = FALSE;
+}
