diff --git a/app/Actions/View.php b/app/Actions/View.php
index c5ec6a38d..62e03d9f4 100644
--- a/app/Actions/View.php
+++ b/app/Actions/View.php
@@ -1,8 +1,19 @@
 <?php namespace BookStack\Actions;
 
+use BookStack\Interfaces\Viewable;
 use BookStack\Model;
 use Illuminate\Database\Eloquent\Relations\MorphTo;
 
+/**
+ * Class View
+ * Views are stored per-item per-person within the database.
+ * They can be used to find popular items or recently viewed items
+ * at a per-person level. They do not record every view instance as an
+ * activity. Only the latest and original view times could be recognised.
+ *
+ * @property int $views
+ * @property int $user_id
+ */
 class View extends Model
 {
 
@@ -15,4 +26,32 @@ class View extends Model
     {
         return $this->morphTo();
     }
+
+    /**
+     * Increment the current user's view count for the given viewable model.
+     */
+    public static function incrementFor(Viewable $viewable): int
+    {
+        $user = user();
+        if (is_null($user) || $user->isDefault()) {
+            return 0;
+        }
+
+        /** @var View $view */
+        $view = $viewable->views()->firstOrNew([
+            'user_id' => $user->id,
+        ], ['views' => 0]);
+
+        $view->save(['views' => $view->views + 1]);
+
+        return $view->views;
+    }
+
+    /**
+     * Clear all views from the system.
+     */
+    public static function clearAll()
+    {
+        static::query()->truncate();
+    }
 }
diff --git a/app/Actions/ViewService.php b/app/Actions/ViewService.php
index a4e620d4b..febc93af3 100644
--- a/app/Actions/ViewService.php
+++ b/app/Actions/ViewService.php
@@ -1,7 +1,6 @@
 <?php namespace BookStack\Actions;
 
 use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Entity;
 use BookStack\Entities\EntityProvider;
 use DB;
@@ -26,33 +25,6 @@ class ViewService
         $this->entityProvider = $entityProvider;
     }
 
-    /**
-     * Add a view to the given entity.
-     * @param \BookStack\Entities\Models\Entity $entity
-     * @return int
-     */
-    public function add(Entity $entity)
-    {
-        $user = user();
-        if ($user === null || $user->isDefault()) {
-            return 0;
-        }
-        $view = $entity->views()->where('user_id', '=', $user->id)->first();
-        // Add view if model exists
-        if ($view) {
-            $view->increment('views');
-            return $view->views;
-        }
-
-        // Otherwise create new view count
-        $entity->views()->save($this->view->newInstance([
-            'user_id' => $user->id,
-            'views' => 1
-        ]));
-
-        return 1;
-    }
-
     /**
      * Get the entities with the most views.
      * @param int $count
@@ -106,12 +78,4 @@ class ViewService
 
         return $all->sortByDesc('last_viewed_at')->slice(0, $count);
     }
-
-    /**
-     * Reset all view counts by deleting all views.
-     */
-    public function resetAll()
-    {
-        $this->view->truncate();
-    }
 }
diff --git a/app/Console/Commands/ClearViews.php b/app/Console/Commands/ClearViews.php
index 35356210b..693d93639 100644
--- a/app/Console/Commands/ClearViews.php
+++ b/app/Console/Commands/ClearViews.php
@@ -2,6 +2,7 @@
 
 namespace BookStack\Console\Commands;
 
+use BookStack\Actions\View;
 use Illuminate\Console\Command;
 
 class ClearViews extends Command
@@ -36,7 +37,7 @@ class ClearViews extends Command
      */
     public function handle()
     {
-        \Views::resetAll();
+        View::clearAll();
         $this->comment('Views cleared');
     }
 }
diff --git a/app/Entities/Models/Entity.php b/app/Entities/Models/Entity.php
index be63cec2b..561876769 100644
--- a/app/Entities/Models/Entity.php
+++ b/app/Entities/Models/Entity.php
@@ -12,6 +12,7 @@ use BookStack\Entities\Tools\SlugGenerator;
 use BookStack\Facades\Permissions;
 use BookStack\Interfaces\Favouritable;
 use BookStack\Interfaces\Sluggable;
+use BookStack\Interfaces\Viewable;
 use BookStack\Model;
 use BookStack\Traits\HasCreatorAndUpdater;
 use BookStack\Traits\HasOwner;
@@ -40,7 +41,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static Builder withLastView()
  * @method static Builder withViewCount()
  */
-abstract class Entity extends Model implements Sluggable, Favouritable
+abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
 {
     use SoftDeletes;
     use HasCreatorAndUpdater;
diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php
index 59c205d0a..d111f9f0a 100644
--- a/app/Http/Controllers/BookController.php
+++ b/app/Http/Controllers/BookController.php
@@ -2,6 +2,7 @@
 
 use Activity;
 use BookStack\Actions\ActivityType;
+use BookStack\Actions\View;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Models\Bookshelf;
 use BookStack\Entities\Tools\PermissionsUpdater;
@@ -112,7 +113,7 @@ class BookController extends Controller
         $bookChildren = (new BookContents($book))->getTree(true);
         $bookParentShelves = $book->shelves()->visible()->get();
 
-        Views::add($book);
+        View::incrementFor($book);
         if ($request->has('shelf')) {
             $this->entityContextManager->setShelfContext(intval($request->get('shelf')));
         }
diff --git a/app/Http/Controllers/BookshelfController.php b/app/Http/Controllers/BookshelfController.php
index 03b3cad54..b4795db09 100644
--- a/app/Http/Controllers/BookshelfController.php
+++ b/app/Http/Controllers/BookshelfController.php
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Http\Controllers;
 
 use Activity;
+use BookStack\Actions\View;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Entities\Tools\ShelfContext;
@@ -109,7 +110,7 @@ class BookshelfController extends Controller
             ->values()
             ->all();
 
-        Views::add($shelf);
+        View::incrementFor($shelf);
         $this->entityContextManager->setShelfContext($shelf->id);
         $view = setting()->getForCurrentUser('bookshelf_view_type');
 
diff --git a/app/Http/Controllers/ChapterController.php b/app/Http/Controllers/ChapterController.php
index 1d69df2a2..fbef81582 100644
--- a/app/Http/Controllers/ChapterController.php
+++ b/app/Http/Controllers/ChapterController.php
@@ -1,5 +1,6 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Actions\View;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Repos\ChapterRepo;
@@ -64,7 +65,7 @@ class ChapterController extends Controller
 
         $sidebarTree = (new BookContents($chapter->book))->getTree();
         $pages = $chapter->getVisiblePages();
-        Views::add($chapter);
+        View::incrementFor($chapter);
 
         $this->setPageTitle($chapter->getShortName());
         return view('chapters.show', [
diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php
index 30d33ad48..769ea8e69 100644
--- a/app/Http/Controllers/PageController.php
+++ b/app/Http/Controllers/PageController.php
@@ -1,5 +1,6 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Actions\View;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Tools\PageContent;
 use BookStack\Entities\Tools\PageEditActivity;
@@ -141,7 +142,7 @@ class PageController extends Controller
             $page->load(['comments.createdBy']);
         }
 
-        Views::add($page);
+        View::incrementFor($page);
         $this->setPageTitle($page->getShortName());
         return view('pages.show', [
             'page' => $page,
diff --git a/app/Interfaces/Viewable.php b/app/Interfaces/Viewable.php
new file mode 100644
index 000000000..4a7b6a013
--- /dev/null
+++ b/app/Interfaces/Viewable.php
@@ -0,0 +1,11 @@
+<?php namespace BookStack\Interfaces;
+
+use Illuminate\Database\Eloquent\Relations\MorphMany;
+
+interface Viewable
+{
+    /**
+     * Get all view instances for this viewable model.
+     */
+    public function views(): MorphMany;
+}
\ No newline at end of file