diff --git a/flight/Engine.php b/flight/Engine.php
index b2900c4..f75f42a 100644
--- a/flight/Engine.php
+++ b/flight/Engine.php
@@ -8,6 +8,7 @@ use Closure;
 use ErrorException;
 use Exception;
 use flight\core\Dispatcher;
+use flight\core\EventDispatcher;
 use flight\core\Loader;
 use flight\net\Request;
 use flight\net\Response;
@@ -51,6 +52,10 @@ use flight\net\Route;
  * @method void render(string $file, ?array<string,mixed> $data = null, ?string $key = null) Renders template
  * @method View view() Gets current view
  *
+ * # Events
+ * @method void onEvent(string $event, callable $callback) Registers a callback for an event.
+ * @method void triggerEvent(string $event, ...$args) Triggers an event.
+ *
  * # Request-Response
  * @method Request request() Gets current request
  * @method Response response() Gets current response
@@ -79,7 +84,8 @@ class Engine
     private const MAPPABLE_METHODS = [
         'start', 'stop', 'route', 'halt', 'error', 'notFound',
         'render', 'redirect', 'etag', 'lastModified', 'json', 'jsonHalt', 'jsonp',
-        'post', 'put', 'patch', 'delete', 'group', 'getUrl', 'download', 'resource'
+        'post', 'put', 'patch', 'delete', 'group', 'getUrl', 'download', 'resource',
+        'onEvent', 'triggerEvent'
     ];
 
     /** @var array<string, mixed> Stored variables. */
@@ -88,9 +94,12 @@ class Engine
     /** Class loader. */
     protected Loader $loader;
 
-    /** Event dispatcher. */
+    /** Method and class dispatcher. */
     protected Dispatcher $dispatcher;
 
+    /** Event dispatcher. */
+    protected EventDispatcher $eventDispatcher;
+
     /** If the framework has been initialized or not. */
     protected bool $initialized = false;
 
@@ -101,6 +110,7 @@ class Engine
     {
         $this->loader = new Loader();
         $this->dispatcher = new Dispatcher();
+        $this->eventDispatcher = new EventDispatcher();
         $this->init();
     }
 
@@ -493,6 +503,8 @@ class Engine
             $this->router()->reset();
         }
         $request = $this->request();
+        $this->triggerEvent('flight.request.received', $request);
+
         $response = $this->response();
         $router = $this->router();
 
@@ -515,6 +527,7 @@ class Engine
         // Route the request
         $failedMiddlewareCheck = false;
         while ($route = $router->route($request)) {
+            $this->triggerEvent('flight.route.matched', $route);
             $params = array_values($route->params);
 
             // Add route info to the parameter list
@@ -548,6 +561,7 @@ class Engine
                     $failedMiddlewareCheck = true;
                     break;
                 }
+                $this->triggerEvent('flight.route.middleware.before', $route);
             }
 
             $useV3OutputBuffering =
@@ -563,6 +577,7 @@ class Engine
                 $route->callback,
                 $params
             );
+            $this->triggerEvent('flight.route.executed', $route);
 
             if ($useV3OutputBuffering === true) {
                 $response->write(ob_get_clean());
@@ -577,6 +592,7 @@ class Engine
                     $failedMiddlewareCheck = true;
                     break;
                 }
+                $this->triggerEvent('flight.route.middleware.after', $route);
             }
 
             $dispatched = true;
@@ -662,6 +678,8 @@ class Engine
             }
 
             $response->send();
+
+            $this->triggerEvent('flight.response.sent', $response);
         }
     }
 
@@ -992,4 +1010,26 @@ class Engine
     {
         return $this->router()->getUrlByAlias($alias, $params);
     }
+
+    /**
+     * Adds an event listener.
+     *
+     * @param string $eventName The name of the event to listen to
+     * @param callable $callback The callback to execute when the event is triggered
+     */
+    public function _onEvent(string $eventName, callable $callback): void
+    {
+        $this->eventDispatcher->on($eventName, $callback);
+    }
+
+    /**
+     * Triggers an event.
+     *
+     * @param string $eventName The name of the event to trigger
+     * @param mixed ...$args The arguments to pass to the event listeners
+     */
+    public function _triggerEvent(string $eventName, ...$args): void
+    {
+        $this->eventDispatcher->trigger($eventName, ...$args);
+    }
 }
diff --git a/flight/Flight.php b/flight/Flight.php
index 59a9945..fb35427 100644
--- a/flight/Flight.php
+++ b/flight/Flight.php
@@ -46,14 +46,15 @@ require_once __DIR__ . '/autoload.php';
  * Adds standardized RESTful routes for a controller.
  * @method static Router router() Returns Router instance.
  * @method static string getUrl(string $alias, array<string, mixed> $params = []) Gets a url from an alias
- *
  * @method static void map(string $name, callable $callback) Creates a custom framework method.
  *
+ * # Filters
  * @method static void before(string $name, Closure(array<int, mixed> &$params, string &$output): (void|false) $callback)
  * Adds a filter before a framework method.
  * @method static void after(string $name, Closure(array<int, mixed> &$params, string &$output): (void|false) $callback)
  * Adds a filter after a framework method.
  *
+ * # Variables
  * @method static void set(string|iterable<string, mixed> $key, mixed $value) Sets a variable.
  * @method static mixed get(?string $key) Gets a variable.
  * @method static bool has(string $key) Checks if a variable is set.
@@ -64,6 +65,10 @@ require_once __DIR__ . '/autoload.php';
  * Renders a template file.
  * @method static View view() Returns View instance.
  *
+ * # Events
+ * @method void onEvent(string $event, callable $callback) Registers a callback for an event.
+ * @method void triggerEvent(string $event, ...$args) Triggers an event.
+ *
  * # Request-Response
  * @method static Request request() Returns Request instance.
  * @method static Response response() Returns Response instance.
diff --git a/flight/core/EventDispatcher.php b/flight/core/EventDispatcher.php
new file mode 100644
index 0000000..b80eb42
--- /dev/null
+++ b/flight/core/EventDispatcher.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+namespace flight\core;
+
+class EventDispatcher
+{
+    /** @var array<string, array<int, callable>> */
+    protected array $listeners = [];
+
+    /**
+     * Register a callback for an event.
+     *
+     * @param string $event Event name
+     * @param callable $callback Callback function
+     */
+    public function on(string $event, callable $callback): void
+    {
+        if (isset($this->listeners[$event]) === false) {
+            $this->listeners[$event] = [];
+        }
+        $this->listeners[$event][] = $callback;
+    }
+
+    /**
+     * Trigger an event with optional arguments.
+     *
+     * @param string $event Event name
+     * @param mixed ...$args Arguments to pass to the callbacks
+     */
+    public function trigger(string $event, ...$args): void
+    {
+        if (isset($this->listeners[$event]) === true) {
+            foreach ($this->listeners[$event] as $callback) {
+                $result = call_user_func_array($callback, $args);
+
+                // If you return false, it will break the loop and stop the other event listeners.
+                if ($result === false) {
+                    break; // Stop executing further listeners
+                }
+            }
+        }
+    }
+}
diff --git a/tests/EventSystemTest.php b/tests/EventSystemTest.php
new file mode 100644
index 0000000..6dba2c7
--- /dev/null
+++ b/tests/EventSystemTest.php
@@ -0,0 +1,227 @@
+<?php
+
+declare(strict_types=1);
+
+namespace flight\tests;
+
+use Flight;
+use PHPUnit\Framework\TestCase;
+use flight\Engine;
+use TypeError;
+
+class EventSystemTest extends TestCase
+{
+    protected function setUp(): void
+    {
+        // Reset the Flight engine before each test to ensure a clean state
+        Flight::setEngine(new Engine());
+        Flight::app()->init();
+    }
+
+    /**
+     * Test registering and triggering a single listener.
+     */
+    public function testRegisterAndTriggerSingleListener()
+    {
+        $called = false;
+        Flight::onEvent('test.event', function () use (&$called) {
+            $called = true;
+        });
+        Flight::triggerEvent('test.event');
+        $this->assertTrue($called, 'Single listener should be called when event is triggered.');
+    }
+
+    /**
+     * Test registering multiple listeners for the same event.
+     */
+    public function testRegisterMultipleListeners()
+    {
+        $counter = 0;
+        Flight::onEvent('test.event', function () use (&$counter) {
+            $counter++;
+        });
+        Flight::onEvent('test.event', function () use (&$counter) {
+            $counter++;
+        });
+        Flight::triggerEvent('test.event');
+        $this->assertEquals(2, $counter, 'All registered listeners should be called.');
+    }
+
+    /**
+     * Test triggering an event with no listeners registered.
+     */
+    public function testTriggerWithNoListeners()
+    {
+        // Should not throw any errors
+        Flight::triggerEvent('non.existent.event');
+        $this->assertTrue(true, 'Triggering an event with no listeners should not throw an error.');
+    }
+
+    /**
+     * Test that a listener receives a single argument correctly.
+     */
+    public function testListenerReceivesSingleArgument()
+    {
+        $received = null;
+        Flight::onEvent('test.event', function ($arg) use (&$received) {
+            $received = $arg;
+        });
+        Flight::triggerEvent('test.event', 'hello');
+        $this->assertEquals('hello', $received, 'Listener should receive the passed argument.');
+    }
+
+    /**
+     * Test that a listener receives multiple arguments correctly.
+     */
+    public function testListenerReceivesMultipleArguments()
+    {
+        $received = [];
+        Flight::onEvent('test.event', function ($arg1, $arg2) use (&$received) {
+            $received = [$arg1, $arg2];
+        });
+        Flight::triggerEvent('test.event', 'first', 'second');
+        $this->assertEquals(['first', 'second'], $received, 'Listener should receive all passed arguments.');
+    }
+
+    /**
+     * Test that listeners are called in the order they were registered.
+     */
+    public function testListenersCalledInOrder()
+    {
+        $order = [];
+        Flight::onEvent('test.event', function () use (&$order) {
+            $order[] = 1;
+        });
+        Flight::onEvent('test.event', function () use (&$order) {
+            $order[] = 2;
+        });
+        Flight::triggerEvent('test.event');
+        $this->assertEquals([1, 2], $order, 'Listeners should be called in registration order.');
+    }
+
+    /**
+     * Test that listeners are not called for unrelated events.
+     */
+    public function testListenerNotCalledForOtherEvents()
+    {
+        $called = false;
+        Flight::onEvent('test.event1', function () use (&$called) {
+            $called = true;
+        });
+        Flight::triggerEvent('test.event2');
+        $this->assertFalse($called, 'Listeners should not be called for different events.');
+    }
+
+    /**
+     * Test overriding the onEvent method.
+     */
+    public function testOverrideOnEvent()
+    {
+        $called = false;
+        Flight::map('onEvent', function ($event, $callback) use (&$called) {
+            $called = true;
+        });
+        Flight::onEvent('test.event', function () {
+        });
+        $this->assertTrue($called, 'Overridden onEvent method should be called.');
+    }
+
+    /**
+     * Test overriding the triggerEvent method.
+     */
+    public function testOverrideTriggerEvent()
+    {
+        $called = false;
+        Flight::map('triggerEvent', function ($event, ...$args) use (&$called) {
+            $called = true;
+        });
+        Flight::triggerEvent('test.event');
+        $this->assertTrue($called, 'Overridden triggerEvent method should be called.');
+    }
+
+    /**
+     * Test that an overridden onEvent can still register listeners by calling the original method.
+     */
+    public function testOverrideOnEventStillRegistersListener()
+    {
+        $overrideCalled = false;
+        Flight::map('onEvent', function ($event, $callback) use (&$overrideCalled) {
+            $overrideCalled = true;
+            // Call the original method
+            Flight::app()->_onEvent($event, $callback);
+        });
+
+        $listenerCalled = false;
+        Flight::onEvent('test.event', function () use (&$listenerCalled) {
+            $listenerCalled = true;
+        });
+
+        Flight::triggerEvent('test.event');
+
+        $this->assertTrue($overrideCalled, 'Overridden onEvent should be called.');
+        $this->assertTrue($listenerCalled, 'Listener should still be triggered after override.');
+    }
+
+    /**
+     * Test that an overridden triggerEvent can still trigger listeners by calling the original method.
+     */
+    public function testOverrideTriggerEventStillTriggersListeners()
+    {
+        $overrideCalled = false;
+        Flight::map('triggerEvent', function ($event, ...$args) use (&$overrideCalled) {
+            $overrideCalled = true;
+            // Call the original method
+            Flight::app()->_triggerEvent($event, ...$args);
+        });
+
+        $listenerCalled = false;
+        Flight::onEvent('test.event', function () use (&$listenerCalled) {
+            $listenerCalled = true;
+        });
+
+        Flight::triggerEvent('test.event');
+
+        $this->assertTrue($overrideCalled, 'Overridden triggerEvent should be called.');
+        $this->assertTrue($listenerCalled, 'Listeners should still be triggered after override.');
+    }
+
+    /**
+     * Test that an invalid callable throws an exception (if applicable).
+     */
+    public function testInvalidCallableThrowsException()
+    {
+        $this->expectException(TypeError::class);
+        // Assuming the event system validates callables
+        Flight::onEvent('test.event', 'not_a_callable');
+    }
+
+    /**
+     * Test that event propagation stops if a listener returns false.
+     */
+    public function testStopPropagation()
+    {
+        $firstCalled = false;
+        $secondCalled = false;
+        $thirdCalled = false;
+
+        Flight::onEvent('test.event', function () use (&$firstCalled) {
+            $firstCalled = true;
+            return true; // Continue propagation
+        });
+
+        Flight::onEvent('test.event', function () use (&$secondCalled) {
+            $secondCalled = true;
+            return false; // Stop propagation
+        });
+
+        Flight::onEvent('test.event', function () use (&$thirdCalled) {
+            $thirdCalled = true;
+        });
+
+        Flight::triggerEvent('test.event');
+
+        $this->assertTrue($firstCalled, 'First listener should be called');
+        $this->assertTrue($secondCalled, 'Second listener should be called');
+        $this->assertFalse($thirdCalled, 'Third listener should not be called after propagation stopped');
+    }
+}