diff --git a/acl.module b/acl.module
index 3d07833..3c06e86 100644
--- a/acl.module
+++ b/acl.module
@@ -9,7 +9,10 @@
  * other without having to actually know much about them, and so that
  * ACLs can easily co-exist with the existing node_access system.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\acl\Hook\AclHooks;
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\Core\Database\Statement\FetchAs;
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -145,7 +148,7 @@ function acl_add_nodes(SelectInterface $subselect, $acl_id, $view, $update, $del
     // The PostgreSQL and SQLite drivers currently fail to
     // generate the required parentheses around the subselect and
     // cause an error in their respective database systems.
-    $results = $subselect->execute()->fetchAll(PDO::FETCH_ASSOC);
+    $results = DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $subselect->execute()->fetchAll(FetchAs::Associative), fn() => $subselect->execute()->fetchAll(PDO::FETCH_ASSOC));
     if (!empty($results)) {
       $query = $database->insert('acl_node');
       $query->fields([
@@ -317,117 +320,44 @@ function acl_get_usernames($acl_id) {
 /**
  * Implements hook_node_access_records().
  */
-function acl_node_access_records(NodeInterface $node) {
-  if (!$node->id()) {
-    return;
-  }
-
-  $result = \Drupal::database()
-    ->query("SELECT n.*, 'acl' AS realm, n.acl_id AS gid, a.module FROM {acl_node} n INNER JOIN {acl} a ON n.acl_id = a.acl_id WHERE nid = :nid", [
-      'nid' => $node->id(),
-    ], ['fetch' => PDO::FETCH_ASSOC]);
-  $grants = [];
-  foreach ($result as $grant) {
-    if (\Drupal::moduleHandler()->invoke($grant['module'], 'enabled')) {
-      if (acl_has_users($grant['gid'])) {
-        $grants[] = $grant;
-      }
-      else {
-        // Just deny access.
-        $grants[] = [
-          'realm' => 'acl',
-          'gid' => $grant['gid'],
-          'grant_view' => 0,
-          'grant_update' => 0,
-          'grant_delete' => 0,
-          'priority' => $grant['priority'],
-        ];
-      }
-    }
-  }
-
-  return $grants;
+#[LegacyHook]
+function acl_node_access_records(NodeInterface $node)
+{
+    return \Drupal::service(AclHooks::class)->nodeAccessRecords($node);
 }
 
 /**
  * Implements hook_node_grants().
  */
-function acl_node_grants($account, $op) {
-  $acl_ids = \Drupal::database()
-    ->query("SELECT acl_id FROM {acl_user} WHERE uid = :uid", [
-      'uid' => $account->id(),
-    ])
-    ->fetchCol();
-
-  return (!empty($acl_ids) ? ['acl' => $acl_ids] : NULL);
+#[LegacyHook]
+function acl_node_grants($account, $op)
+{
+    return \Drupal::service(AclHooks::class)->nodeGrants($account, $op);
 }
 
 /**
  * Implements hook_node_delete().
  */
+#[LegacyHook]
 function acl_node_delete(NodeInterface $node) {
-  \Drupal::database()->delete('acl_node')
-    ->condition('nid', $node->id())
-    ->execute();
+  \Drupal::service(AclHooks::class)->nodeDelete($node);
 }
 
 /**
  * Implements hook_user_cancel().
  */
+#[LegacyHook]
 function acl_user_cancel($edit, AccountInterface $account, $method) {
-  \Drupal::database()->delete('acl_user')
-    ->condition('uid', $account->id())
-    ->execute();
+  \Drupal::service(AclHooks::class)->userCancel($edit, $account, $method);
 }
 
 /**
  * Implements hook_node_access_explain().
  */
-function acl_node_access_explain($row) {
-  static $interpretations = [];
-  $database = \Drupal::database();
-
-  if ($row->realm == 'acl') {
-    if (!isset($interpretations[$row->gid])) {
-      $acl = $database->query("SELECT * FROM {acl} WHERE acl_id = :acl_id", [
-        'acl_id' => $row->gid,
-      ])->fetchObject();
-      $acl->tag = '?';
-      if (!isset($acl->name)) {
-        $acl->tag = $acl->figure;
-      }
-      elseif (!isset($acl->figure)) {
-        $acl->tag = $acl->name;
-      }
-      else {
-        $acl->tag = $acl->name . '-' . $acl->figure;
-      }
-      $account_names = acl_get_usernames($row->gid);
-      if (!empty($account_names)) {
-        $account_names = implode(', ', $account_names);
-        $interpretations[$row->gid] = _acl_get_explanation("$acl->module/$acl->tag: $account_names", $acl->acl_id, $acl->module, $acl->name, $acl->figure, $account_names);
-      }
-      elseif ($row->gid == 0) {
-        $result = $database->query("SELECT an.acl_id, a.module, a.name FROM {acl_node} an JOIN {acl} a ON an.acl_id = a.acl_id LEFT JOIN {acl_user} au ON a.acl_id = au.acl_id WHERE an.nid = :nid AND au.uid IS NULL", [
-          'nid' => $row->nid,
-        ]);
-        foreach ($result as $acl) {
-          $rows[] = _acl_get_explanation("$acl->acl_id:&nbsp;$acl->module/$acl->tag", $acl->acl_id, $acl->module, $acl->name, $acl->figure);
-        }
-
-        if (!empty($rows)) {
-          return implode('<br />', $rows);
-        }
-
-        return 'No access via ACL.';
-      }
-      else {
-        $interpretations[$row->gid] = _acl_get_explanation("$acl->module/$acl->tag: no users!", $acl->acl_id, $acl->module, $acl->name, $acl->figure);
-      }
-    }
-
-    return $interpretations[$row->gid];
-  }
+#[LegacyHook]
+function acl_node_access_explain($row)
+{
+    return \Drupal::service(AclHooks::class)->nodeAccessExplain($row);
 }
 
 /**
diff --git a/acl.services.yml b/acl.services.yml
new file mode 100644
index 0000000..dc4598a
--- /dev/null
+++ b/acl.services.yml
@@ -0,0 +1,9 @@
+
+services:
+  Drupal\acl\Hook\AclHooks:
+    class: Drupal\acl\Hook\AclHooks
+    autowire: true
+
+  Drupal\acl\Hook\AclViewsHooks:
+    class: Drupal\acl\Hook\AclViewsHooks
+    autowire: true
diff --git a/acl.views.inc b/acl.views.inc
index 9b6cc30..0c9e4e9 100644
--- a/acl.views.inc
+++ b/acl.views.inc
@@ -1,4 +1,7 @@
 <?php
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\acl\Hook\AclViewsHooks;
+
 /**
  * @file
  * Views integration.
@@ -7,222 +10,16 @@
 /**
  * Implements hook_views_data().
  */
-function acl_views_data() {
-  $data = [];
-
-  $data['acl'] = [
-    'table' => [
-      'group' => t('ACL'),
-      'provider' => 'acl',
-      'base' => [
-        'field' => 'acl_id',
-        'title' => t('ACL'),
-        'weight' => 10,
-      ],
-    ],
-    'acl_id' => [
-      'title' => t('ACL Id'),
-      'help' => t('ACL Id: Appears in ACL'),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-    ],
-    'module' => [
-      'title' => t('ACL Module name'),
-      'help' => t('ACL Module: Appears in ACL'),
-      'field' => ['id' => 'standard'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'string'],
-      'argument' => ['id' => 'string'],
-    ],
-    'name' => [
-      'title' => t('ACL Name'),
-      'help' => t('ACL Name: Appears in ACL'),
-      'field' => ['id' => 'standard'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'string'],
-      'argument' => ['id' => 'string'],
-    ],
-    'figure' => [
-      'title' => t('ACL Figure (Number)'),
-      'help' => t('ACL Figure: Appears in ACL'),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-    ],
-    'acl_node' => [
-      'title' => t('Relationship to ACL Node'),
-      'help' => t('Relationship from ACL to ACL Node table'),
-      'relationship' => [
-        'base' => 'acl_node',
-        'base field' => 'acl_id',
-        'field' => 'acl_id',
-        'id' => 'standard',
-        'label' => t('ACL Node'),
-      ],
-    ],
-    'acl_user' => [
-      'title' => t('Relationship to ACL User'),
-      'help' => t('Relationship from ACL to ACL User table'),
-      'relationship' => [
-        'base' => 'acl_user',
-        'base field' => 'acl_id',
-        'field' => 'acl_id',
-        'id' => 'standard',
-        'label' => t('ACL User'),
-      ],
-    ],
-  ];
-
-  $data['acl_node'] = [
-    'table' => [
-      'group' => t('ACL'),
-      'provider' => 'acl',
-    ],
-    'acl_id' => [
-      'title' => t('ACL Id'),
-      'help' => t('ACL Id: Appears in ACL Node'),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-      'relationship' => [
-        'base' => 'acl',
-        'base field' => 'acl_id',
-        'id' => 'standard',
-        'label' => t('ACL'),
-        'help' => t('Relationship from ACL Node to ACL table'),
-      ],
-    ],
-    'nid' => [
-      'title' => t('Node Id'),
-      'help' => t('Node Id: Appears in ACL Node'),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-      'relationship' => [
-        'base' => 'node_field_data',
-        'base field' => 'nid',
-        'id' => 'standard',
-        'title' => t('Nid'),
-        'label' => t('Content'),
-        'help' => t('Relationship from ACL Node to Content using Node Id'),
-      ],
-    ],
-    'grant_view' => [
-      'title' => t("Grant 'view'"),
-      'help' => t("Grant 'view' : Appears in ACL Node"),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-    ],
-    'grant_update' => [
-      'title' => t("Grant 'update'"),
-      'help' => t("Grant 'update' : Appears in ACL Node"),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-    ],
-    'grant_delete' => [
-      'title' => t("Grant 'delete'"),
-      'help' => t("Grant 'delete' : Appears in ACL Node"),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-    ],
-    'priority' => [
-      'title' => t("Priority'"),
-      'help' => t("Priority : Appears in ACL Node"),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-    ],
-    'acl_user' => [
-      'title' => t('Relationship to ACL User'),
-      'help' => t('Relationship from ACL Node to ACL User table'),
-      'relationship' => [
-        'base' => 'acl_user',
-        'base field' => 'acl_id',
-        'field' => 'acl_id',
-        'id' => 'standard',
-        'label' => t('ACL User'),
-      ],
-    ],
-  ];
-
-  $data['acl_user'] = [
-    'table' => [
-      'group' => t('ACL'),
-      'provider' => 'acl',
-    ],
-    'acl_id' => [
-      'title' => t('ACL Id'),
-      'help' => t('ACL Id: Appears in ACL User'),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-      'relationship' => [
-        'base' => 'acl',
-        'base field' => 'acl_id',
-        'id' => 'standard',
-        'title' => t('ACL'),
-        'label' => t('ACL'),
-        'help' => t('Relationship from ACL User to ACL table'),
-      ],
-    ],
-    'uid' => [
-      'title' => t('User Id'),
-      'help' => t('User Id: Appears in ACL User'),
-      'field' => ['id' => 'numeric'],
-      'sort' => ['id' => 'standard'],
-      'filter' => ['id' => 'numeric'],
-      'argument' => ['id' => 'numeric'],
-      'relationship' => [
-        'base' => 'users_field_data',
-        'base field' => 'uid',
-        'id' => 'standard',
-        'title' => t('Uid'),
-        'label' => t('User'),
-        'help' => t('Relationship from ACL User to User using Uid'),
-      ],
-    ],
-    'acl_node' => [
-      'title' => t('Relationship to ACL Node'),
-      'help' => t('Relationship from ACL User to ACL Node table using ACL Id'),
-      'relationship' => [
-        'base' => 'acl_node',
-        'base field' => 'acl_id',
-        'field' => 'acl_id',
-        'id' => 'standard',
-        'label' => t('ACL Node'),
-      ],
-    ],
-  ];
-
-  return $data;
+#[LegacyHook]
+function acl_views_data()
+{
+    return \Drupal::service(AclViewsHooks::class)->viewsData();
 }
 
 /**
  * Implements hook_views_data_alter().
  */
+#[LegacyHook]
 function acl_views_data_alter(array &$data) {
-  $data['users']['acl_user'] = [
-    'title' => t('Relationship to ACL User'),
-    'help' => t('Relationship from User to ACL User table using Uid'),
-    'relationship' => [
-      'base' => 'acl_user',
-      'base field' => 'uid',
-      'field' => 'uid',
-      'id' => 'standard',
-      'label' => t('ACL User'),
-    ],
-  ];
+  \Drupal::service(AclViewsHooks::class)->viewsDataAlter($data);
 }
diff --git a/src/Hook/AclHooks.php b/src/Hook/AclHooks.php
new file mode 100644
index 0000000..0f4446b
--- /dev/null
+++ b/src/Hook/AclHooks.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Drupal\acl\Hook;
+
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\Core\Database\Statement\FetchAs;
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\node\NodeInterface;
+use Drupal\user\Entity\User;
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for acl.
+ */
+class AclHooks
+{
+    /**
+     * Implements hook_node_access_records().
+     */
+    #[Hook('node_access_records')]
+    public static function nodeAccessRecords(\Drupal\node\NodeInterface $node)
+    {
+        if (!$node->id()) {
+            return;
+        }
+        $result = \Drupal::database()->query("SELECT n.*, 'acl' AS realm, n.acl_id AS gid, a.module FROM {acl_node} n INNER JOIN {acl} a ON n.acl_id = a.acl_id WHERE nid = :nid", [
+            'nid' => $node->id(),
+        ], [
+            'fetch' => \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\Core\Database\Statement\FetchAs::Associative, fn() => \PDO::FETCH_ASSOC),
+        ]);
+        $grants = [
+        ];
+        foreach ($result as $grant) {
+            if (\Drupal::moduleHandler()->invoke($grant['module'], 'enabled')) {
+                if (acl_has_users($grant['gid'])) {
+                    $grants[] = $grant;
+                } else {
+                    // Just deny access.
+                    $grants[] = [
+                        'realm' => 'acl',
+                        'gid' => $grant['gid'],
+                        'grant_view' => 0,
+                        'grant_update' => 0,
+                        'grant_delete' => 0,
+                        'priority' => $grant['priority'],
+                    ];
+                }
+            }
+        }
+        return $grants;
+    }
+    /**
+     * Implements hook_node_grants().
+     */
+    #[Hook('node_grants')]
+    public static function nodeGrants($account, $op)
+    {
+        $acl_ids = \Drupal::database()->query("SELECT acl_id FROM {acl_user} WHERE uid = :uid", [
+            'uid' => $account->id(),
+        ])->fetchCol();
+        return !empty($acl_ids) ? [
+            'acl' => $acl_ids,
+        ] : NULL;
+    }
+    /**
+     * Implements hook_node_delete().
+     */
+    #[Hook('node_delete')]
+    public static function nodeDelete(\Drupal\node\NodeInterface $node)
+    {
+        \Drupal::database()->delete('acl_node')->condition('nid', $node->id())->execute();
+    }
+    /**
+     * Implements hook_user_cancel().
+     */
+    #[Hook('user_cancel')]
+    public static function userCancel($edit, \Drupal\Core\Session\AccountInterface $account, $method)
+    {
+        \Drupal::database()->delete('acl_user')->condition('uid', $account->id())->execute();
+    }
+    /**
+     * Implements hook_node_access_explain().
+     */
+    #[Hook('node_access_explain')]
+    public static function nodeAccessExplain($row)
+    {
+        static $interpretations = [
+        ];
+        $database = \Drupal::database();
+        if ($row->realm == 'acl') {
+            if (!isset($interpretations[$row->gid])) {
+                $acl = $database->query("SELECT * FROM {acl} WHERE acl_id = :acl_id", [
+                    'acl_id' => $row->gid,
+                ])->fetchObject();
+                $acl->tag = '?';
+                if (!isset($acl->name)) {
+                    $acl->tag = $acl->figure;
+                } elseif (!isset($acl->figure)) {
+                    $acl->tag = $acl->name;
+                } else {
+                    $acl->tag = $acl->name . '-' . $acl->figure;
+                }
+                $account_names = acl_get_usernames($row->gid);
+                if (!empty($account_names)) {
+                    $account_names = implode(', ', $account_names);
+                    $interpretations[$row->gid] = _acl_get_explanation("{$acl->module}/{$acl->tag}: {$account_names}", $acl->acl_id, $acl->module, $acl->name, $acl->figure, $account_names);
+                } elseif ($row->gid == 0) {
+                    $result = $database->query("SELECT an.acl_id, a.module, a.name FROM {acl_node} an JOIN {acl} a ON an.acl_id = a.acl_id LEFT JOIN {acl_user} au ON a.acl_id = au.acl_id WHERE an.nid = :nid AND au.uid IS NULL", [
+                        'nid' => $row->nid,
+                    ]);
+                    foreach ($result as $acl) {
+                        $rows[] = _acl_get_explanation("{$acl->acl_id}:&nbsp;{$acl->module}/{$acl->tag}", $acl->acl_id, $acl->module, $acl->name, $acl->figure);
+                    }
+                    if (!empty($rows)) {
+                        return implode('<br />', $rows);
+                    }
+                    return 'No access via ACL.';
+                } else {
+                    $interpretations[$row->gid] = _acl_get_explanation("{$acl->module}/{$acl->tag}: no users!", $acl->acl_id, $acl->module, $acl->name, $acl->figure);
+                }
+            }
+            return $interpretations[$row->gid];
+        }
+    }
+}
diff --git a/src/Hook/AclViewsHooks.php b/src/Hook/AclViewsHooks.php
new file mode 100644
index 0000000..d637106
--- /dev/null
+++ b/src/Hook/AclViewsHooks.php
@@ -0,0 +1,335 @@
+<?php
+
+namespace Drupal\acl\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+/**
+ * Hook implementations for acl.
+ */
+class AclViewsHooks
+{
+    use StringTranslationTrait;
+    /**
+     * @file
+     * Views integration.
+     */
+    /**
+     * Implements hook_views_data().
+     */
+    #[Hook('views_data')]
+    public function viewsData()
+    {
+        $data = [
+        ];
+        $data['acl'] = [
+            'table' => [
+                'group' => $this->t('ACL'),
+                'provider' => 'acl',
+                'base' => [
+                    'field' => 'acl_id',
+                    'title' => $this->t('ACL'),
+                    'weight' => 10,
+                ],
+            ],
+            'acl_id' => [
+                'title' => $this->t('ACL Id'),
+                'help' => $this->t('ACL Id: Appears in ACL'),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+            ],
+            'module' => [
+                'title' => $this->t('ACL Module name'),
+                'help' => $this->t('ACL Module: Appears in ACL'),
+                'field' => [
+                    'id' => 'standard',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'string',
+                ],
+                'argument' => [
+                    'id' => 'string',
+                ],
+            ],
+            'name' => [
+                'title' => $this->t('ACL Name'),
+                'help' => $this->t('ACL Name: Appears in ACL'),
+                'field' => [
+                    'id' => 'standard',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'string',
+                ],
+                'argument' => [
+                    'id' => 'string',
+                ],
+            ],
+            'figure' => [
+                'title' => $this->t('ACL Figure (Number)'),
+                'help' => $this->t('ACL Figure: Appears in ACL'),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+            ],
+            'acl_node' => [
+                'title' => $this->t('Relationship to ACL Node'),
+                'help' => $this->t('Relationship from ACL to ACL Node table'),
+                'relationship' => [
+                    'base' => 'acl_node',
+                    'base field' => 'acl_id',
+                    'field' => 'acl_id',
+                    'id' => 'standard',
+                    'label' => $this->t('ACL Node'),
+                ],
+            ],
+            'acl_user' => [
+                'title' => $this->t('Relationship to ACL User'),
+                'help' => $this->t('Relationship from ACL to ACL User table'),
+                'relationship' => [
+                    'base' => 'acl_user',
+                    'base field' => 'acl_id',
+                    'field' => 'acl_id',
+                    'id' => 'standard',
+                    'label' => $this->t('ACL User'),
+                ],
+            ],
+        ];
+        $data['acl_node'] = [
+            'table' => [
+                'group' => $this->t('ACL'),
+                'provider' => 'acl',
+            ],
+            'acl_id' => [
+                'title' => $this->t('ACL Id'),
+                'help' => $this->t('ACL Id: Appears in ACL Node'),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+                'relationship' => [
+                    'base' => 'acl',
+                    'base field' => 'acl_id',
+                    'id' => 'standard',
+                    'label' => $this->t('ACL'),
+                    'help' => $this->t('Relationship from ACL Node to ACL table'),
+                ],
+            ],
+            'nid' => [
+                'title' => $this->t('Node Id'),
+                'help' => $this->t('Node Id: Appears in ACL Node'),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+                'relationship' => [
+                    'base' => 'node_field_data',
+                    'base field' => 'nid',
+                    'id' => 'standard',
+                    'title' => $this->t('Nid'),
+                    'label' => $this->t('Content'),
+                    'help' => $this->t('Relationship from ACL Node to Content using Node Id'),
+                ],
+            ],
+            'grant_view' => [
+                'title' => $this->t("Grant 'view'"),
+                'help' => $this->t("Grant 'view' : Appears in ACL Node"),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+            ],
+            'grant_update' => [
+                'title' => $this->t("Grant 'update'"),
+                'help' => $this->t("Grant 'update' : Appears in ACL Node"),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+            ],
+            'grant_delete' => [
+                'title' => $this->t("Grant 'delete'"),
+                'help' => $this->t("Grant 'delete' : Appears in ACL Node"),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+            ],
+            'priority' => [
+                'title' => $this->t("Priority'"),
+                'help' => $this->t("Priority : Appears in ACL Node"),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+            ],
+            'acl_user' => [
+                'title' => $this->t('Relationship to ACL User'),
+                'help' => $this->t('Relationship from ACL Node to ACL User table'),
+                'relationship' => [
+                    'base' => 'acl_user',
+                    'base field' => 'acl_id',
+                    'field' => 'acl_id',
+                    'id' => 'standard',
+                    'label' => $this->t('ACL User'),
+                ],
+            ],
+        ];
+        $data['acl_user'] = [
+            'table' => [
+                'group' => $this->t('ACL'),
+                'provider' => 'acl',
+            ],
+            'acl_id' => [
+                'title' => $this->t('ACL Id'),
+                'help' => $this->t('ACL Id: Appears in ACL User'),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+                'relationship' => [
+                    'base' => 'acl',
+                    'base field' => 'acl_id',
+                    'id' => 'standard',
+                    'title' => $this->t('ACL'),
+                    'label' => $this->t('ACL'),
+                    'help' => $this->t('Relationship from ACL User to ACL table'),
+                ],
+            ],
+            'uid' => [
+                'title' => $this->t('User Id'),
+                'help' => $this->t('User Id: Appears in ACL User'),
+                'field' => [
+                    'id' => 'numeric',
+                ],
+                'sort' => [
+                    'id' => 'standard',
+                ],
+                'filter' => [
+                    'id' => 'numeric',
+                ],
+                'argument' => [
+                    'id' => 'numeric',
+                ],
+                'relationship' => [
+                    'base' => 'users_field_data',
+                    'base field' => 'uid',
+                    'id' => 'standard',
+                    'title' => $this->t('Uid'),
+                    'label' => $this->t('User'),
+                    'help' => $this->t('Relationship from ACL User to User using Uid'),
+                ],
+            ],
+            'acl_node' => [
+                'title' => $this->t('Relationship to ACL Node'),
+                'help' => $this->t('Relationship from ACL User to ACL Node table using ACL Id'),
+                'relationship' => [
+                    'base' => 'acl_node',
+                    'base field' => 'acl_id',
+                    'field' => 'acl_id',
+                    'id' => 'standard',
+                    'label' => $this->t('ACL Node'),
+                ],
+            ],
+        ];
+        return $data;
+    }
+    /**
+     * Implements hook_views_data_alter().
+     */
+    #[Hook('views_data_alter')]
+    public function viewsDataAlter(array &$data)
+    {
+        $data['users']['acl_user'] = [
+            'title' => $this->t('Relationship to ACL User'),
+            'help' => $this->t('Relationship from User to ACL User table using Uid'),
+            'relationship' => [
+                'base' => 'acl_user',
+                'base field' => 'uid',
+                'field' => 'uid',
+                'id' => 'standard',
+                'label' => $this->t('ACL User'),
+            ],
+        ];
+    }
+}
diff --git a/tests/modules/acl_node_test/acl_node_test.module b/tests/modules/acl_node_test/acl_node_test.module
index 444133a..f2a73d8 100644
--- a/tests/modules/acl_node_test/acl_node_test.module
+++ b/tests/modules/acl_node_test/acl_node_test.module
@@ -4,31 +4,26 @@
  * @file
  * Dummy module for test API interaction with the Node module.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\acl_node_test\Hook\AclNodeTestHooks;
+use Drupal\Component\Utility\DeprecationHelper;
 use Drupal\node\NodeInterface;
 
 /**
  * Implements hook_enabled().
  */
+#[LegacyHook]
 function acl_node_test_enabled() {
-  // Required for ACL hook_node_access_records().
-  return TRUE;
+  return \Drupal::service(AclNodeTestHooks::class)->enabled();
 }
 
 /**
  * Implements hook_node_grants().
  */
-function acl_node_test_node_grants($account, $op) {
-  // Give everyone full grants so we don't break other node tests.
-  // Our node access tests asserts three realms of access.
-  // See testGrantAlter().
-  $grants = [];
-  foreach (node_type_get_names() as $type) {
-    $grants['test_' . $type . '_realm'] = [1];
-  }
-  $grants['test_alter_realm'] = [2];
-
-  return $grants;
+#[LegacyHook]
+function acl_node_test_node_grants($account, $op)
+{
+    return \Drupal::service(AclNodeTestHooks::class)->nodeGrants($account, $op);
 }
 
 /**
@@ -36,35 +31,18 @@ function acl_node_test_node_grants($account, $op) {
  *
  * Implicit View by All is allowed.
  */
-function acl_node_test_node_access_records(NodeInterface $node) {
-  $grants = [];
-
-  $grants[] = [
-    'realm' => 'test_' . $node->getType() . '_realm',
-    'gid' => 1,
-    'grant_view' => 1,
-    'grant_update' => 0,
-    'grant_delete' => 0,
-    'priority' => 0,
-  ];
-
-  return $grants;
+#[LegacyHook]
+function acl_node_test_node_access_records(NodeInterface $node)
+{
+    return \Drupal::service(AclNodeTestHooks::class)->nodeAccessRecords($node);
 }
 
 /**
  * Implements hook_node_access_records_alter().
  */
+#[LegacyHook]
 function acl_node_test_node_access_records_alter(&$grants, NodeInterface $node) {
-  if (!empty($grants)) {
-    foreach ($grants as $key => $grant) {
-      // Alter grant from test_page_realm to test_alter_realm and modify
-      // the gid.
-      if ($grant['realm'] == 'test_test_alter_realm' && $node->isPromoted()) {
-        $grants[$key]['realm'] = 'test_alter_realm';
-        $grants[$key]['gid'] = 2;
-      }
-    }
-  }
+  \Drupal::service(AclNodeTestHooks::class)->nodeAccessRecordsAlter($grants, $node);
 }
 
 /**
@@ -72,18 +50,15 @@ function acl_node_test_node_access_records_alter(&$grants, NodeInterface $node)
  *
  * Returns an empty array of grants to prove that we can alter by reference.
  */
+#[LegacyHook]
 function acl_node_test_node_grants_alter(&$grants, $account, $op) {
-  $grants = [];
+  \Drupal::service(AclNodeTestHooks::class)->nodeGrantsAlter($grants, $account, $op);
 }
 
 /**
  * Implements hook_node_presave().
  */
+#[LegacyHook]
 function acl_node_test_node_presave(NodeInterface $node) {
-  if ($node->getTitle() == 'testing_node_presave') {
-    // Sun, 19 Nov 1978 05:00:00 GMT.
-    $node->setCreatedTime(280299600);
-    // Drupal 1.0 release.
-    $node->set('changed', 979534800);
-  }
+  \Drupal::service(AclNodeTestHooks::class)->nodePresave($node);
 }
diff --git a/tests/modules/acl_node_test/acl_node_test.services.yml b/tests/modules/acl_node_test/acl_node_test.services.yml
new file mode 100644
index 0000000..69cde82
--- /dev/null
+++ b/tests/modules/acl_node_test/acl_node_test.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\acl_node_test\Hook\AclNodeTestHooks:
+    class: Drupal\acl_node_test\Hook\AclNodeTestHooks
+    autowire: true
diff --git a/tests/modules/acl_node_test/src/Hook/AclNodeTestHooks.php b/tests/modules/acl_node_test/src/Hook/AclNodeTestHooks.php
new file mode 100644
index 0000000..89dba11
--- /dev/null
+++ b/tests/modules/acl_node_test/src/Hook/AclNodeTestHooks.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace Drupal\acl_node_test\Hook;
+
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\node\NodeInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for acl_node_test.
+ */
+class AclNodeTestHooks
+{
+    /**
+     * Implements hook_enabled().
+     */
+    #[Hook('enabled')]
+    public static function enabled()
+    {
+        // Required for ACL hook_node_access_records().
+        return TRUE;
+    }
+    /**
+     * Implements hook_node_grants().
+     */
+    #[Hook('node_grants')]
+    public static function nodeGrants($account, $op)
+    {
+        // Give everyone full grants so we don't break other node tests.
+        // Our node access tests asserts three realms of access.
+        // See testGrantAlter().
+        $grants = [
+        ];
+        foreach (\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service('entity_type.bundle.info')->getBundleLabels('node'), fn() => node_type_get_names()) as $type) {
+            $grants['test_' . $type . '_realm'] = [
+                1,
+            ];
+        }
+        $grants['test_alter_realm'] = [
+            2,
+        ];
+        return $grants;
+    }
+    /**
+     * Implements hook_node_access_records().
+     *
+     * Implicit View by All is allowed.
+     */
+    #[Hook('node_access_records')]
+    public static function nodeAccessRecords(\Drupal\node\NodeInterface $node)
+    {
+        $grants = [
+        ];
+        $grants[] = [
+            'realm' => 'test_' . $node->getType() . '_realm',
+            'gid' => 1,
+            'grant_view' => 1,
+            'grant_update' => 0,
+            'grant_delete' => 0,
+            'priority' => 0,
+        ];
+        return $grants;
+    }
+    /**
+     * Implements hook_node_access_records_alter().
+     */
+    #[Hook('node_access_records_alter')]
+    public static function nodeAccessRecordsAlter(&$grants, \Drupal\node\NodeInterface $node)
+    {
+        if (!empty($grants)) {
+            foreach ($grants as $key => $grant) {
+                // Alter grant from test_page_realm to test_alter_realm and modify
+                // the gid.
+                if ($grant['realm'] == 'test_test_alter_realm' && $node->isPromoted()) {
+                    $grants[$key]['realm'] = 'test_alter_realm';
+                    $grants[$key]['gid'] = 2;
+                }
+            }
+        }
+    }
+    /**
+     * Implements hook_node_grants_alter().
+     *
+     * Returns an empty array of grants to prove that we can alter by reference.
+     */
+    #[Hook('node_grants_alter')]
+    public static function nodeGrantsAlter(&$grants, $account, $op)
+    {
+        $grants = [
+        ];
+    }
+    /**
+     * Implements hook_node_presave().
+     */
+    #[Hook('node_presave')]
+    public static function nodePresave(\Drupal\node\NodeInterface $node)
+    {
+        if ($node->getTitle() == 'testing_node_presave') {
+            // Sun, 19 Nov 1978 05:00:00 GMT.
+            $node->setCreatedTime(280299600);
+            // Drupal 1.0 release.
+            $node->set('changed', 979534800);
+        }
+    }
+}
