diff --git a/README.md b/README.md
index 153e107..d31e15e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
-⚠️ Work in Progress
-Note: This package is currently under development and not ready for production use.
+# ⚠️ Work in Progress
+#### Note: This package is currently under development and not ready for production use.
+--
+
 
 <p align="center">
 <img height="100%" src="assets/cover.png" alt="Laravel Package Skeleton Logo"/>
@@ -31,7 +33,14 @@ Fasti::schedule($job, '2024-12-31 23:59:59'); // Schedule a job for New Year's E
 composer require a-bashtannik/fasti
 ```
 
-Fasti is built with developers in mind.
+Add the tick command to your `console.php` file:
+
+```php
+use Bashtannik\Fasti\Console\Commands\FastiTickCommand;
+
+Schedule::command(FastiTickCommand::class)->everyMinute();
+
+```
 
 ## Usage
 
@@ -83,11 +92,12 @@ Fasti includes a lightweight Eloquent model that stores essential job informatio
 
 use \Bashtannik\Fasti\Facades\Fasti;
 
-Fasti::all(); // Retrieve all scheduled tasks
-Fasti::scheduled($at); // Retrieve all tasks scheduled at a specific time
-Fasti::cancel($id); // Cancel a scheduled task
-Fasti::cancelled(); // Retrieve all canceled tasks
-Fasti::find($id); // Retrieve a scheduled task by ID
+Fasti::all(); 
+Fasti::schedule($job, $at);
+Fasti::scheduled($at); // List all jobs scheduled for a specific time
+Fasti::cancel($job);
+Fasti::cancelled(); // List all cancelled jobs
+Fasti::find($id);
 
 // Or use the Eloquent model directly
 
@@ -104,15 +114,30 @@ Test your scheduled jobs with ease using Fasti's `Fasti::fake()` method and buil
 ```php
 use \Bashtannik\Fasti\Facades\Fasti;
 
-Fasti::fake();
+// If you are using custom model or repository, avoid using this method and test real storage instead.
+
+Fasti::fake(); 
+
+// Test if the expected job is scheduled
 
 Fasti::assertScheduled($job);
-Fasti::assertNotScheduled($job);
 
-Fasti::assertScheduledAt();
-Fasti::assertNotScheduledAt();
+// Or by job class name
+
+Fasti::assertScheduled(SendGreetingEmail::class);
 
-Fasti::assertCancelled()
+// Add custom assertion by using callback
+
+Fasti::assertScheduled(function ($job) {
+    return $job->type === 'send_greeting_email';
+});
+
+// Many other assertions are available
+
+Fasti::assertNotScheduled($job);
+Fasti::assertScheduledAt($job, '2024-12-31 23:59:59');
+Fasti::assertNotScheduledAt($job, '2024-12-31 23:59:59');
+Fasti::assertCancelled($job);
 
 ```
 
@@ -120,7 +145,7 @@ Fasti operates as a scheduling layer for your Laravel jobs, focusing solely on w
 
 Instead, when the scheduled time arrives, Fasti simply hands off the job to Laravel's default `Bus`.
 
-It means you are free to use well-known Bus assertion methods shipped with Laravel. 
+It means you are free to use well-known Bus assertion methods shipped with Laravel when the job is dispatched to the queue. 
 
 ```php
 
@@ -149,7 +174,7 @@ class MyOwnModel extends Model implements SchedulableJob
 
 ```
 
-Create your own repository that implements the `FastiRepository` interface and bind it in your app service provider `register()` method.
+Create your own repository that implements the `FastiScheduledJobsRepository` interface and bind it in your app service provider `register()` method.
 
 ```php
 use Bashtannik\Fasti\Repositories\FastiRepository;
@@ -159,20 +184,31 @@ $this->app->bind(
     FastiScheduledJobsRepository::class,
     MyOwnRepository::class
 );
-```
-
-Or use more context-aware approach and switch your repository on the fly.
 
-```php
-use \Bashtannik\Fasti\Facades\Fasti;
+// Or if you just need to switch the model
 
-$repository = new MyOwnRepository(
-    user: $user,
-    company: $company,
+$this->app->bind(
+    FastiScheduledJobsRepository::class,
+    function () {
+        $repository = new FastiEloquentRepository;
+        $respository::$model = MyOwnModel::class;
+        
+        return $repository;
+    }
 );
+```
 
-Fasti::setRepository($repository);
+### Hint: store human-friendly job type name
+
+When using `FastiEloquentRepository` repository, by default it stores class name in the `type` field.
+However, you can define your own set of human-friendly names in your AppServiceProvider like you do with morph-many relationships.
+
+```php
+use Bashtannik\Fasti\Repositories\FastiEloquentRepository;
 
+FastiEloquentRepository::enforceTypeMapping([
+    'fake_job' => FakeJob::class,
+]);
 ```
 
-You are done, now Fasti will use your custom repository to manage scheduled jobs.
+This field doesn't affect the job execution but can be useful for debugging or logging purposes.
diff --git a/assets/cover1.png b/assets/cover1.png
new file mode 100644
index 0000000..6d4ef88
Binary files /dev/null and b/assets/cover1.png differ
diff --git a/composer.json b/composer.json
index 282f321..f37e2c1 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,7 @@
 {
   "name": "a-bashtannik/fasti",
   "description": "Laravel task scheduler with calendar-based management.",
+  "version": "dev-main",
   "keywords": [
     "laravel",
     "package",
@@ -43,6 +44,16 @@
       "Bashtannik\\Fasti\\Tests\\": "tests/"
     }
   },
+  "extra": {
+    "laravel": {
+      "providers": [
+        "Bashtannik\\Fasti\\Providers\\FastiServiceProvider"
+      ],
+      "aliases": {
+        "Fasti": "Bashtannik\\Fasti\\Facades\\Fasti"
+      }
+    }
+  },
   "require": {
     "php": "^8.2"
   },
diff --git a/database/migrations/2024_09_28_000000_create_scheduled_jobs_table.php b/database/migrations/2024_09_28_000000_create_scheduled_jobs_table.php
index 2aeced1..117f83f 100644
--- a/database/migrations/2024_09_28_000000_create_scheduled_jobs_table.php
+++ b/database/migrations/2024_09_28_000000_create_scheduled_jobs_table.php
@@ -10,6 +10,7 @@ public function up(): void
     {
         Schema::create('scheduled_jobs', function (Blueprint $table) {
             $table->id();
+            $table->string('type');
             $table->longText('payload');
             $table->dateTime('scheduled_at');
             $table->dateTime('cancelled_at')->nullable();
@@ -18,6 +19,6 @@ public function up(): void
 
     public function down(): void
     {
-        Schema::dropIfExists('scheduled_tasks');
+        Schema::dropIfExists('scheduled_jobs');
     }
 };
diff --git a/phpunit.xml b/phpunit.xml
index 5cec5dc..28cb2fd 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -12,7 +12,7 @@
     </testsuites>
     <source>
         <include>
-            <directory suffix=".php">./app</directory>
+            <directory suffix=".php">./src</directory>
         </include>
     </source>
 </phpunit>
diff --git a/src/Console/Commands/FastiListCommand.php b/src/Console/Commands/FastiListCommand.php
index c32568a..34d1f02 100644
--- a/src/Console/Commands/FastiListCommand.php
+++ b/src/Console/Commands/FastiListCommand.php
@@ -9,29 +9,29 @@
 
 class FastiListCommand extends Command
 {
-    protected $signature = 'fasti:list --all';
+    protected $signature = 'fasti:list';
 
     protected $description = 'List all scheduled jobs, add --all to include cancelled jobs.';
 
     public function handle(FastiService $fasti): void
     {
-        $all = $this->option('all');
-
-        $jobs = $all
-            ? $fasti->all()
-            : $fasti->scheduled(now());
+        $jobs = $fasti->all();
 
         table(
             array_values([
                 'ID',
+                'Type',
                 'Scheduled at',
-                'Canceled at',
+                'Cancelled at',
             ]),
-            $jobs->only([
-                'id',
-                'scheduled_at',
-                'canceled_at',
-            ])->toArray()
+            $jobs->map(function ($job) {
+                return [
+                    'id' => $job->id,
+                    'type' => $job->type,
+                    'scheduled_at' => $job->scheduled_at,
+                    'cancelled_at' => $job->cancelled_at ?? '-',
+                ];
+            })->toArray()
         );
     }
 }
diff --git a/src/Contracts/JobDispatcher.php b/src/Contracts/JobDispatcher.php
new file mode 100644
index 0000000..d34fb16
--- /dev/null
+++ b/src/Contracts/JobDispatcher.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bashtannik\Fasti\Contracts;
+
+interface JobDispatcher
+{
+    public function dispatch(mixed $job): mixed;
+
+    public function dispatchSync(mixed $job): mixed;
+}
diff --git a/src/Contracts/SchedulableJob.php b/src/Contracts/SchedulableJob.php
index a5e7d1c..cd9d4a9 100644
--- a/src/Contracts/SchedulableJob.php
+++ b/src/Contracts/SchedulableJob.php
@@ -10,6 +10,7 @@
  * A job that can be scheduled and canceled.
  *
  * @property int|string $id
+ * @property string $type
  * @property string $payload
  * @property CarbonInterface $scheduled_at
  * @property CarbonInterface|null $cancelled_at
diff --git a/src/Events/JobCancelled.php b/src/Events/JobCancelled.php
new file mode 100644
index 0000000..f842c01
--- /dev/null
+++ b/src/Events/JobCancelled.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bashtannik\Fasti\Events;
+
+use Bashtannik\Fasti\Contracts\SchedulableJob;
+
+class JobCancelled
+{
+    public function __construct(public SchedulableJob $job) {}
+}
diff --git a/src/Events/JobDispatched.php b/src/Events/JobDispatched.php
new file mode 100644
index 0000000..4100f7c
--- /dev/null
+++ b/src/Events/JobDispatched.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bashtannik\Fasti\Events;
+
+use Bashtannik\Fasti\Contracts\SchedulableJob;
+
+class JobDispatched
+{
+    public function __construct(public SchedulableJob $job) {}
+}
diff --git a/src/Events/JobScheduled.php b/src/Events/JobScheduled.php
new file mode 100644
index 0000000..90c4eba
--- /dev/null
+++ b/src/Events/JobScheduled.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bashtannik\Fasti\Events;
+
+use Bashtannik\Fasti\Contracts\SchedulableJob;
+
+class JobScheduled
+{
+    public function __construct(public SchedulableJob $job) {}
+}
diff --git a/src/Facades/Fasti.php b/src/Facades/Fasti.php
index 5d16627..989f9d1 100644
--- a/src/Facades/Fasti.php
+++ b/src/Facades/Fasti.php
@@ -6,6 +6,7 @@
 
 use Bashtannik\Fasti\Contracts\SchedulableJob;
 use Bashtannik\Fasti\Repositories\FastiArrayRepository;
+use Bashtannik\Fasti\Services\BusJobDispatcher;
 use Bashtannik\Fasti\Services\FastiService;
 use Carbon\CarbonInterface;
 use Closure;
@@ -16,9 +17,9 @@
 
 /**
  * @method static Collection<int, SchedulableJob> all()
- * @method static int|string schedule(object $job, DateTimeInterface|CarbonInterface $at)
- * @method static Collection<int, SchedulableJob> scheduled(DateTimeInterface|CarbonInterface $at)
- * @method static void cancel(int|string $id)
+ * @method static SchedulableJob schedule(object $job, DateTimeInterface|CarbonInterface $at)
+ * @method static Collection<int, SchedulableJob> scheduled(DateTimeInterface|CarbonInterface|string $at)
+ * @method static void cancel(int|string|SchedulableJob $id)
  * @method static Collection<int, SchedulableJob> cancelled()
  * @method static SchedulableJob find(int|string $id)
  * @method static void dispatch(int|string $id)
@@ -30,7 +31,7 @@ public static function fake(): void
     {
         $fakeRepository = new FastiArrayRepository;
 
-        $fake = new FastiService($fakeRepository);
+        $fake = new FastiService($fakeRepository, new BusJobDispatcher);
 
         static::swap($fake);
     }
@@ -40,64 +41,79 @@ protected static function getFacadeAccessor(): string
         return 'fasti';
     }
 
-    public static function assertScheduled(object $job, ?Closure $closure = null): void
+    public static function assertScheduled(object|string $job, ?Closure $closure = null): void
     {
         $scheduled = static::getFacadeRoot()->all();
 
         PHPUnit::assertTrue(
-            $scheduled->contains(fn (SchedulableJob $scheduledJob): bool => $scheduledJob->payload === serialize($job)
-                && (! $closure instanceof \Closure || $closure($scheduledJob))
-            ),
+            $scheduled->contains(
+                function (SchedulableJob $scheduledJob) use ($job, $closure): bool {
+                    $isSame = is_string($job) ? unserialize($scheduledJob->payload)::class === $job : $scheduledJob->payload === serialize($job);
+
+                    return $isSame && (! $closure instanceof \Closure || $closure($scheduledJob));
+                }),
             'The job was not scheduled.'
         );
     }
 
-    public static function assertNotScheduled(object $job, ?Closure $closure = null): void
+    public static function assertNotScheduled(object|string $job, ?Closure $closure = null): void
     {
         $scheduled = static::getFacadeRoot()->all();
 
         PHPUnit::assertFalse(
-            $scheduled->contains(fn (SchedulableJob $scheduledJob): bool => $scheduledJob->payload === serialize($job)
-                && (! $closure instanceof \Closure || $closure($scheduledJob))
-            ),
-            'The job was scheduled.'
+            $scheduled->contains(
+                function (SchedulableJob $scheduledJob) use ($job, $closure): bool {
+                    $isSame = is_string($job) ? unserialize($scheduledJob->payload)::class === $job : $scheduledJob->payload === serialize($job);
+
+                    return $isSame && (! $closure instanceof \Closure || $closure($scheduledJob));
+                }),
+            'The job was not scheduled.'
         );
     }
 
-    public static function assertScheduledAt(object $job, DateTimeInterface|CarbonInterface $at, ?Closure $closure = null): void
+    public static function assertScheduledAt(object|string $job, DateTimeInterface|CarbonInterface|string $at, ?Closure $closure = null): void
     {
         $scheduled = static::getFacadeRoot()->all();
 
         PHPUnit::assertTrue(
-            $scheduled->contains(fn (SchedulableJob $scheduledJob): bool => $scheduledJob->payload === serialize($job)
-                && $scheduledJob->scheduled_at->isSameMinute($at)
-                && (! $closure instanceof \Closure || $closure($scheduledJob))
-            ),
+            $scheduled->contains(function (SchedulableJob $scheduledJob) use ($job, $closure, $at): bool {
+                $isSame = is_string($job) ? unserialize($scheduledJob->payload)::class === $job : $scheduledJob->payload === serialize($job);
+
+                return $isSame
+                    && $scheduledJob->scheduled_at->isSameMinute($at)
+                    && (! $closure instanceof \Closure || $closure($scheduledJob));
+            }),
             'The job was not scheduled at the specified time.'
         );
     }
 
-    public static function assertNotScheduledAt(object $job, DateTimeInterface|CarbonInterface $at, ?Closure $closure = null): void
+    public static function assertNotScheduledAt(object|string $job, DateTimeInterface|CarbonInterface|string $at, ?Closure $closure = null): void
     {
         $scheduled = static::getFacadeRoot()->all();
 
         PHPUnit::assertFalse(
-            $scheduled->contains(fn (SchedulableJob $scheduledJob): bool => $scheduledJob->payload === serialize($job)
-                && $scheduledJob->scheduled_at->isSameMinute($at)
-                && (! $closure instanceof \Closure || $closure($scheduledJob))
-            ),
+            $scheduled->contains(function (SchedulableJob $scheduledJob) use ($job, $closure, $at): bool {
+                $isSame = is_string($job) ? unserialize($scheduledJob->payload)::class === $job : $scheduledJob->payload === serialize($job);
+
+                return $isSame
+                    && $scheduledJob->scheduled_at->isSameMinute($at)
+                    && (! $closure instanceof \Closure || $closure($scheduledJob));
+            }),
             'The job was scheduled at the specified time.'
         );
     }
 
-    public static function assertCancelled(object $job, ?Closure $closure = null): void
+    public static function assertCancelled(object|string $job, ?Closure $closure = null): void
     {
         $canceled = static::getFacadeRoot()->cancelled();
 
         PHPUnit::assertTrue(
-            $canceled->contains(fn (SchedulableJob $canceledJob): bool => $canceledJob->payload === serialize($job)
-                && (! $closure instanceof \Closure || $closure($canceledJob))
-            ),
+            $canceled->contains(
+                function (SchedulableJob $scheduledJob) use ($job, $closure): bool {
+                    $isSame = is_string($job) ? unserialize($scheduledJob->payload)::class === $job : $scheduledJob->payload === serialize($job);
+
+                    return $isSame && (! $closure instanceof \Closure || $closure($scheduledJob));
+                }),
             'The job was not canceled.'
         );
     }
diff --git a/src/Models/ScheduledJob.php b/src/Models/ScheduledJob.php
index 4bed25d..57bf312 100644
--- a/src/Models/ScheduledJob.php
+++ b/src/Models/ScheduledJob.php
@@ -8,6 +8,7 @@
 
 /**
  * @property int $id
+ * @property string $type
  * @property string $payload
  * @property CarbonImmutable|null $cancelled_at
  * @property CarbonImmutable $scheduled_at
@@ -18,6 +19,8 @@ class ScheduledJob extends Model implements SchedulableJob
 
     protected $guarded = [];
 
+    protected $hidden = ['payload'];
+
     /**
      * @return array<string,string>
      */
diff --git a/src/Providers/FastiServiceProvider.php b/src/Providers/FastiServiceProvider.php
index 545c526..b561767 100644
--- a/src/Providers/FastiServiceProvider.php
+++ b/src/Providers/FastiServiceProvider.php
@@ -2,10 +2,14 @@
 
 namespace Bashtannik\Fasti\Providers;
 
+use Bashtannik\Fasti\Console\Commands\FastiListCommand;
 use Bashtannik\Fasti\Console\Commands\FastiTickCommand;
+use Bashtannik\Fasti\Contracts\JobDispatcher;
 use Bashtannik\Fasti\Repositories\FastiEloquentRepository;
 use Bashtannik\Fasti\Repositories\FastiScheduledJobsRepository;
+use Bashtannik\Fasti\Services\BusJobDispatcher;
 use Bashtannik\Fasti\Services\FastiService;
+use Illuminate\Foundation\Console\AboutCommand;
 use Illuminate\Support\ServiceProvider;
 
 class FastiServiceProvider extends ServiceProvider
@@ -21,13 +25,24 @@ public function register(): void
             FastiService::class,
             'fasti'
         );
+
+        $this->app->when(FastiService::class)
+            ->needs(JobDispatcher::class)
+            ->give(BusJobDispatcher::class);
     }
 
     public function boot(): void
     {
+        AboutCommand::add('Fasti', fn (): array => ['Version' => '1.0.0']);
+
         if ($this->app->runningInConsole()) {
             $this->commands([
                 FastiTickCommand::class,
+                FastiListCommand::class,
+            ]);
+
+            $this->publishesMigrations([
+                __DIR__.'/../../database/migrations' => database_path('migrations'),
             ]);
         }
     }
diff --git a/src/Repositories/FastiArrayRepository.php b/src/Repositories/FastiArrayRepository.php
index 4a29cb4..e86c8bf 100644
--- a/src/Repositories/FastiArrayRepository.php
+++ b/src/Repositories/FastiArrayRepository.php
@@ -23,16 +23,19 @@ public function all(): Collection
         return collect($this->jobsToFake);
     }
 
-    public function store(object $job, DateTimeInterface|CarbonInterface $dateTime): int|string
+    public function store(object $job, DateTimeInterface|CarbonInterface $dateTime): SchedulableJob
     {
-        $this->jobsToFake[] = new GenericScheduledJob(
+        $scheduledJob = new GenericScheduledJob(
             id: count($this->jobsToFake),
+            type: $job::class,
             payload: serialize($job),
             scheduled_at: Carbon::instance($dateTime),
             cancelled_at: null,
         );
 
-        return count($this->jobsToFake) - 1;
+        $this->jobsToFake[] = $scheduledJob;
+
+        return $scheduledJob;
     }
 
     public function scheduled(DateTimeInterface|CarbonInterface $at): Collection
@@ -42,9 +45,13 @@ public function scheduled(DateTimeInterface|CarbonInterface $at): Collection
         );
     }
 
-    public function cancel(int|string $id): void
+    public function cancel(int|string|SchedulableJob $id): SchedulableJob
     {
-        $this->jobsToFake[$id]->cancelled_at = now();
+        $key = $id instanceof SchedulableJob ? $id->id : $id;
+
+        $this->jobsToFake[$key]->cancelled_at = now();
+
+        return $this->jobsToFake[$key];
     }
 
     public function cancelled(): Collection
diff --git a/src/Repositories/FastiEloquentRepository.php b/src/Repositories/FastiEloquentRepository.php
index 15f41d8..2f6cdd5 100644
--- a/src/Repositories/FastiEloquentRepository.php
+++ b/src/Repositories/FastiEloquentRepository.php
@@ -15,26 +15,46 @@
 
 class FastiEloquentRepository implements FastiScheduledJobsRepository
 {
+    public static string $model = ScheduledJob::class;
+
+    /**
+     * @var array<string, class-string>
+     */
+    public static array $morphMap = [];
+
     public function all(): Collection
     {
-        return ScheduledJob::all();
+        return static::$model::all();
     }
 
-    public function store(object $job, DateTimeInterface|CarbonInterface $dateTime): int|string
+    /**
+     * @param  array<string, class-string>  $morphMap
+     */
+    public static function enforceTypeMap(array $morphMap): void
     {
-        $task = new ScheduledJob;
+        static::$morphMap = $morphMap;
+    }
 
-        if ($job instanceof ShouldBeEncrypted) {
-            $task->payload = encrypt(serialize(clone $job));
+    public function store(object $job, DateTimeInterface|CarbonInterface $dateTime): SchedulableJob
+    {
+        $scheduledJob = new static::$model;
+
+        if (count(self::$morphMap) && isset(array_flip(self::$morphMap)[$job::class])) {
+            $scheduledJob->type = array_flip(self::$morphMap)[$job::class];
         } else {
-            $task->payload = serialize(clone $job);
+            $scheduledJob->type = $job::class;
         }
 
-        $task->scheduled_at = CarbonImmutable::instance($dateTime);
+        if ($job instanceof ShouldBeEncrypted) {
+            $scheduledJob->payload = encrypt(serialize(clone $job));
+        } else {
+            $scheduledJob->payload = serialize(clone $job);
+        }
 
-        $task->save();
+        $scheduledJob->scheduled_at = CarbonImmutable::instance($dateTime);
+        $scheduledJob->save();
 
-        return $task->id;
+        return $scheduledJob;
     }
 
     public function scheduled(DateTimeInterface|CarbonInterface $at): Collection
@@ -42,26 +62,28 @@ public function scheduled(DateTimeInterface|CarbonInterface $at): Collection
         $from = Carbon::instance($at)->startOf('minute');
         $to = Carbon::instance($at)->endOf('minute');
 
-        return ScheduledJob::query()
+        return static::$model::query()
             ->whereNull('cancelled_at')
             ->whereBetween('scheduled_at', [$from, $to])->get();
     }
 
-    public function cancel(string|int $id): void
+    public function cancel(string|int|SchedulableJob $id): SchedulableJob
     {
-        $job = ScheduledJob::query()->findOrFail($id);
+        $scheduledJob = static::$model::query()->findOrFail($id instanceof SchedulableJob ? $id->id : $id);
+
+        $scheduledJob->cancelled_at = now()->toImmutable();
+        $scheduledJob->save();
 
-        $job->cancelled_at = now()->toImmutable();
-        $job->save();
+        return $scheduledJob;
     }
 
     public function cancelled(): Collection
     {
-        return ScheduledJob::query()->whereNotNull('cancelled_at')->get();
+        return static::$model::query()->whereNotNull('cancelled_at')->get();
     }
 
     public function find(int|string $id): SchedulableJob
     {
-        return ScheduledJob::query()->findOrFail($id);
+        return static::$model::query()->findOrFail($id);
     }
 }
diff --git a/src/Repositories/FastiScheduledJobsRepository.php b/src/Repositories/FastiScheduledJobsRepository.php
index 6f372b0..a348839 100644
--- a/src/Repositories/FastiScheduledJobsRepository.php
+++ b/src/Repositories/FastiScheduledJobsRepository.php
@@ -16,14 +16,14 @@ interface FastiScheduledJobsRepository
      */
     public function all(): Collection;
 
-    public function store(object $job, DateTimeInterface|CarbonInterface $dateTime): int|string;
+    public function store(object $job, DateTimeInterface|CarbonInterface $dateTime): SchedulableJob;
 
     /**
      * @return Collection<int, SchedulableJob>|Collection<string, SchedulableJob>
      */
     public function scheduled(DateTimeInterface|CarbonInterface $at): Collection;
 
-    public function cancel(int|string $id): void;
+    public function cancel(int|string|SchedulableJob $id): SchedulableJob;
 
     /**
      * @return Collection<int, SchedulableJob>|Collection<string, SchedulableJob>
diff --git a/src/Repositories/GenericScheduledJob.php b/src/Repositories/GenericScheduledJob.php
index ba03948..0cd8267 100644
--- a/src/Repositories/GenericScheduledJob.php
+++ b/src/Repositories/GenericScheduledJob.php
@@ -7,12 +7,42 @@
 use Bashtannik\Fasti\Contracts\SchedulableJob;
 use Carbon\CarbonInterface;
 
+/**
+ * @codeCoverageIgnore
+ */
 class GenericScheduledJob implements SchedulableJob
 {
+    /**
+     * @var array<string, mixed>
+     */
+    private array $properties = [];
+
     public function __construct(
         public int|string $id,
+        public string $type,
         public string $payload,
         public CarbonInterface $scheduled_at,
         public ?CarbonInterface $cancelled_at,
     ) {}
+
+    public function __set(string $name, mixed $value): void
+    {
+        if (! property_exists($this, $name)) {
+            $this->properties[$name] = $value;
+        }
+    }
+
+    public function __get(string $name): mixed
+    {
+        if (property_exists($this, $name)) {
+            return $this->$name;
+        }
+
+        return $this->properties[$name] ?? null;
+    }
+
+    public function __call(string $name, mixed $arguments): void
+    {
+        // Just silently ignores the call
+    }
 }
diff --git a/src/Services/BusJobDispatcher.php b/src/Services/BusJobDispatcher.php
new file mode 100644
index 0000000..2c27683
--- /dev/null
+++ b/src/Services/BusJobDispatcher.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bashtannik\Fasti\Services;
+
+use Bashtannik\Fasti\Contracts\JobDispatcher;
+use Illuminate\Support\Facades\Bus;
+
+class BusJobDispatcher implements JobDispatcher
+{
+    public function dispatch(mixed $job): mixed
+    {
+        return Bus::dispatch($job);
+    }
+
+    public function dispatchSync(mixed $job): mixed
+    {
+        return Bus::dispatchSync($job);
+    }
+}
diff --git a/src/Services/FastiService.php b/src/Services/FastiService.php
index 44939df..bccefce 100644
--- a/src/Services/FastiService.php
+++ b/src/Services/FastiService.php
@@ -4,19 +4,26 @@
 
 namespace Bashtannik\Fasti\Services;
 
-use Bashtannik\Fasti\Models\ScheduledJob;
+use Bashtannik\Fasti\Contracts\JobDispatcher;
+use Bashtannik\Fasti\Contracts\SchedulableJob;
+use Bashtannik\Fasti\Events\JobCancelled;
+use Bashtannik\Fasti\Events\JobDispatched;
+use Bashtannik\Fasti\Events\JobScheduled;
 use Bashtannik\Fasti\Repositories\FastiScheduledJobsRepository;
 use Carbon\CarbonInterface;
 use DateTimeInterface;
 use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Support\Carbon;
 use Illuminate\Support\Collection;
-use Illuminate\Support\Facades\Bus;
+use Illuminate\Support\Facades\Event;
 use RuntimeException;
-use stdClass;
 
 class FastiService
 {
-    public function __construct(protected FastiScheduledJobsRepository $repository) {}
+    public function __construct(
+        protected FastiScheduledJobsRepository $repository,
+        protected JobDispatcher $dispatcher,
+    ) {}
 
     /**
      * Retrieve the repository.
@@ -26,22 +33,10 @@ public function getRepository(): FastiScheduledJobsRepository
         return $this->repository;
     }
 
-    /**
-     * Set the repository.
-     *
-     * @return $this
-     */
-    public function setRepository(FastiScheduledJobsRepository $repository): static
-    {
-        $this->repository = $repository;
-
-        return $this;
-    }
-
     /**
      * Retrieve all scheduled jobs.
      *
-     * @return Collection<int, ScheduledJob>|Collection<int|string, array{id: int|string, payload: stdClass, scheduled_at: CarbonInterface, canceled_at: CarbonInterface|null}>
+     * @return Collection<int, SchedulableJob>
      */
     public function all(): Collection
     {
@@ -51,13 +46,22 @@ public function all(): Collection
     /**
      * Schedule a job to run at a specific time.
      */
-    public function schedule(object $job, DateTimeInterface|CarbonInterface $at): int|string
+    public function schedule(object $job, DateTimeInterface|CarbonInterface|string $at): SchedulableJob
     {
-        return $this->repository->store($job, $at);
+        if (is_string($at)) {
+            $at = Carbon::parse($at);
+        }
+
+        $scheduledJob = $this->repository->store($job, $at);
+
+        Event::dispatch(new JobScheduled($scheduledJob));
+
+        return $scheduledJob;
     }
 
     /**
      * List all scheduled jobs for a specific minute.
+     * @return Collection<int, SchedulableJob>
      */
     public function scheduled(DateTimeInterface|CarbonInterface $at): Collection
     {
@@ -67,22 +71,25 @@ public function scheduled(DateTimeInterface|CarbonInterface $at): Collection
     /**
      * Cancel a scheduled job.
      */
-    public function cancel(int|string $id): void
+    public function cancel(int|string|SchedulableJob $id): void
     {
-        $this->repository->cancel($id);
+        $scheduledJob = $this->repository->cancel($id);
+
+        Event::dispatch(new JobCancelled($scheduledJob));
     }
 
     /**
      * List all canceled jobs.
+     * @return Collection<int, SchedulableJob>
      */
     public function cancelled(): Collection
     {
         return $this->repository->cancelled();
     }
 
-    public function dispatch(int|string $id): void
+    public function dispatch(int|string|SchedulableJob $id): void
     {
-        $scheduledJob = $this->repository->find($id);
+        $scheduledJob = $id instanceof SchedulableJob ? $id : $this->repository->find($id);
 
         if (! str_starts_with((string) $scheduledJob->payload, 'O:')) {
             $data = decrypt($scheduledJob->payload);
@@ -96,10 +103,12 @@ public function dispatch(int|string $id): void
             $instance = unserialize($scheduledJob->payload);
         }
 
+        Event::dispatch(new JobDispatched($scheduledJob));
+
         if ($instance instanceof ShouldQueue) {
-            Bus::dispatch($instance);
+            $this->dispatcher->dispatch($instance);
         } else {
-            Bus::dispatchSync($instance);
+            $this->dispatcher->dispatchSync($instance);
         }
     }
 }
diff --git a/tests/Unit/FastiFacadeTest.php b/tests/Unit/FastiFacadeTest.php
index 58278b4..00a19fc 100644
--- a/tests/Unit/FastiFacadeTest.php
+++ b/tests/Unit/FastiFacadeTest.php
@@ -7,6 +7,7 @@
 use Bashtannik\Fasti\Facades\Fasti;
 use Bashtannik\Fasti\Repositories\FastiArrayRepository;
 use Bashtannik\Fasti\Services\FastiService;
+use Bashtannik\Fasti\Tests\Fake\FakeEncryptedJob;
 use Bashtannik\Fasti\Tests\Fake\FakeJob;
 use Bashtannik\Fasti\Tests\TestCase;
 use DateTime;
@@ -41,6 +42,8 @@ public function test_can_assert_scheduled_state(): void
 
         $notScheduledJob = new FakeJob(2);
 
+        $notScheduledEncryptedJob = new FakeEncryptedJob; // Other instance
+
         $dateTime = new DateTime('now');
 
         // act
@@ -51,8 +54,17 @@ public function test_can_assert_scheduled_state(): void
 
         Fasti::assertScheduled($job);
         Fasti::assertScheduledAt($job, $dateTime);
+        Fasti::assertScheduledAt($job, $dateTime->format('Y-m-d H:i:s'));
         Fasti::assertNotScheduled($notScheduledJob);
         Fasti::assertNotScheduledAt($job, new DateTime('tomorrow'));
+        Fasti::assertNotScheduledAt($job, (new DateTime('tomorrow'))->format('Y-m-d H:i:s'));
+
+        Fasti::assertScheduled($job::class);
+        Fasti::assertScheduledAt($job::class, $dateTime);
+        Fasti::assertScheduledAt($job::class, $dateTime->format('Y-m-d H:i:s'));
+        Fasti::assertNotScheduled($notScheduledEncryptedJob::class); // Other instance
+        Fasti::assertNotScheduledAt($job::class, new DateTime('tomorrow'));
+        Fasti::assertNotScheduledAt($job::class, (new DateTime('tomorrow'))->format('Y-m-d H:i:s'));
     }
 
     public function test_can_assert_canceled_state(): void
diff --git a/tests/Unit/FastiServiceArrayRepositoryTest.php b/tests/Unit/FastiServiceArrayRepositoryTest.php
index f1c83d4..a43139a 100644
--- a/tests/Unit/FastiServiceArrayRepositoryTest.php
+++ b/tests/Unit/FastiServiceArrayRepositoryTest.php
@@ -5,6 +5,7 @@
 namespace Bashtannik\Fasti\Tests\Unit;
 
 use Bashtannik\Fasti\Repositories\FastiArrayRepository;
+use Bashtannik\Fasti\Services\BusJobDispatcher;
 use Bashtannik\Fasti\Services\FastiService;
 use Bashtannik\Fasti\Tests\Fake\FakeEncryptedJob;
 use Bashtannik\Fasti\Tests\Fake\FakeJob;
@@ -22,10 +23,10 @@ protected function setUp(): void
     {
         parent::setUp();
 
-        self::$fasti = new FastiService(new FastiArrayRepository);
+        self::$fasti = new FastiService(new FastiArrayRepository, new BusJobDispatcher);
     }
 
-    public function test_can_schedule_job_date_time(): void
+    public function test_can_schedule_job_at_date_time(): void
     {
         // arrange
 
@@ -35,17 +36,17 @@ public function test_can_schedule_job_date_time(): void
 
         // act
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
-        $scheduledJob = self::$fasti->getRepository()->find($id);
+        $scheduledJob = self::$fasti->getRepository()->find($scheduledJob->id);
 
         $this->assertEquals(serialize($job), $scheduledJob->payload, 'The job should be the one we scheduled.');
         $this->assertEquals($dateTime->format('Y-m-d H:i:s'), $scheduledJob->scheduled_at->format('Y-m-d H:i:s'), 'The scheduled date should be the one we scheduled.');
     }
 
-    public function test_can_schedule_job_date_time_immutable(): void
+    public function test_can_schedule_job_at_date_time_immutable(): void
     {
         // arrange
 
@@ -55,17 +56,17 @@ public function test_can_schedule_job_date_time_immutable(): void
 
         // act
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
-        $scheduledJob = self::$fasti->getRepository()->find($id);
+        $scheduledJob = self::$fasti->getRepository()->find($scheduledJob->id);
 
         $this->assertEquals(serialize($job), $scheduledJob->payload, 'The job should be the one we scheduled.');
         $this->assertEquals($dateTime->format('Y-m-d H:i:s'), $scheduledJob->scheduled_at->format('Y-m-d H:i:s'), 'The scheduled date should be the one we scheduled.');
     }
 
-    public function test_can_schedule_job_carbon(): void
+    public function test_can_schedule_job_at_carbon(): void
     {
         // arrange
 
@@ -75,17 +76,17 @@ public function test_can_schedule_job_carbon(): void
 
         // act
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
-        $scheduledJob = self::$fasti->getRepository()->find($id);
+        $scheduledJob = self::$fasti->getRepository()->find($scheduledJob->id);
 
         $this->assertEquals(serialize($job), $scheduledJob->payload, 'The job should be the one we scheduled.');
         $this->assertEquals($dateTime->format('Y-m-d H:i:s'), $scheduledJob->scheduled_at->format('Y-m-d H:i:s'), 'The scheduled date should be the one we scheduled.');
     }
 
-    public function test_can_schedule_job_carbon_immutable(): void
+    public function test_can_schedule_job_at_carbon_immutable(): void
     {
         // arrange
 
@@ -95,11 +96,31 @@ public function test_can_schedule_job_carbon_immutable(): void
 
         // act
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
-        $scheduledJob = self::$fasti->getRepository()->find($id);
+        $scheduledJob = self::$fasti->getRepository()->find($scheduledJob->id);
+
+        $this->assertEquals(serialize($job), $scheduledJob->payload, 'The job should be the one we scheduled.');
+        $this->assertEquals($dateTime->format('Y-m-d H:i:s'), $scheduledJob->scheduled_at->format('Y-m-d H:i:s'), 'The scheduled date should be the one we scheduled.');
+    }
+
+    public function test_can_schedule_job_at_string(): void
+    {
+        // arrange
+
+        $job = new FakeJob;
+
+        $dateTime = now();
+
+        // act
+
+        $scheduledJob = self::$fasti->schedule($job, $dateTime->toDateTimeString());
+
+        // assert
+
+        $scheduledJob = self::$fasti->getRepository()->find($scheduledJob->id);
 
         $this->assertEquals(serialize($job), $scheduledJob->payload, 'The job should be the one we scheduled.');
         $this->assertEquals($dateTime->format('Y-m-d H:i:s'), $scheduledJob->scheduled_at->format('Y-m-d H:i:s'), 'The scheduled date should be the one we scheduled.');
@@ -115,11 +136,11 @@ public function test_can_schedule_encrypted_job(): void
 
         // act
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
-        $scheduledJob = self::$fasti->getRepository()->find($id);
+        $scheduledJob = self::$fasti->getRepository()->find($scheduledJob->id);
 
         $this->assertEquals(serialize($job), $scheduledJob->payload, 'The job should be the one we scheduled.');
         $this->assertEquals($dateTime->format('Y-m-d H:i:s'), $scheduledJob->scheduled_at->format('Y-m-d H:i:s'), 'The scheduled date should be the one we scheduled.');
@@ -134,7 +155,7 @@ public function test_can_get_scheduled_jobs(): void
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         $cancelledId = self::$fasti->schedule($scheduledJob, $dateTime);
         self::$fasti->cancel($cancelledId);
@@ -146,7 +167,7 @@ public function test_can_get_scheduled_jobs(): void
         // assert
 
         $this->assertCount(1, $jobs, 'Only the scheduled job should be returned.');
-        $this->assertEquals($id, $jobs->first()->id, 'The scheduled job should be the one we scheduled.');
+        $this->assertEquals($scheduledJob, $jobs->first(), 'The scheduled job should be the one we scheduled.');
     }
 
     public function test_can_cancel_scheduled_job(): void
@@ -157,15 +178,15 @@ public function test_can_cancel_scheduled_job(): void
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // act
 
-        self::$fasti->cancel($id);
+        self::$fasti->cancel($scheduledJob);
 
         // assert
 
-        $job = self::$fasti->getRepository()->find($id);
+        $job = self::$fasti->getRepository()->find($scheduledJob->id);
 
         $this->assertNotNull($job->cancelled_at, 'The job should be canceled.');
     }
@@ -181,8 +202,8 @@ public function test_can_get_cancelled_jobs(): void
 
         self::$fasti->schedule($job, $dateTime);
 
-        $id = self::$fasti->schedule($canceledJob, $dateTime);
-        self::$fasti->cancel($id);
+        $scheduledJob = self::$fasti->schedule($canceledJob, $dateTime);
+        self::$fasti->cancel($scheduledJob);
 
         // act
 
@@ -191,7 +212,7 @@ public function test_can_get_cancelled_jobs(): void
         // assert
 
         $this->assertCount(1, $jobs, 'Only the canceled job should be returned.');
-        $this->assertEquals($id, $jobs->first()->id, 'The canceled job should be the one we canceled.');
+        $this->assertEquals($scheduledJob, $jobs->first(), 'The canceled job should be the one we canceled.');
     }
 
     public function test_can_get_all_jobs(): void
@@ -205,8 +226,8 @@ public function test_can_get_all_jobs(): void
 
         self::$fasti->schedule($job, $dateTime);
 
-        $id = self::$fasti->schedule($canceledJob, $dateTime);
-        self::$fasti->cancel($id);
+        $scheduledJob = self::$fasti->schedule($canceledJob, $dateTime);
+        self::$fasti->cancel($scheduledJob);
 
         // act
 
@@ -227,11 +248,11 @@ public function test_can_dispatch_sync_job(): void
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // act
 
-        self::$fasti->dispatch($id);
+        self::$fasti->dispatch($scheduledJob);
 
         // assert
 
@@ -248,11 +269,11 @@ public function test_can_dispatch_queued_job(): void
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // act
 
-        self::$fasti->dispatch($id);
+        self::$fasti->dispatch($scheduledJob);
 
         // assert
 
@@ -269,11 +290,11 @@ public function test_can_dispatch_encrypted_job(): void
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // act
 
-        self::$fasti->dispatch($id);
+        self::$fasti->dispatch($scheduledJob);
 
         // assert
 
diff --git a/tests/Unit/FastiServiceEloquentRepositoryTest.php b/tests/Unit/FastiServiceEloquentRepositoryTest.php
index 2e35f65..0300d72 100644
--- a/tests/Unit/FastiServiceEloquentRepositoryTest.php
+++ b/tests/Unit/FastiServiceEloquentRepositoryTest.php
@@ -2,7 +2,12 @@
 
 namespace Bashtannik\Fasti\Tests\Unit;
 
+use Bashtannik\Fasti\Events\JobCancelled;
+use Bashtannik\Fasti\Events\JobDispatched;
+use Bashtannik\Fasti\Events\JobScheduled;
+use Bashtannik\Fasti\Models\ScheduledJob;
 use Bashtannik\Fasti\Repositories\FastiEloquentRepository;
+use Bashtannik\Fasti\Services\BusJobDispatcher;
 use Bashtannik\Fasti\Services\FastiService;
 use Bashtannik\Fasti\Tests\Fake\FakeEncryptedJob;
 use Bashtannik\Fasti\Tests\Fake\FakeJob;
@@ -11,6 +16,7 @@
 use DateTime;
 use DateTimeImmutable;
 use Illuminate\Support\Facades\Bus;
+use Illuminate\Support\Facades\Event;
 
 class FastiServiceEloquentRepositoryTest extends TestCase
 {
@@ -20,106 +26,196 @@ protected function setUp(): void
     {
         parent::setUp();
 
-        self::$fasti = new FastiService(new FastiEloquentRepository);
+        self::$fasti = new FastiService(new FastiEloquentRepository, new BusJobDispatcher);
     }
 
-    public function test_can_schedule_job_date_time(): void
+    public function test_can_schedule_job_at_date_time(): void
     {
         // arrange
 
+        Event::fake(JobScheduled::class);
+
         $job = new FakeJob;
 
         $dateTime = new DateTime('now');
 
         // act
 
-        self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
+        $this->assertInstanceOf(ScheduledJob::class, $scheduledJob);
+
         $this->assertDatabaseHas('scheduled_jobs', [
             'payload' => serialize($job),
+            'type' => FakeJob::class,
             'scheduled_at' => $dateTime->format('Y-m-d H:i:s'),
         ]);
+
+        Event::assertDispatched(JobScheduled::class);
     }
 
-    public function test_can_schedule_job_date_time_immutable(): void
+    public function test_can_schedule_job_at_date_time_immutable(): void
     {
         // arrange
 
+        Event::fake(JobScheduled::class);
+
         $job = new FakeJob;
 
         $dateTime = new DateTimeImmutable('now');
 
         // act
 
-        self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
+        $this->assertInstanceOf(ScheduledJob::class, $scheduledJob);
+
         $this->assertDatabaseHas('scheduled_jobs', [
             'payload' => serialize($job),
             'scheduled_at' => $dateTime->format('Y-m-d H:i:s'),
         ]);
+
+        Event::assertDispatched(JobScheduled::class);
     }
 
-    public function test_can_schedule_job_carbon(): void
+    public function test_can_schedule_job_at_carbon(): void
     {
         // arrange
 
+        Event::fake(JobScheduled::class);
+
         $job = new FakeJob;
 
         $dateTime = now();
 
         // act
 
-        self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
+        $this->assertInstanceOf(ScheduledJob::class, $scheduledJob);
+
         $this->assertDatabaseHas('scheduled_jobs', [
             'payload' => serialize($job),
             'scheduled_at' => $dateTime->format('Y-m-d H:i:s'),
         ]);
+
+        Event::assertDispatched(JobScheduled::class);
     }
 
-    public function test_can_schedule_job_carbon_immutable(): void
+    public function test_can_schedule_job_at_carbon_immutable(): void
     {
         // arrange
 
+        Event::fake(JobScheduled::class);
+
+        $job = new FakeJob;
+
+        $dateTime = now()->toImmutable();
+
+        // act
+
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
+
+        // assert
+
+        $this->assertInstanceOf(ScheduledJob::class, $scheduledJob);
+
+        // assert
+
+        $this->assertDatabaseHas('scheduled_jobs', [
+            'payload' => serialize($job),
+            'scheduled_at' => $dateTime->format('Y-m-d H:i:s'),
+        ]);
+
+        Event::assertDispatched(JobScheduled::class);
+    }
+
+    public function test_can_schedule_job_at_string(): void
+    {
+        // arrange
+
+        Event::fake(JobScheduled::class);
+
         $job = new FakeJob;
 
         $dateTime = now();
 
         // act
 
-        self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
+        $this->assertInstanceOf(ScheduledJob::class, $scheduledJob);
+
         $this->assertDatabaseHas('scheduled_jobs', [
             'payload' => serialize($job),
             'scheduled_at' => $dateTime->format('Y-m-d H:i:s'),
         ]);
+
+        Event::assertDispatched(JobScheduled::class);
     }
 
     public function test_can_schedule_encrypted_job(): void
     {
         // arrange
 
+        Event::fake(JobScheduled::class);
+
         $job = new FakeEncryptedJob;
 
         $dateTime = new DateTime('now');
 
         // act
 
-        self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // assert
 
+        $this->assertInstanceOf(ScheduledJob::class, $scheduledJob);
+
         $first = self::$fasti->getRepository()->all()->first();
 
         $this->assertEquals(serialize($job), decrypt($first->payload), 'The job should be encrypted.');
+
+        Event::assertDispatched(JobScheduled::class);
+    }
+
+    public function test_can_schedule_mapped_type_job(): void
+    {
+        // arrange
+
+        Event::fake(JobScheduled::class);
+
+        $job = new FakeJob;
+
+        $dateTime = new DateTime('now');
+
+        FastiEloquentRepository::enforceTypeMap([
+            'fake_job' => FakeJob::class,
+        ]);
+
+        // act
+
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
+
+        // assert
+
+        $this->assertInstanceOf(ScheduledJob::class, $scheduledJob);
+
+        $this->assertDatabaseHas('scheduled_jobs', [
+            'payload' => serialize($job),
+            'type' => 'fake_job',
+            'scheduled_at' => $dateTime->format('Y-m-d H:i:s'),
+        ]);
+
+        Event::assertDispatched(JobScheduled::class);
     }
 
     public function test_can_get_scheduled_jobs(): void
@@ -127,14 +223,14 @@ public function test_can_get_scheduled_jobs(): void
         // arrange
 
         $job = new FakeJob;
-        $scheduledJob = new FakeJob;
+        $job2 = new FakeJob;
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob2 = self::$fasti->schedule($job2, $dateTime);
 
-        $cancelledId = self::$fasti->schedule($scheduledJob, $dateTime);
-        self::$fasti->cancel($cancelledId);
+        self::$fasti->cancel($scheduledJob2->id);
 
         // act
 
@@ -143,29 +239,33 @@ public function test_can_get_scheduled_jobs(): void
         // assert
 
         $this->assertCount(1, $jobs, 'Both jobs should be returned.');
-        $this->assertEquals($id, $jobs->first()->id, 'The scheduled job should be the one we scheduled.');
+        $this->assertEquals($scheduledJob->id, $jobs->first()->id, 'The scheduled job should be the one we scheduled.');
     }
 
     public function test_can_cancel_scheduled_job(): void
     {
         // arrange
 
+        Event::fake(JobCancelled::class);
+
         $job = new FakeJob;
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // act
 
-        self::$fasti->cancel($id);
+        self::$fasti->cancel($scheduledJob->id);
 
         // assert
 
         $this->assertDatabaseHas('scheduled_jobs', [
-            'id' => $id,
+            'id' => $scheduledJob->id,
             'cancelled_at' => now()->format('Y-m-d H:i:s'),
         ]);
+
+        Event::assertDispatched(JobCancelled::class);
     }
 
     public function test_can_get_cancelled_jobs(): void
@@ -173,14 +273,14 @@ public function test_can_get_cancelled_jobs(): void
         // arrange
 
         $job = new FakeJob;
-        $canceledJob = new FakeJob;
+        $job2 = new FakeJob;
 
         $dateTime = new DateTime('now');
 
         self::$fasti->schedule($job, $dateTime);
 
-        $id = self::$fasti->schedule($canceledJob, $dateTime);
-        self::$fasti->cancel($id);
+        $scheduledJob = self::$fasti->schedule($job2, $dateTime);
+        self::$fasti->cancel($scheduledJob->id);
 
         // act
 
@@ -189,7 +289,7 @@ public function test_can_get_cancelled_jobs(): void
         // assert
 
         $this->assertCount(1, $jobs, 'Only the canceled job should be returned.');
-        $this->assertEquals($id, $jobs->first()->id, 'The canceled job should be the one we canceled.');
+        $this->assertEquals($scheduledJob->id, $jobs->first()->id, 'The canceled job should be the one we canceled.');
     }
 
     public function test_can_get_all_jobs(): void
@@ -197,14 +297,14 @@ public function test_can_get_all_jobs(): void
         // arrange
 
         $job = new FakeJob;
-        $canceledJob = new FakeJob;
+        $job2 = new FakeJob;
 
         $dateTime = new DateTime('now');
 
         self::$fasti->schedule($job, $dateTime);
 
-        $id = self::$fasti->schedule($canceledJob, $dateTime);
-        self::$fasti->cancel($id);
+        $scheduledJob = self::$fasti->schedule($job2, $dateTime);
+        self::$fasti->cancel($scheduledJob->id);
 
         // act
 
@@ -220,20 +320,22 @@ public function test_can_dispatch_sync_job(): void
         // arrange
 
         Bus::fake(FakeJob::class);
+        Event::fake(JobDispatched::class);
 
         $job = new FakeJob;
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // act
 
-        self::$fasti->dispatch($id);
+        self::$fasti->dispatch($scheduledJob->id);
 
         // assert
 
         Bus::assertDispatchedSync(FakeJob::class);
+        Event::fake(JobDispatched::class);
     }
 
     public function test_can_dispatch_queued_job(): void
@@ -241,20 +343,22 @@ public function test_can_dispatch_queued_job(): void
         // arrange
 
         Bus::fake(FakeQueuedJob::class);
+        Event::fake(JobDispatched::class);
 
         $job = new FakeQueuedJob;
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // act
 
-        self::$fasti->dispatch($id);
+        self::$fasti->dispatch($scheduledJob->id);
 
         // assert
 
         Bus::assertDispatched(FakeQueuedJob::class);
+        Event::assertDispatched(JobDispatched::class);
     }
 
     public function test_can_dispatch_encrypted_job(): void
@@ -262,19 +366,21 @@ public function test_can_dispatch_encrypted_job(): void
         // arrange
 
         Bus::fake(FakeEncryptedJob::class);
+        Event::fake(JobDispatched::class);
 
         $job = new FakeEncryptedJob;
 
         $dateTime = new DateTime('now');
 
-        $id = self::$fasti->schedule($job, $dateTime);
+        $scheduledJob = self::$fasti->schedule($job, $dateTime);
 
         // act
 
-        self::$fasti->dispatch($id);
+        self::$fasti->dispatch($scheduledJob->id);
 
         // assert
 
         Bus::assertDispatchedSync(FakeEncryptedJob::class);
+        Event::assertDispatched(JobDispatched::class);
     }
 }