diff --git a/cache_tools.info.yml b/cache_tools.info.yml
index 1aaf90f..bfd7f87 100644
--- a/cache_tools.info.yml
+++ b/cache_tools.info.yml
@@ -2,4 +2,4 @@ type: module
 name: Cache Tools
 description: Module contains caching related tools.
 package: Cache
-core_version_requirement: ^8.8 || ^9 || ^10 || ^11
+core_version_requirement: ^10.1 || ^11 || ^12
diff --git a/cache_tools.module b/cache_tools.module
index c05ef57..afec8ea 100644
--- a/cache_tools.module
+++ b/cache_tools.module
@@ -4,16 +4,17 @@
  * @file
  * Provides various caching enhancements.
  */
-
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\cache_tools\Hook\CacheToolsHooks;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
  * Implements hook_entity_type_alter().
  */
-function cache_tools_entity_type_alter(array &$entity_types) {
-  /** @var \Drupal\Core\Config\Entity\ConfigEntityType $block */
-  $block = $entity_types['block'];
-  $block->setViewBuilderClass('Drupal\cache_tools\CachewiseBlockViewBuilder');
+#[LegacyHook]
+function cache_tools_entity_type_alter(array &$entity_types)
+{
+    \Drupal::service(CacheToolsHooks::class)->entityTypeAlter($entity_types);
 }
 
 /**
@@ -27,11 +28,10 @@ function cache_tools_entity_type_alter(array &$entity_types) {
  *   Use a PublishedEntityCacheTag event subscriber instead of hooks after:
  *   https://www.drupal.org/project/drupal/issues/2551893 .
  */
-function cache_tools_entity_insert(EntityInterface $entity) {
-  /** @var \Drupal\cache_tools\Service\CacheInvalidator $cache_invalidator */
-  $cache_invalidator = \Drupal::service('cache_tools.cache.invalidator');
-  $cache_invalidator->invalidatePublishedEntity($entity);
-  $cache_invalidator->invalidatePublishedEntityFields($entity);
+#[LegacyHook]
+function cache_tools_entity_insert(EntityInterface $entity)
+{
+    \Drupal::service(CacheToolsHooks::class)->entityInsert($entity);
 }
 
 /**
@@ -42,11 +42,10 @@ function cache_tools_entity_insert(EntityInterface $entity) {
  * a published state and/or a configured field value is changed. Entity type or
  * a field needs to be allowed for invalidation.
  */
-function cache_tools_entity_update(EntityInterface $entity) {
-  /** @var \Drupal\cache_tools\Service\CacheInvalidator $cache_invalidator */
-  $cache_invalidator = \Drupal::service('cache_tools.cache.invalidator');
-  $cache_invalidator->invalidatePublishedEntity($entity);
-  $cache_invalidator->invalidatePublishedEntityFields($entity);
+#[LegacyHook]
+function cache_tools_entity_update(EntityInterface $entity)
+{
+    \Drupal::service(CacheToolsHooks::class)->entityUpdate($entity);
 }
 
 /**
@@ -57,8 +56,8 @@ function cache_tools_entity_update(EntityInterface $entity) {
  * Entity type or a field needs to be allowed for invalidation.
  * Note: Deleted entities are invalidated via entity_type:id (eg node:123).
  */
-function cache_tools_entity_delete(EntityInterface $entity) {
-  /** @var \Drupal\cache_tools\Service\CacheInvalidator $cache_invalidator */
-  $cache_invalidator = \Drupal::service('cache_tools.cache.invalidator');
-  $cache_invalidator->invalidatePublishedEntityFields($entity);
+#[LegacyHook]
+function cache_tools_entity_delete(EntityInterface $entity)
+{
+    \Drupal::service(CacheToolsHooks::class)->entityDelete($entity);
 }
diff --git a/cache_tools.services.yml b/cache_tools.services.yml
index 97bf38b..fb18843 100644
--- a/cache_tools.services.yml
+++ b/cache_tools.services.yml
@@ -42,3 +42,7 @@ parameters:
           contexts:
             exclude:
              - url.query_args
+
+  Drupal\cache_tools\Hook\CacheToolsHooks:
+    class: Drupal\cache_tools\Hook\CacheToolsHooks
+    autowire: true
diff --git a/src/Hook/CacheToolsHooks.php b/src/Hook/CacheToolsHooks.php
new file mode 100644
index 0000000..5e0fa76
--- /dev/null
+++ b/src/Hook/CacheToolsHooks.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Drupal\cache_tools\Hook;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+/**
+ * Hook implementations for cache_tools.
+ */
+class CacheToolsHooks
+{
+    /**
+     * Implements hook_entity_type_alter().
+     */
+    #[Hook('entity_type_alter')]
+    public static function entityTypeAlter(array &$entity_types)
+    {
+        /** @var \Drupal\Core\Config\Entity\ConfigEntityType $block */
+        $block = $entity_types['block'];
+        $block->setViewBuilderClass('Drupal\cache_tools\CachewiseBlockViewBuilder');
+    }
+    /**
+     * Implements hook_entity_insert().
+     *
+     * Invalidates `entitytype_entitybundle_pub` and
+     * `entitytype_entitybundle_pub:field_name:value` if entity is going to be
+     * published. Entity type or a field needs to be allowed for invalidation.
+     *
+     * @todo
+     *   Use a PublishedEntityCacheTag event subscriber instead of hooks after:
+     *   https://www.drupal.org/project/drupal/issues/2551893 .
+     */
+    #[Hook('entity_insert')]
+    public static function entityInsert(\Drupal\Core\Entity\EntityInterface $entity)
+    {
+        /** @var \Drupal\cache_tools\Service\CacheInvalidator $cache_invalidator */
+        $cache_invalidator = \Drupal::service('cache_tools.cache.invalidator');
+        $cache_invalidator->invalidatePublishedEntity($entity);
+        $cache_invalidator->invalidatePublishedEntityFields($entity);
+    }
+    /**
+     * Implements hook_entity_update().
+     *
+     * Invalidates `entitytype_entitybundle_pub` if entity is going from unpublished
+     * state to published and `entitytype_entitybundle_pub:field_name:value` if
+     * a published state and/or a configured field value is changed. Entity type or
+     * a field needs to be allowed for invalidation.
+     */
+    #[Hook('entity_update')]
+    public static function entityUpdate(\Drupal\Core\Entity\EntityInterface $entity)
+    {
+        /** @var \Drupal\cache_tools\Service\CacheInvalidator $cache_invalidator */
+        $cache_invalidator = \Drupal::service('cache_tools.cache.invalidator');
+        $cache_invalidator->invalidatePublishedEntity($entity);
+        $cache_invalidator->invalidatePublishedEntityFields($entity);
+    }
+    /**
+     * Implements hook_entity_delete().
+     *
+     * Invalidates `entitytype_entitybundle_pub:field_name:value` if deleting
+     * published entity.
+     * Entity type or a field needs to be allowed for invalidation.
+     * Note: Deleted entities are invalidated via entity_type:id (eg node:123).
+     */
+    #[Hook('entity_delete')]
+    public static function entityDelete(\Drupal\Core\Entity\EntityInterface $entity)
+    {
+        /** @var \Drupal\cache_tools\Service\CacheInvalidator $cache_invalidator */
+        $cache_invalidator = \Drupal::service('cache_tools.cache.invalidator');
+        $cache_invalidator->invalidatePublishedEntityFields($entity);
+    }
+}
diff --git a/src/Service/CacheInvalidator.php b/src/Service/CacheInvalidator.php
index 728bdea..54fd646 100644
--- a/src/Service/CacheInvalidator.php
+++ b/src/Service/CacheInvalidator.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\cache_tools\Service;
 
+use Drupal\Component\Utility\DeprecationHelper;
 use drunomics\ServiceUtils\Core\Entity\EntityTypeManagerTrait;
 use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
 use Drupal\Core\Entity\EntityPublishedInterface;
@@ -103,8 +104,8 @@ class CacheInvalidator {
     ];
     // If this is update we need to take a look at original entity as well.
     $entities = [$entity];
-    if ($entity->original) {
-      $entities[] = $entity->original;
+    if (DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $entity->getOriginal(), fn() => $entity->original)) {
+      $entities[] = DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $entity->getOriginal(), fn() => $entity->original);
     }
     // If entity is going to be unpublished (insert)
     // or stays unpublished (update), skip.
@@ -132,7 +133,7 @@ class CacheInvalidator {
    * @return string[]
    *   The custom cache tags `entitytype_entitybundle_pub:field_name:value`.
    */
-  public function getPublishedEntityFieldsCacheTags(FieldableEntityInterface $entity, FieldableEntityInterface $entity_compare = NULL) {
+  public function getPublishedEntityFieldsCacheTags(FieldableEntityInterface $entity, ?FieldableEntityInterface $entity_compare = NULL) {
     $tags = [];
     $entity_type = $entity->getEntityTypeId();
     // Get field-based tags configured for current entity bundle.
@@ -255,8 +256,8 @@ class CacheInvalidator {
     // Determine published status and assume the entity is published if it
     // doesn't implement the interface.
     $is_published = $entity instanceof EntityPublishedInterface ? $entity->isPublished() : TRUE;
-    if (isset($entity->original)) {
-      $was_published = $entity->original instanceof EntityPublishedInterface ? $entity->original->isPublished() : TRUE;
+    if (DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $entity->getOriginal() !== NULL, fn() => isset($entity->original))) {
+      $was_published = DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $entity->getOriginal(), fn() => $entity->original) instanceof EntityPublishedInterface ? DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $entity->getOriginal(), fn() => $entity->original)->isPublished() : TRUE;
     }
     else {
       $was_published = FALSE;
@@ -265,11 +266,11 @@ class CacheInvalidator {
     $tags = [];
     if ($is_published && $was_published) {
       // Update published: only the modified field values.
-      $tags = $this->getPublishedEntityFieldsCacheTags($entity, $entity->original);
+      $tags = $this->getPublishedEntityFieldsCacheTags($entity, DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $entity->getOriginal(), fn() => $entity->original));
     }
     elseif ($was_published) {
       // Update and unpublish: all non-empty original field values.
-      $tags = $this->getPublishedEntityFieldsCacheTags($entity->original);
+      $tags = $this->getPublishedEntityFieldsCacheTags(DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $entity->getOriginal(), fn() => $entity->original));
     }
     elseif ($is_published) {
       // Insert or delete published, update and publish: all non-empty field
