diff --git a/node_authlink.install b/node_authlink.install
index 6b86b6d..e2094ee 100644
--- a/node_authlink.install
+++ b/node_authlink.install
@@ -1,6 +1,9 @@
 <?php
 
-use Drupal\Core\Messenger\MessengerInterface;
+/**
+ * @file
+ */
+
 use Drupal\Core\Serialization\Yaml;
 use Drupal\Component\Uuid\Php as Uuid;
 use Drupal\views\Entity\View;
@@ -62,7 +65,7 @@ function node_authlink_update_10201() {
   }
 
   $config->set('grants', $grants);
-  $config->save(TRUE);
+  $config->save();
 }
 
 /**
@@ -73,7 +76,7 @@ function node_authlink_update_10202() {
     $config_name = 'views.view.node_authlinks';
     $config_path = \Drupal::service('extension.list.module')->getPath('node_authlink') . '/config/optional/' . $config_name . '.yml';
     $data = ['uuid' => (new Uuid())->generate()] + Yaml::decode(file_get_contents($config_path));
-    \Drupal::configFactory()->getEditable($config_name)->setData($data)->save(TRUE);
+    \Drupal::configFactory()->getEditable($config_name)->setData($data)->save();
     $message = 'The new node_authlink list view has been created.';
   }
   else {
diff --git a/node_authlink.module b/node_authlink.module
index a38bee2..f69c190 100644
--- a/node_authlink.module
+++ b/node_authlink.module
@@ -5,8 +5,8 @@
  * Node Authlink hooks and alters.
  */
 
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\node_authlink\Hook\NodeAuthlinkHooks;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\BubbleableMetadata;
@@ -15,8 +15,6 @@ use Drupal\Core\Url;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
 use Drupal\node\NodeInterface;
-use Drupal\node_authlink\Access\NodeAuthlinkNodeAccessControlHandler;
-use Drupal\node_authlink\Plugin\NodeAuthlinkGroupContentAccessControlHandler;
 
 /**
  * Alter of node_type_form.
@@ -198,21 +196,16 @@ function node_authlink_batch_delete(array &$form, FormStateInterface &$form_stat
  *
  * Appends authkey to loaded node object.
  */
+#[LegacyHook]
 function node_authlink_node_load($nodes) {
-  foreach ($nodes as $nid => $node) {
-    // @todo check node type (performance)
-    if ($authkey = node_authlink_load_authkey($nid)) {
-      $nodes[$nid]->authkey = $authkey;
-    }
-  }
+  \Drupal::service(NodeAuthlinkHooks::class)->nodeLoad($nodes);
 }
 
 /**
  * Loads key from NID.
  *
- * @return NULL|string
+ * @return null|string
  *   The authkey if it's found, NULL if not.
- *
  */
 function node_authlink_load_authkey($nid) {
   $authkey = NULL;
@@ -235,7 +228,7 @@ function node_authlink_load_authkey($nid) {
  *   Operation to do with node. view, view revision, update (default) or delete.
  * @param null|int $revision_id
  *
- * @return NULL|Drupal\Core\Url
+ * @return null|Drupal\Core\Url
  *   The requested URL or NULL if the authkey is not yet set or the requested
  *   operation is unknown.
  */
@@ -287,23 +280,9 @@ function node_authlink_get_url($node, $op = 'view', $revision_id = NULL) {
 /**
  * Implements hook_node_access().
  */
+#[LegacyHook]
 function node_authlink_node_access(NodeInterface $node, $op, AccountInterface $account) {
-  // Ignore if just creating node.
-  if ($op == 'create') {
-    return AccessResult::neutral();
-  }
-
-  // Ignore if node type is not enabled.
-  // Or the node does not have authkey.
-  if (!node_authlink_node_is_enabled($node)
-    || !isset($node->authkey)) {
-    return AccessResult::neutral();
-  }
-
-  if (node_authlink_check_authlink($node, $op, $account)) {
-    return AccessResult::allowed();
-  }
-
+  return \Drupal::service(NodeAuthlinkHooks::class)->nodeAccess($node, $op, $account);
 }
 
 /**
@@ -374,13 +353,9 @@ function node_authlink_check_authlink(NodeInterface $node, $op, AccountInterface
 /**
  * Implements hook_ENTITY_TYPE_delete().
  */
+#[LegacyHook]
 function node_authlink_node_delete(EntityInterface $entity) {
-  $config = \Drupal::config('node_authlink.settings');
-  $enable = $config->get('enable');
-  // Ignore if node type is disabled.
-  if (isset($enable[$entity->bundle()]) && $enable[$entity->bundle()]) {
-    node_authlink_delete($entity->id());
-  }
+  \Drupal::service(NodeAuthlinkHooks::class)->nodeDelete($entity);
 }
 
 /**
@@ -503,64 +478,25 @@ function node_authlink_token_info() {
 /**
  * Implements hook_tokens().
  */
+#[LegacyHook]
 function node_authlink_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
-
-  $replacements = [];
-
-  if ($type != 'node' || empty($data['node'])) {
-    return $replacements;
-  }
-
-
-
-  if ($type == 'node' && !empty($data['node']->authkey)) {
-    $node = $data['node'];
-
-    if (!($node instanceof NodeInterface)) {
-      return $replacements;
-    }
-
-    foreach ($tokens as $name => $original) {
-      switch ($name) {
-        case 'authlink:authkey':
-          $replacements[$original] = $node->authkey;
-          break;
-
-        case 'authlink:view-url':
-          $replacements[$original] = node_authlink_get_node_url($node, 'view');
-          break;
-
-        case 'authlink:edit-url':
-          $replacements[$original] = node_authlink_get_node_url($node, 'update');
-          break;
-
-        case 'authlink:delete-url':
-          $replacements[$original] = node_authlink_get_node_url($node, 'delete');
-          break;
-      }
-    }
-  }
-
-  return $replacements;
+  return \Drupal::service(NodeAuthlinkHooks::class)->tokens($type, $tokens, $data, $options, $bubbleable_metadata);
 }
 
 /**
  * Implements hook_group_content_info_alter().
  */
+#[LegacyHook]
 function node_authlink_group_content_info_alter(array &$groupContentInfo) {
-  // Override the access control handler so authlink can operate first.
-  foreach ($groupContentInfo as &$info) {
-    $info['handlers']['access'] = NodeAuthlinkGroupContentAccessControlHandler::class;
-  }
+  \Drupal::service(NodeAuthlinkHooks::class)->groupContentInfoAlter($groupContentInfo);
 }
 
 /**
  * Implements hook_entity_type_alter().
  */
+#[LegacyHook]
 function node_authlink_entity_type_alter(array &$entity_types) {
-  if ($entity_types['node'] instanceof ContentEntityTypeInterface) {
-    $entity_types['node']->setAccessClass(NodeAuthlinkNodeAccessControlHandler::class);
-  }
+  \Drupal::service(NodeAuthlinkHooks::class)->entityTypeAlter($entity_types);
 }
 
 /**
diff --git a/node_authlink.services.yml b/node_authlink.services.yml
index 661cff7..7ed8c45 100644
--- a/node_authlink.services.yml
+++ b/node_authlink.services.yml
@@ -3,3 +3,11 @@ services:
     class: Drupal\node_authlink\Access\NodeRevisionAccessCheck
     decorates: access_check.entity
     parent: access_check.entity
+
+  Drupal\node_authlink\Hook\NodeAuthlinkHooks:
+    class: Drupal\node_authlink\Hook\NodeAuthlinkHooks
+    autowire: true
+
+  Drupal\node_authlink\Hook\NodeAuthlinkViewsHooks:
+    class: Drupal\node_authlink\Hook\NodeAuthlinkViewsHooks
+    autowire: true
diff --git a/node_authlink.views.inc b/node_authlink.views.inc
index 123fed8..ad6862f 100644
--- a/node_authlink.views.inc
+++ b/node_authlink.views.inc
@@ -1,5 +1,12 @@
 <?php
 
+/**
+ * @file
+ */
+
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\node_authlink\Hook\NodeAuthlinkViewsHooks;
+
 /**
  * @file
  */
@@ -7,100 +14,7 @@
 /**
  * Implements hook_views_data().
  */
+#[LegacyHook]
 function node_authlink_views_data() {
-
-  // Table properties.
-  $data['node_authlink_nodes']['table']['group'] = t('Node Auth link');
-
-  $data['node_authlink_nodes']['table']['base'] = [
-    'field' => 'nid',
-    'title' => t('Node auth link nodes table'),
-    'help' => t('Table for store authorization keys.'),
-    'weight' => -10,
-  ];
-
-  $data['node_authlink_nodes']['table']['join'] = [
-    'node_field_data' => [
-      'left_field' => 'nid',
-      'field' => 'nid',
-    ],
-  ];
-
-  // Table fields.
-  $data['node_authlink_nodes']['nid'] = [
-    'title' => t('Nid'),
-    'help' => t('The node ID.'),
-    'relationship' => [
-      'label' => t('node'),
-      'base' => 'node_field_data',
-      'id' => 'standard',
-    ],
-  ];
-
-  $data['node_authlink_nodes']['authkey'] = [
-    'title' => t('Authkey'),
-    'help' => t('Authkey field.'),
-    'field' => [
-      'id' => 'standard',
-      'click sortable' => TRUE,
-    ],
-    'sort' => [
-      'id' => 'standard',
-    ],
-    'filter' => [
-      'id' => 'string',
-    ],
-    'argument' => [
-      'id' => 'standard',
-    ],
-  ];
-
-  $data['node_authlink_nodes']['created'] = [
-    'title' => t('Created date'),
-    'help' => t('The date the authkey was created.'),
-    'field' => [
-      'id' => 'date',
-      'click sortable' => TRUE,
-    ],
-    'sort' => [
-      'id' => 'date',
-    ],
-    'filter' => [
-      'id' => 'date',
-    ],
-  ];
-
-  $data['node_authlink_nodes']['authlink_delete_link'] = [
-    'title' => t('Authlink delete'),
-    'field' => [
-      'title' => t('Authlink delete link'),
-      'help' => t('Provides a link to delete the node authlink.'),
-      'id' => 'authlink_delete_link',
-      'real field' => 'nid',
-      'click sortable' => FALSE,
-    ],
-    'relationship' => [
-      'base' => 'node_authlink_nodes',
-      'base field' => 'nid',
-      'field' => 'nid',
-      'id' => 'standard',
-    ],
-  ];
-
-  // Node integration.
-  $data['node']['node_authlink_nodes'] = [
-    'title' => t('Nid'),
-    'help' => t('The node ID.'),
-    'relationship' => [
-      'base' => 'node_authlink_nodes',
-      'base field' => 'nid',
-      'field' => 'nid',
-      'id' => 'standard',
-      'label' => t('node authlink'),
-      'title' => t('Node Authlink'),
-      'help' => t('Relate content to the node authlink table.'),
-    ],
-  ];
-
-  return $data;
+  return \Drupal::service(NodeAuthlinkViewsHooks::class)->viewsData();
 }
diff --git a/src/Hook/NodeAuthlinkHooks.php b/src/Hook/NodeAuthlinkHooks.php
new file mode 100644
index 0000000..48bd1d9
--- /dev/null
+++ b/src/Hook/NodeAuthlinkHooks.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Drupal\node_authlink\Hook;
+
+use Drupal\node_authlink\Access\NodeAuthlinkNodeAccessControlHandler;
+use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\node_authlink\Plugin\NodeAuthlinkGroupContentAccessControlHandler;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\node\NodeInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for node_authlink.
+ */
+class NodeAuthlinkHooks {
+
+  /**
+   * Implements hook_node_load().
+   *
+   * Appends authkey to loaded node object.
+   */
+  #[Hook('node_load')]
+  public function nodeLoad($nodes) {
+    foreach ($nodes as $nid => $node) {
+      // @todo check node type (performance)
+      if ($authkey = node_authlink_load_authkey($nid)) {
+        $nodes[$nid]->authkey = $authkey;
+      }
+    }
+  }
+
+  /**
+   * Implements hook_node_access().
+   */
+  #[Hook('node_access')]
+  public function nodeAccess(NodeInterface $node, $op, AccountInterface $account) {
+    // Ignore if just creating node.
+    if ($op == 'create') {
+      return AccessResult::neutral();
+    }
+    // Ignore if node type is not enabled.
+    // Or the node does not have authkey.
+    if (!node_authlink_node_is_enabled($node) || !isset($node->authkey)) {
+      return AccessResult::neutral();
+    }
+    if (node_authlink_check_authlink($node, $op, $account)) {
+      return AccessResult::allowed();
+    }
+  }
+
+  /**
+   * Implements hook_ENTITY_TYPE_delete().
+   */
+  #[Hook('node_delete')]
+  public function nodeDelete(EntityInterface $entity) {
+    $config = \Drupal::config('node_authlink.settings');
+    $enable = $config->get('enable');
+    // Ignore if node type is disabled.
+    if (isset($enable[$entity->bundle()]) && $enable[$entity->bundle()]) {
+      node_authlink_delete($entity->id());
+    }
+  }
+
+  /**
+   * Implements hook_tokens().
+   */
+  #[Hook('tokens')]
+  public function tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
+    $replacements = [];
+    if ($type != 'node' || empty($data['node'])) {
+      return $replacements;
+    }
+    if ($type == 'node' && !empty($data['node']->authkey)) {
+      $node = $data['node'];
+      if (!$node instanceof NodeInterface) {
+        return $replacements;
+      }
+      foreach ($tokens as $name => $original) {
+        switch ($name) {
+          case 'authlink:authkey':
+            $replacements[$original] = $node->authkey;
+            break;
+
+          case 'authlink:view-url':
+            $replacements[$original] = node_authlink_get_node_url($node, 'view');
+            break;
+
+          case 'authlink:edit-url':
+            $replacements[$original] = node_authlink_get_node_url($node, 'update');
+            break;
+
+          case 'authlink:delete-url':
+            $replacements[$original] = node_authlink_get_node_url($node, 'delete');
+            break;
+        }
+      }
+    }
+    return $replacements;
+  }
+
+  /**
+   * Implements hook_group_content_info_alter().
+   */
+  #[Hook('group_content_info_alter')]
+  public function groupContentInfoAlter(array &$groupContentInfo) {
+    // Override the access control handler so authlink can operate first.
+    foreach ($groupContentInfo as &$info) {
+      $info['handlers']['access'] = NodeAuthlinkGroupContentAccessControlHandler::class;
+    }
+  }
+
+  /**
+   * Implements hook_entity_type_alter().
+   */
+  #[Hook('entity_type_alter')]
+  public function entityTypeAlter(array &$entity_types) {
+    if ($entity_types['node'] instanceof ContentEntityTypeInterface) {
+      $entity_types['node']->setAccessClass(NodeAuthlinkNodeAccessControlHandler::class);
+    }
+  }
+
+}
diff --git a/src/Hook/NodeAuthlinkViewsHooks.php b/src/Hook/NodeAuthlinkViewsHooks.php
new file mode 100644
index 0000000..fb9d212
--- /dev/null
+++ b/src/Hook/NodeAuthlinkViewsHooks.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Drupal\node_authlink\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for node_authlink.
+ */
+class NodeAuthlinkViewsHooks {
+  use StringTranslationTrait;
+  /**
+   * @file
+   */
+
+  /**
+   * Implements hook_views_data().
+   */
+  #[Hook('views_data')]
+  public function viewsData() {
+    // Table properties.
+    $data['node_authlink_nodes']['table']['group'] = $this->t('Node Auth link');
+    $data['node_authlink_nodes']['table']['base'] = [
+      'field' => 'nid',
+      'title' => $this->t('Node auth link nodes table'),
+      'help' => $this->t('Table for store authorization keys.'),
+      'weight' => -10,
+    ];
+    $data['node_authlink_nodes']['table']['join'] = [
+      'node_field_data' => [
+        'left_field' => 'nid',
+        'field' => 'nid',
+      ],
+    ];
+    // Table fields.
+    $data['node_authlink_nodes']['nid'] = [
+      'title' => $this->t('Nid'),
+      'help' => $this->t('The node ID.'),
+      'relationship' => [
+        'label' => $this->t('node'),
+        'base' => 'node_field_data',
+        'id' => 'standard',
+      ],
+    ];
+    $data['node_authlink_nodes']['authkey'] = [
+      'title' => $this->t('Authkey'),
+      'help' => $this->t('Authkey field.'),
+      'field' => [
+        'id' => 'standard',
+        'click sortable' => TRUE,
+      ],
+      'sort' => [
+        'id' => 'standard',
+      ],
+      'filter' => [
+        'id' => 'string',
+      ],
+      'argument' => [
+        'id' => 'standard',
+      ],
+    ];
+    $data['node_authlink_nodes']['created'] = [
+      'title' => $this->t('Created date'),
+      'help' => $this->t('The date the authkey was created.'),
+      'field' => [
+        'id' => 'date',
+        'click sortable' => TRUE,
+      ],
+      'sort' => [
+        'id' => 'date',
+      ],
+      'filter' => [
+        'id' => 'date',
+      ],
+    ];
+    $data['node_authlink_nodes']['authlink_delete_link'] = [
+      'title' => $this->t('Authlink delete'),
+      'field' => [
+        'title' => $this->t('Authlink delete link'),
+        'help' => $this->t('Provides a link to delete the node authlink.'),
+        'id' => 'authlink_delete_link',
+        'real field' => 'nid',
+        'click sortable' => FALSE,
+      ],
+      'relationship' => [
+        'base' => 'node_authlink_nodes',
+        'base field' => 'nid',
+        'field' => 'nid',
+        'id' => 'standard',
+      ],
+    ];
+    // Node integration.
+    $data['node']['node_authlink_nodes'] = [
+      'title' => $this->t('Nid'),
+      'help' => $this->t('The node ID.'),
+      'relationship' => [
+        'base' => 'node_authlink_nodes',
+        'base field' => 'nid',
+        'field' => 'nid',
+        'id' => 'standard',
+        'label' => $this->t('node authlink'),
+        'title' => $this->t('Node Authlink'),
+        'help' => $this->t('Relate content to the node authlink table.'),
+      ],
+    ];
+    return $data;
+  }
+
+}
