diff --git a/modules/typed_entity_example/src/Hook/TypedEntityExampleHooks.php b/modules/typed_entity_example/src/Hook/TypedEntityExampleHooks.php
new file mode 100644
index 0000000..10d9b43
--- /dev/null
+++ b/modules/typed_entity_example/src/Hook/TypedEntityExampleHooks.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Drupal\typed_entity_example\Hook;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessibleInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for typed_entity_example.
+ */
+class TypedEntityExampleHooks {
+
+  /**
+   * Implements hook_entity_access().
+   *
+   * NOTE: This is for demonstrations of the APIs. If you were to implement this
+   * hook you would not want to do it this way. See the linked article for a
+   * better pattern.
+   *
+   * @see https://www.lullabot.com/articles/maintainable-code-drupal-wrapped-entities
+   */
+  #[Hook('entity_access')]
+  public static function entityAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+    $repository = typed_entity_repository_manager()->repositoryFromEntity($entity);
+    if (!$repository instanceof AccessibleInterface) {
+      return AccessResult::neutral();
+    }
+    $access = $repository->access($operation, $account, TRUE);
+    if (!$access->isNeutral()) {
+      return $access;
+    }
+    $wrapped = $repository->wrap($entity);
+    if (!$wrapped instanceof AccessibleInterface) {
+      return AccessResult::neutral();
+    }
+    return $wrapped->access($operation, $account, TRUE);
+  }
+
+}
diff --git a/modules/typed_entity_example/typed_entity_example.module b/modules/typed_entity_example/typed_entity_example.module
index 1c2d5ae..3d44117 100644
--- a/modules/typed_entity_example/typed_entity_example.module
+++ b/modules/typed_entity_example/typed_entity_example.module
@@ -5,8 +5,8 @@
  * Module implementation file.
  */
 
-use Drupal\Core\Access\AccessibleInterface;
-use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\typed_entity_example\Hook\TypedEntityExampleHooks;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Session\AccountInterface;
 
@@ -19,18 +19,7 @@ use Drupal\Core\Session\AccountInterface;
  *
  * @see https://www.lullabot.com/articles/maintainable-code-drupal-wrapped-entities
  */
+#[LegacyHook]
 function typed_entity_example_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
-  $repository = typed_entity_repository_manager()->repositoryFromEntity($entity);
-  if (!$repository instanceof AccessibleInterface) {
-    return AccessResult::neutral();
-  }
-  $access = $repository->access($operation, $account, TRUE);
-  if (!$access->isNeutral()) {
-    return $access;
-  }
-  $wrapped = $repository->wrap($entity);
-  if (!$wrapped instanceof AccessibleInterface) {
-    return AccessResult::neutral();
-  }
-  return $wrapped->access($operation, $account, TRUE);
+  return \Drupal::service(TypedEntityExampleHooks::class)->entityAccess($entity, $operation, $account);
 }
diff --git a/modules/typed_entity_example/typed_entity_example.services.yml b/modules/typed_entity_example/typed_entity_example.services.yml
new file mode 100644
index 0000000..673ca0f
--- /dev/null
+++ b/modules/typed_entity_example/typed_entity_example.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\typed_entity_example\Hook\TypedEntityExampleHooks:
+    class: Drupal\typed_entity_example\Hook\TypedEntityExampleHooks
+    autowire: true
diff --git a/modules/typed_entity_ui/src/Hook/TypedEntityUiHooks.php b/modules/typed_entity_ui/src/Hook/TypedEntityUiHooks.php
new file mode 100644
index 0000000..5f5270e
--- /dev/null
+++ b/modules/typed_entity_ui/src/Hook/TypedEntityUiHooks.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\typed_entity_ui\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for typed_entity_ui.
+ */
+class TypedEntityUiHooks {
+
+  /**
+   * Implements hook_theme().
+   */
+  #[Hook('theme')]
+  public static function theme(array $existing, string $type, string $theme, string $path): array {
+    return [
+      'php_class_info' => [
+        'variables' => [
+          'class_name' => NULL,
+        ],
+      ],
+      'class_with_variants' => [
+        'variables' => [
+          'object' => NULL,
+          'base_class' => NULL,
+        ],
+      ],
+      'php_class_summary' => [
+        'variables' => [
+          'class_name' => NULL,
+          'with_comment' => TRUE,
+          'with_fqn' => TRUE,
+          'with_attribute' => TRUE,
+        ],
+      ],
+    ];
+  }
+
+}
diff --git a/modules/typed_entity_ui/typed_entity_ui.module b/modules/typed_entity_ui/typed_entity_ui.module
index 6c19e6d..4caf18b 100644
--- a/modules/typed_entity_ui/typed_entity_ui.module
+++ b/modules/typed_entity_ui/typed_entity_ui.module
@@ -5,6 +5,8 @@
  * Module implementation file.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\typed_entity_ui\Hook\TypedEntityUiHooks;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\typed_entity\Attribute\TypedRepository;
 use Drupal\typed_entity\ClassWithVariantsInterface;
@@ -12,28 +14,9 @@ use Drupal\typed_entity\ClassWithVariantsInterface;
 /**
  * Implements hook_theme().
  */
+#[LegacyHook]
 function typed_entity_ui_theme(array $existing, string $type, string $theme, string $path): array {
-  return [
-    'php_class_info' => [
-      'variables' => [
-        'class_name' => NULL,
-      ],
-    ],
-    'class_with_variants' => [
-      'variables' => [
-        'object' => NULL,
-        'base_class' => NULL,
-      ],
-    ],
-    'php_class_summary' => [
-      'variables' => [
-        'class_name' => NULL,
-        'with_comment' => TRUE,
-        'with_fqn' => TRUE,
-        'with_attribute' => TRUE,
-      ],
-    ],
-  ];
+  return \Drupal::service(TypedEntityUiHooks::class)->theme($existing, $type, $theme, $path);
 }
 
 /**
diff --git a/modules/typed_entity_ui/typed_entity_ui.services.yml b/modules/typed_entity_ui/typed_entity_ui.services.yml
new file mode 100644
index 0000000..2ea17c8
--- /dev/null
+++ b/modules/typed_entity_ui/typed_entity_ui.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\typed_entity_ui\Hook\TypedEntityUiHooks:
+    class: Drupal\typed_entity_ui\Hook\TypedEntityUiHooks
+    autowire: true
diff --git a/src/Hook/TypedEntityHooks.php b/src/Hook/TypedEntityHooks.php
new file mode 100644
index 0000000..112be9b
--- /dev/null
+++ b/src/Hook/TypedEntityHooks.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Drupal\typed_entity\Hook;
+
+use Drupal\typed_entity\WrappedEntities\WrappedEntityInterface;
+use Drupal\typed_entity\Render\TypedEntityRendererInterface;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for typed_entity.
+ */
+class TypedEntityHooks {
+
+  /**
+   * Implements hook_entity_view_alter().
+   */
+  #[Hook('entity_view_alter')]
+  public static function entityViewAlter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
+    [$renderer, $wrapped_entity] = _typed_entity_find_renderer($entity, $build);
+    if (!$renderer instanceof TypedEntityRendererInterface || !$wrapped_entity instanceof WrappedEntityInterface) {
+      return;
+    }
+    $renderer->viewAlter($build, $wrapped_entity, $display);
+  }
+
+  /**
+   * Implements hook_preprocess().
+   */
+  #[Hook('preprocess')]
+  public static function preprocess(&$variables, $hook) {
+    $entity = $variables[$hook] ?? NULL;
+    if (!$entity instanceof EntityInterface) {
+      return;
+    }
+    [$renderer, $wrapped_entity] = _typed_entity_find_renderer($entity, $variables['elements'] ?? []);
+    if (!$renderer instanceof TypedEntityRendererInterface || !$wrapped_entity instanceof WrappedEntityInterface) {
+      return;
+    }
+    $renderer->preprocess($variables, $wrapped_entity);
+  }
+
+  /**
+   * Implements hook_entity_display_build_alter().
+   */
+  #[Hook('entity_display_build_alter')]
+  public static function entityDisplayBuildAlter(&$build, $context) {
+    $entity = $context['entity'] ?? NULL;
+    if (!$entity instanceof EntityInterface) {
+      return;
+    }
+    [$renderer, $wrapped_entity] = _typed_entity_find_renderer($entity, $build);
+    if (!$renderer instanceof TypedEntityRendererInterface || !$wrapped_entity instanceof WrappedEntityInterface) {
+      return;
+    }
+    $renderer->displayBuildAlter($build, $wrapped_entity, $context);
+  }
+
+  /**
+   * Implements hook_entity_build_defaults_alter().
+   */
+  #[Hook('entity_build_defaults_alter')]
+  public static function entityBuildDefaultsAlter(array &$build, EntityInterface $entity, $view_mode) {
+    [$renderer, $wrapped_entity] = _typed_entity_find_renderer($entity, $build);
+    if (!$renderer instanceof TypedEntityRendererInterface || !$wrapped_entity instanceof WrappedEntityInterface) {
+      return;
+    }
+    $renderer->buildDefaultsAlter($build, $wrapped_entity, $view_mode);
+  }
+
+}
diff --git a/typed_entity.module b/typed_entity.module
index 9c0b6ff..9cfaf49 100644
--- a/typed_entity.module
+++ b/typed_entity.module
@@ -5,63 +5,43 @@
  * Module implementation file.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\typed_entity\Hook\TypedEntityHooks;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\typed_entity\TypedEntityContext;
-use Drupal\typed_entity\Render\TypedEntityRendererInterface;
 use Drupal\typed_entity\RepositoryManager;
-use Drupal\typed_entity\WrappedEntities\WrappedEntityInterface;
 
 /**
  * Implements hook_entity_view_alter().
  */
+#[LegacyHook]
 function typed_entity_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
-  [$renderer, $wrapped_entity] = _typed_entity_find_renderer($entity, $build);
-  if (!$renderer instanceof TypedEntityRendererInterface || !$wrapped_entity instanceof WrappedEntityInterface) {
-    return;
-  }
-  $renderer->viewAlter($build, $wrapped_entity, $display);
+  \Drupal::service(TypedEntityHooks::class)->entityViewAlter($build, $entity, $display);
 }
 
 /**
  * Implements hook_preprocess().
  */
+#[LegacyHook]
 function typed_entity_preprocess(&$variables, $hook) {
-  $entity = $variables[$hook] ?? NULL;
-  if (!$entity instanceof EntityInterface) {
-    return;
-  }
-  [$renderer, $wrapped_entity] = _typed_entity_find_renderer($entity, $variables['elements'] ?? []);
-  if (!$renderer instanceof TypedEntityRendererInterface || !$wrapped_entity instanceof WrappedEntityInterface) {
-    return;
-  }
-  $renderer->preprocess($variables, $wrapped_entity);
+  \Drupal::service(TypedEntityHooks::class)->preprocess($variables, $hook);
 }
 
 /**
  * Implements hook_entity_display_build_alter().
  */
+#[LegacyHook]
 function typed_entity_entity_display_build_alter(&$build, $context) {
-  $entity = $context['entity'] ?? NULL;
-  if (!$entity instanceof EntityInterface) {
-    return;
-  }
-  [$renderer, $wrapped_entity] = _typed_entity_find_renderer($entity, $build);
-  if (!$renderer instanceof TypedEntityRendererInterface || !$wrapped_entity instanceof WrappedEntityInterface) {
-    return;
-  }
-  $renderer->displayBuildAlter($build, $wrapped_entity, $context);
+  \Drupal::service(TypedEntityHooks::class)->entityDisplayBuildAlter($build, $context);
 }
 
 /**
  * Implements hook_entity_build_defaults_alter().
  */
+#[LegacyHook]
 function typed_entity_entity_build_defaults_alter(array &$build, EntityInterface $entity, $view_mode) {
-  [$renderer, $wrapped_entity] = _typed_entity_find_renderer($entity, $build);
-  if (!$renderer instanceof TypedEntityRendererInterface || !$wrapped_entity instanceof WrappedEntityInterface) {
-    return;
-  }
-  $renderer->buildDefaultsAlter($build, $wrapped_entity, $view_mode);
+  \Drupal::service(TypedEntityHooks::class)->entityBuildDefaultsAlter($build, $entity, $view_mode);
 }
 
 /**
diff --git a/typed_entity.services.yml b/typed_entity.services.yml
index 144a6c4..f458be9 100644
--- a/typed_entity.services.yml
+++ b/typed_entity.services.yml
@@ -6,3 +6,7 @@ services:
   plugin.manager.typed_entity_repository:
     class: Drupal\typed_entity\TypedRepositoryPluginManager
     parent: default_plugin_manager
+
+  Drupal\typed_entity\Hook\TypedEntityHooks:
+    class: Drupal\typed_entity\Hook\TypedEntityHooks
+    autowire: true
