diff --git a/acl.module b/acl.module
index 3d07833..125c90f 100644
--- a/acl.module
+++ b/acl.module
@@ -10,6 +10,10 @@
  * 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 +149,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 +321,41 @@ function acl_get_usernames($acl_id) {
 /**
  * Implements hook_node_access_records().
  */
+#[LegacyHook]
 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;
+  return \Drupal::service(AclHooks::class)->nodeAccessRecords($node);
 }
 
 /**
  * Implements hook_node_grants().
  */
+#[LegacyHook]
 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);
+  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().
  */
+#[LegacyHook]
 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];
-  }
+  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..fa27495 100644
--- a/acl.views.inc
+++ b/acl.views.inc
@@ -1,4 +1,12 @@
 <?php
+
+/**
+ * @file
+ */
+
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\acl\Hook\AclViewsHooks;
+
 /**
  * @file
  * Views integration.
@@ -7,222 +15,15 @@
 /**
  * Implements hook_views_data().
  */
+#[LegacyHook]
 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;
+  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..e6255b2
--- /dev/null
+++ b/src/Hook/AclHooks.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace Drupal\acl\Hook;
+
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Database\Statement\FetchAs;
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\node\NodeInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for acl.
+ */
+class AclHooks {
+
+  /**
+   * Implements hook_node_access_records().
+   */
+  #[Hook('node_access_records')]
+  public function nodeAccessRecords(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' => DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => 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 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 function nodeDelete(NodeInterface $node) {
+    \Drupal::database()->delete('acl_node')->condition('nid', $node->id())->execute();
+  }
+
+  /**
+   * Implements hook_user_cancel().
+   */
+  #[Hook('user_cancel')]
+  public function userCancel($edit, AccountInterface $account, $method) {
+    \Drupal::database()->delete('acl_user')->condition('uid', $account->id())->execute();
+  }
+
+  /**
+   * Implements hook_node_access_explain().
+   */
+  #[Hook('node_access_explain')]
+  public 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..f43dd3d
--- /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..9b4d175 100644
--- a/tests/modules/acl_node_test/acl_node_test.module
+++ b/tests/modules/acl_node_test/acl_node_test.module
@@ -5,30 +5,24 @@
  * 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\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().
  */
+#[LegacyHook]
 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;
+  return \Drupal::service(AclNodeTestHooks::class)->nodeGrants($account, $op);
 }
 
 /**
@@ -36,35 +30,17 @@ function acl_node_test_node_grants($account, $op) {
  *
  * Implicit View by All is allowed.
  */
+#[LegacyHook]
 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;
+  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 +48,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..e57337d
--- /dev/null
+++ b/tests/modules/acl_node_test/src/Hook/AclNodeTestHooks.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\acl_node_test\Hook;
+
+use Drupal\node\NodeInterface;
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for acl_node_test.
+ */
+class AclNodeTestHooks {
+
+  /**
+   * Implements hook_enabled().
+   */
+  #[Hook('enabled')]
+  public function enabled() {
+    // Required for ACL hook_node_access_records().
+    return TRUE;
+  }
+
+  /**
+   * Implements hook_node_grants().
+   */
+  #[Hook('node_grants')]
+  public 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 (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 function nodeAccessRecords(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 function nodeAccessRecordsAlter(&$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;
+        }
+      }
+    }
+  }
+
+  /**
+   * Implements hook_node_grants_alter().
+   *
+   * Returns an empty array of grants to prove that we can alter by reference.
+   */
+  #[Hook('node_grants_alter')]
+  public function nodeGrantsAlter(&$grants, $account, $op) {
+    $grants = [];
+  }
+
+  /**
+   * Implements hook_node_presave().
+   */
+  #[Hook('node_presave')]
+  public function nodePresave(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);
+    }
+  }
+
+}
diff --git a/tests/src/Functional/AclTest.php b/tests/src/Functional/AclTest.php
index a29ade7..b4093b5 100644
--- a/tests/src/Functional/AclTest.php
+++ b/tests/src/Functional/AclTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\acl\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\node\Entity\NodeType;
 use Drupal\node\Entity\Node;
 use Drupal\Tests\BrowserTestBase;
@@ -11,6 +13,8 @@ use Drupal\Tests\BrowserTestBase;
  *
  * @group Access
  */
+#[Group('Access')]
+#[RunTestsInSeparateProcesses]
 class AclTest extends BrowserTestBase {
 
   /**
diff --git a/tests/src/Kernel/Migrate/d6/MigrateAclList68Test.php b/tests/src/Kernel/Migrate/d6/MigrateAclList68Test.php
index 524000a..b550fff 100644
--- a/tests/src/Kernel/Migrate/d6/MigrateAclList68Test.php
+++ b/tests/src/Kernel/Migrate/d6/MigrateAclList68Test.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\acl\Kernel\Migrate\d6;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\acl\Kernel\Migrate\AclMigrationTestTrait;
 use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
 
@@ -10,6 +12,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
  *
  * @group acl
  */
+#[Group('acl')]
+#[RunTestsInSeparateProcesses]
 class MigrateAclList68Test extends MigrateDrupal6TestBase {
 
   use AclMigrationTestTrait;
diff --git a/tests/src/Kernel/Migrate/d7/MigrateAclList78Test.php b/tests/src/Kernel/Migrate/d7/MigrateAclList78Test.php
index cef689c..ef70dea 100644
--- a/tests/src/Kernel/Migrate/d7/MigrateAclList78Test.php
+++ b/tests/src/Kernel/Migrate/d7/MigrateAclList78Test.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\acl\Kernel\Migrate\d7;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Tests\acl\Kernel\Migrate\AclMigrationTestTrait;
 use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
@@ -11,6 +13,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
  *
  * @group acl
  */
+#[Group('acl')]
+#[RunTestsInSeparateProcesses]
 class MigrateAclList78Test extends MigrateDrupal7TestBase {
 
   use AclMigrationTestTrait;
