diff --git a/app/Template/StaticPageLayout/Turbo.php b/app/Template/StaticPageLayout/Turbo.php
index a4064549823615ae0787b5e0545688cca0b8396f..63cce2d776f34acc1cd70a60f6012b7e82ad5fd0 100644
--- a/app/Template/StaticPageLayout/Turbo.php
+++ b/app/Template/StaticPageLayout/Turbo.php
@@ -80,7 +80,7 @@ abstract readonly class Turbo extends StaticPageLayout
         </head>
         <body>
             <main class="body-content website">
-                <nav class="primary-navigation" id="primary-navigation">
+                <nav class="primary-navigation">
         HTML;
         yield from $this->renderPrimaryNavigation($staticPage);
         yield <<<'HTML'
@@ -105,7 +105,7 @@ abstract readonly class Turbo extends StaticPageLayout
         yield from $this->renderPrimaryBanner($staticPage);
         yield from $this->renderBodyContent($staticPage);
         yield <<<HTML
-                <footer class="primary-footer" id="primary-footer">
+                <footer class="primary-footer">
                     <div class="primary-footer__copyright">
                         Copyright &copy; {$currentYear} Distantmagic.
                         Built with Resonance.
@@ -170,7 +170,7 @@ abstract readonly class Turbo extends StaticPageLayout
     protected function renderPrimaryBanner(StaticPage $currentPage): Generator
     {
         yield <<<'HTML'
-            <div class="primary-banner" id="primary-banner">
+            <div class="primary-banner">
                 <iframe
                     frameborder="0"
                     height="30"
diff --git a/bin/resonance.php b/bin/resonance.php
index fffcb039a51004f62e0f9b7a15a86b80a703b4fe..246a43f996e76c8032327a0fc4ac4e6c5c576a92 100644
--- a/bin/resonance.php
+++ b/bin/resonance.php
@@ -2,18 +2,8 @@
 
 declare(strict_types=1);
 
-require_once __DIR__.'/../constants.php';
-require_once __DIR__.'/../vendor/autoload.php';
-
 use Distantmagic\Resonance\ConsoleApplication;
-use Distantmagic\Resonance\DependencyInjectionContainer;
-use Swoole\Runtime;
-
-Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
 
-$container = new DependencyInjectionContainer();
-$container->phpProjectFiles->indexDirectory(DM_RESONANCE_ROOT);
-$container->phpProjectFiles->indexDirectory(DM_APP_ROOT);
-$container->registerSingletons();
+$container = require_once __DIR__.'/../container.php';
 
 exit($container->make(ConsoleApplication::class)->run());
diff --git a/container.php b/container.php
new file mode 100644
index 0000000000000000000000000000000000000000..c1f745b655bccd9b73d26c1714bbe4f28ee5f77d
--- /dev/null
+++ b/container.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+require_once __DIR__.'/vendor/autoload.php';
+
+defined('DM_ROOT') or exit('Configuration is not loaded.');
+
+use Distantmagic\Resonance\DependencyInjectionContainer;
+use Swoole\Runtime;
+
+Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
+
+$container = new DependencyInjectionContainer();
+$container->phpProjectFiles->indexDirectory(DM_RESONANCE_ROOT);
+$container->phpProjectFiles->indexDirectory(DM_APP_ROOT);
+$container->registerSingletons();
+
+return $container;
diff --git a/docs/pages/docs/features/templating/twig/rendering-templates.md b/docs/pages/docs/features/templating/twig/rendering-templates.md
index f00642b830a9aec25f40998c576cab78cb349d19..9b007000f161588b10f0b618d06a9d8528629ddb 100644
--- a/docs/pages/docs/features/templating/twig/rendering-templates.md
+++ b/docs/pages/docs/features/templating/twig/rendering-templates.md
@@ -166,3 +166,12 @@ Generates URL that points to the requested route:
     {{ 'pages.homepage'|trans(request) }}
 </a>
 ```
+
+If you use just a string, it will try to use your `App\HttpRouteSymbol` file 
+to match the route:
+
+```twig
+<a href="{{ route('Homepage') }}">
+    {{ 'pages.homepage'|trans(request) }}
+</a>
+```
diff --git a/docs/pages/tutorials/session-based-authentication/index.md b/docs/pages/tutorials/session-based-authentication/index.md
index 9dec331ff17e63e7625bf148875256b018e758d4..0725b59f13e87c58b6e95e10114d320f1093141a 100644
--- a/docs/pages/tutorials/session-based-authentication/index.md
+++ b/docs/pages/tutorials/session-based-authentication/index.md
@@ -260,7 +260,7 @@ final readonly class LoginForm extends HttpResponder
 
 ```twig file:app/views/auth/login_form.twig
 <form
-    action="{{ route(constant('App\\HttpRouteSymbol::LoginValidation')) }}"
+    action="{{ route('LoginValidation') }}"
     method="post"
 >
     <input
@@ -543,7 +543,7 @@ final readonly class LogoutForm extends HttpResponder
 ```
 ```twig file:app/views/auth/logout_form.twig
 <form
-    action="{{ route(constant('App\\HttpRouteSymbol::LogoutValidation')) }}"
+    action="{{ route('LogoutValidation') }}"
     method="post"
 >
     <input
diff --git a/phpunit.xml b/phpunit.xml
index fcf0695d0ea6fdbc5257fe5aa506293098ba28ca..18666a2256d8c0564157a64ec90cf78c4347eb27 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <phpunit
-    bootstrap="vendor/autoload.php"
+    bootstrap="phpunit_bootstrap.php"
     colors="true"
     displayDetailsOnTestsThatTriggerWarnings="true"
 >
diff --git a/phpunit_bootstrap.php b/phpunit_bootstrap.php
new file mode 100644
index 0000000000000000000000000000000000000000..d09e7bf3b5938d3023c19066e61e989f72054cae
--- /dev/null
+++ b/phpunit_bootstrap.php
@@ -0,0 +1,6 @@
+<?php
+
+declare(strict_types=1);
+
+require_once __DIR__.'/vendor/autoload.php';
+require_once __DIR__.'/constants.php';
diff --git a/src/Command/PostfixBounce.php b/src/Command/PostfixBounce.php
new file mode 100644
index 0000000000000000000000000000000000000000..6db07429a0c4650fa7fd306d1390e1b1c9113998
--- /dev/null
+++ b/src/Command/PostfixBounce.php
@@ -0,0 +1,57 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Distantmagic\Resonance\Command;
+
+use Distantmagic\Resonance\Attribute\ConsoleCommand;
+use Distantmagic\Resonance\Attribute\WantsFeature;
+use Distantmagic\Resonance\Command;
+use Distantmagic\Resonance\CoroutineCommand;
+use Distantmagic\Resonance\Event\MailBounced;
+use Distantmagic\Resonance\EventDispatcherInterface;
+use Distantmagic\Resonance\Feature;
+use Distantmagic\Resonance\PostfixBounceAnalyzer;
+use Distantmagic\Resonance\SwooleConfiguration;
+use Psr\Log\LoggerInterface;
+use RuntimeException;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+#[ConsoleCommand(
+    name: 'postfix:bounce',
+    description: 'Handles email bounces (requires mailparse)'
+)]
+#[WantsFeature(Feature::Postfix)]
+final class PostfixBounce extends CoroutineCommand
+{
+    public function __construct(
+        private readonly EventDispatcherInterface $eventDispatcher,
+        private readonly LoggerInterface $logger,
+        private readonly PostfixBounceAnalyzer $postfixBounceAnalyzer,
+        SwooleConfiguration $swooleConfiguration,
+    ) {
+        parent::__construct($swooleConfiguration);
+    }
+
+    protected function executeInCoroutine(InputInterface $input, OutputInterface $output): int
+    {
+        if (!extension_loaded('mailparse') || !extension_loaded('http')) {
+            throw new RuntimeException('You need to install "http" and "mailparse" extensions');
+        }
+
+        $content = stream_get_contents(STDIN);
+
+        if (false === $content || empty($content)) {
+            throw new RuntimeException('Expected email contents in STDIN');
+        }
+
+        $postfixBounceReport = $this->postfixBounceAnalyzer->extractReport($content);
+
+        if ($postfixBounceReport) {
+            $this->eventDispatcher->dispatch(new MailBounced($postfixBounceReport));
+        }
+
+        return Command::SUCCESS;
+    }
+}
diff --git a/src/DatabaseConnection.php b/src/DatabaseConnection.php
index 281deba8bf88efe80802bdc10cb2f548c3d07330..51dbceeb7dd05b669f1272fb1873bd368b834f02 100644
--- a/src/DatabaseConnection.php
+++ b/src/DatabaseConnection.php
@@ -116,11 +116,7 @@ readonly class DatabaseConnection implements ServerInfoAwareConnection
         $pdoPreparedStatement = $this->pdo->prepare($sql);
         $pdoPreparedStatement = $this->assertNotFalse($pdoPreparedStatement);
 
-        return new DatabasePreparedStatement(
-            $this->databaseConnectionPoolRepository->eventDispatcher,
-            $pdoPreparedStatement,
-            $sql,
-        );
+        return new DatabasePreparedStatement($pdoPreparedStatement);
     }
 
     public function query(string $sql): DatabaseExecutedStatement
diff --git a/src/DatabaseConnectionPoolRepository.php b/src/DatabaseConnectionPoolRepository.php
index c4ee1a367a96e148908a73468b457ee8152b311e..b4e9bacbae6919fd17a0ee3d5e38793117a0f51f 100644
--- a/src/DatabaseConnectionPoolRepository.php
+++ b/src/DatabaseConnectionPoolRepository.php
@@ -18,7 +18,7 @@ readonly class DatabaseConnectionPoolRepository
      */
     public Map $databaseConnectionPool;
 
-    public function __construct(public EventDispatcherInterface $eventDispatcher)
+    public function __construct()
     {
         $this->databaseConnectionPool = new Map();
     }
diff --git a/src/DatabasePreparedStatement.php b/src/DatabasePreparedStatement.php
index 2eee6965ddfcbd6db9786e9b2a54bef115e9fedb..67f1a66292b132cddd23c57d7d91eebbe7240ca5 100644
--- a/src/DatabasePreparedStatement.php
+++ b/src/DatabasePreparedStatement.php
@@ -4,7 +4,6 @@ declare(strict_types=1);
 
 namespace Distantmagic\Resonance;
 
-use Distantmagic\Resonance\Event\SQLQueryBeforeExecute;
 use Doctrine\DBAL\Driver\PDO\ParameterTypeMap;
 use Doctrine\DBAL\Driver\Statement;
 use Doctrine\DBAL\ParameterType;
@@ -15,9 +14,7 @@ use Swoole\Database\PDOStatementProxy;
 readonly class DatabasePreparedStatement implements Statement
 {
     public function __construct(
-        private EventDispatcherInterface $eventDispatcher,
         private PDOStatement|PDOStatementProxy $pdoStatement,
-        private string $sql,
     ) {}
 
     /**
@@ -52,8 +49,6 @@ readonly class DatabasePreparedStatement implements Statement
 
     public function execute($params = null): DatabaseExecutedStatement
     {
-        $this->eventDispatcher->dispatch(new SQLQueryBeforeExecute($this->sql));
-
         /**
          * @var bool
          */
diff --git a/src/DoctrineConnectionRepository.php b/src/DoctrineConnectionRepository.php
index ee44b12b2963de81f6610f1969bdb0a105844ad8..6f4af63c179402f9aba9cf5d9687fabe9453768e 100644
--- a/src/DoctrineConnectionRepository.php
+++ b/src/DoctrineConnectionRepository.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace Distantmagic\Resonance;
 
+use Distantmagic\Resonance\Attribute\ListensTo;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Event\HttpResponseReady;
 use Doctrine\DBAL\Configuration;
@@ -22,7 +23,8 @@ use WeakMap;
  *
  * @template-extends EventListener<HttpResponseReady,void>
  */
-#[Singleton]
+#[ListensTo(HttpResponseReady::class)]
+#[Singleton(collection: SingletonCollection::EventListener)]
 readonly class DoctrineConnectionRepository extends EventListener
 {
     /**
@@ -36,30 +38,12 @@ readonly class DoctrineConnectionRepository extends EventListener
         private DoctrineMySQLDriver $doctrineMySQLDriver,
         private DoctrinePostgreSQLDriver $doctrinePostgreSQLDriver,
         private DoctrineSQLiteDriver $doctrineSQLiteDriver,
-        private EventListenerAggregate $eventListenerAggregate,
         private LoggerInterface $logger,
     ) {
         /**
          * @var WeakMap<Request,Map<string,Connection>>
          */
         $this->connections = new WeakMap();
-
-        /**
-         * False positive, $this IS an EventListenerInterface
-         *
-         * @psalm-suppress InvalidArgument
-         */
-        $this->eventListenerAggregate->addListener(HttpResponseReady::class, $this);
-    }
-
-    public function __destruct()
-    {
-        /**
-         * False positive, $this IS an EventListenerInterface
-         *
-         * @psalm-suppress InvalidArgument
-         */
-        $this->eventListenerAggregate->removeListener(HttpResponseReady::class, $this);
     }
 
     /**
diff --git a/src/Event/MailBounced.php b/src/Event/MailBounced.php
index 03d5689cdb3d6d97fba7e8759b06dcfe4c12e1e7..067af4dc271d19d09589a0319e78644fad9a5bb0 100644
--- a/src/Event/MailBounced.php
+++ b/src/Event/MailBounced.php
@@ -5,24 +5,12 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\Event;
 
 use Distantmagic\Resonance\Event;
+use Distantmagic\Resonance\MailBounceReportInterface;
 
 /**
  * @psalm-suppress PossiblyUnusedProperty used in listeners
  */
 final readonly class MailBounced extends Event
 {
-    /**
-     * @param non-empty-string      $recipient
-     * @param null|non-empty-string $diagnosticCode
-     * @param null|non-empty-string $notification
-     * @param null|non-empty-string $sender
-     * @param null|non-empty-string $status
-     */
-    public function __construct(
-        public string $recipient,
-        public ?string $diagnosticCode,
-        public ?string $notification,
-        public ?string $sender,
-        public ?string $status,
-    ) {}
+    public function __construct(public MailBounceReportInterface $report) {}
 }
diff --git a/src/Event/SQLQueryBeforeExecute.php b/src/Event/SQLQueryBeforeExecute.php
deleted file mode 100644
index 96ce1dbb04cefda32cb62ecdda4b3b15277ea388..0000000000000000000000000000000000000000
--- a/src/Event/SQLQueryBeforeExecute.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Distantmagic\Resonance\Event;
-
-use Distantmagic\Resonance\Event;
-
-final readonly class SQLQueryBeforeExecute extends Event
-{
-    public function __construct(public string $sql) {}
-}
diff --git a/src/EventListener/SQLQueryLogger.php b/src/EventListener/SQLQueryLogger.php
deleted file mode 100644
index 0a3e00334cfb0014ae56ccb5384d965f0fa1aa8f..0000000000000000000000000000000000000000
--- a/src/EventListener/SQLQueryLogger.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Distantmagic\Resonance\EventListener;
-
-use Distantmagic\Resonance\Attribute\ListensTo;
-use Distantmagic\Resonance\Attribute\Singleton;
-use Distantmagic\Resonance\DatabaseConfiguration;
-use Distantmagic\Resonance\Event\SQLQueryBeforeExecute;
-use Distantmagic\Resonance\EventListener;
-use Distantmagic\Resonance\SingletonCollection;
-use Doctrine\SqlFormatter\SqlFormatter;
-use Psr\Log\LoggerInterface;
-
-/**
- * @template-extends EventListener<SQLQueryBeforeExecute,void>
- */
-#[ListensTo(SQLQueryBeforeExecute::class)]
-#[Singleton(collection: SingletonCollection::EventListener)]
-final readonly class SQLQueryLogger extends EventListener
-{
-    private SqlFormatter $sqlFormatter;
-
-    public function __construct(
-        private DatabaseConfiguration $databaseConfiguration,
-        private LoggerInterface $logger,
-    ) {
-        $this->sqlFormatter = new SqlFormatter();
-    }
-
-    /**
-     * @param SQLQueryBeforeExecute $event
-     */
-    public function handle(object $event): void
-    {
-        $this->logger->debug($this->sqlFormatter->format($event->sql));
-    }
-
-    public function shouldRegister(): bool
-    {
-        foreach ($this->databaseConfiguration->connectionPoolConfiguration as $connectionPoolConfiguration) {
-            if ($connectionPoolConfiguration->logQueries) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-}
diff --git a/src/MailBounceReportInterface.php b/src/MailBounceReportInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..99360fa80aee315e48ac506e5a9b9d81bf1a0edc
--- /dev/null
+++ b/src/MailBounceReportInterface.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Distantmagic\Resonance;
+
+use Symfony\Component\Mime\Address;
+
+interface MailBounceReportInterface
+{
+    /**
+     * @return null|non-empty-string
+     */
+    public function getDiagnosticCode(): ?string;
+
+    /**
+     * @return null|non-empty-string
+     */
+    public function getNotification(): ?string;
+
+    public function getRecipient(): Address;
+
+    public function getSender(): ?Address;
+
+    /**
+     * @return null|non-empty-string
+     */
+    public function getStatus(): ?string;
+}
diff --git a/src/Command/MailBounce.php b/src/PostfixBounceAnalyzer.php
similarity index 68%
rename from src/Command/MailBounce.php
rename to src/PostfixBounceAnalyzer.php
index 0ca83c438586277b12f7c5783c6a12e9e5e4de33..c5d649fedbddba0dab8dc3d90e401b57ad91548a 100644
--- a/src/Command/MailBounce.php
+++ b/src/PostfixBounceAnalyzer.php
@@ -2,23 +2,13 @@
 
 declare(strict_types=1);
 
-namespace Distantmagic\Resonance\Command;
-
-use Distantmagic\Resonance\Attribute\ConsoleCommand;
-use Distantmagic\Resonance\Attribute\WantsFeature;
-use Distantmagic\Resonance\Command;
-use Distantmagic\Resonance\CoroutineCommand;
-use Distantmagic\Resonance\Event\MailBounced;
-use Distantmagic\Resonance\EventDispatcherInterface;
-use Distantmagic\Resonance\Feature;
-use Distantmagic\Resonance\MailerRepository;
-use Distantmagic\Resonance\SwooleConfiguration;
+namespace Distantmagic\Resonance;
+
+use Distantmagic\Resonance\Attribute\Singleton;
 use Generator;
 use http\Header;
-use Psr\Log\LoggerInterface;
 use RuntimeException;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Mime\Address;
 
 /**
  * @psalm-type PPartData = array{
@@ -27,34 +17,11 @@ use Symfony\Component\Console\Output\OutputInterface;
  *     starting-pos-body: int,
  * }
  */
-#[ConsoleCommand(
-    name: 'mail:bounce',
-    description: 'Handles email bounces (requires mailparse)'
-)]
-#[WantsFeature(Feature::Postfix)]
-final class MailBounce extends CoroutineCommand
+#[Singleton]
+readonly class PostfixBounceAnalyzer
 {
-    public function __construct(
-        private readonly EventDispatcherInterface $eventDispatcher,
-        private readonly LoggerInterface $logger,
-        private readonly MailerRepository $mailerRepository,
-        SwooleConfiguration $swooleConfiguration,
-    ) {
-        parent::__construct($swooleConfiguration);
-    }
-
-    protected function executeInCoroutine(InputInterface $input, OutputInterface $output): int
+    public function extractReport(string $deliveryReport): ?PostfixBounceReport
     {
-        if (!extension_loaded('mailparse') || !extension_loaded('http')) {
-            throw new RuntimeException('You need to install "http" and "mailparse" extensions');
-        }
-
-        $content = stream_get_contents(STDIN);
-
-        if (false === $content || empty($content)) {
-            throw new RuntimeException('Expected email contents in STDIN');
-        }
-
         /**
          * @var array{
          *     diagnostic-code?: non-empty-string,
@@ -65,7 +32,7 @@ final class MailBounce extends CoroutineCommand
          */
         $ret = [];
 
-        foreach ($this->parseMessageContent($content) as $partData => $partBody) {
+        foreach ($this->parseDeliveryReport($deliveryReport) as $partData => $partBody) {
             if (!isset($partData['content-description'])) {
                 continue;
             }
@@ -96,16 +63,54 @@ final class MailBounce extends CoroutineCommand
          * That alone is enough to notify about the bounce.
          */
         if (isset($ret['original-recipient']) && !empty($ret['original-recipient'])) {
-            $this->eventDispatcher->dispatch(new MailBounced(
-                recipient: $ret['original-recipient'],
+            return new PostfixBounceReport(
+                recipient: new Address($ret['original-recipient']),
                 diagnosticCode: empty($ret['diagnostic-code']) ? null : $ret['diagnostic-code'],
                 notification: empty($ret['notification']) ? null : $ret['notification'],
-                sender: empty($ret['x-postfix-sender']) ? null : $ret['x-postfix-sender'],
+                sender: empty($ret['x-postfix-sender']) ? null : new Address($ret['x-postfix-sender']),
                 status: empty($ret['status']) ? null : $ret['status'],
-            ));
+            );
         }
 
-        return Command::SUCCESS;
+        return null;
+    }
+
+    /**
+     * @return Generator<PPartData,string>
+     */
+    private function parseDeliveryReport(string $content): Generator
+    {
+        $message = mailparse_msg_create();
+
+        try {
+            if (!mailparse_msg_parse($message, $content)) {
+                throw new RuntimeException('Unable to parse the email message');
+            }
+
+            $structure = mailparse_msg_get_structure($message);
+
+            /**
+             * @var string $partId
+             */
+            foreach ($structure as $partId) {
+                $part = mailparse_msg_get_part($message, $partId);
+
+                /**
+                 * @var PPartData $partData
+                 */
+                $partData = mailparse_msg_get_part_data($part);
+
+                yield $partData => trim(mb_substr(
+                    $content,
+                    $partData['starting-pos-body'],
+                    $partData['ending-pos-body'] - $partData['starting-pos-body']
+                ));
+            }
+        } finally {
+            if (!mailparse_msg_free($message)) {
+                throw new RuntimeException('Unable to free the message resource');
+            }
+        }
     }
 
     /**
@@ -150,42 +155,4 @@ final class MailBounce extends CoroutineCommand
             }
         }
     }
-
-    /**
-     * @return Generator<PPartData,string>
-     */
-    private function parseMessageContent(string $content): Generator
-    {
-        $message = mailparse_msg_create();
-
-        try {
-            if (!mailparse_msg_parse($message, $content)) {
-                throw new RuntimeException('Unable to parse the email message');
-            }
-
-            $structure = mailparse_msg_get_structure($message);
-
-            /**
-             * @var string $partId
-             */
-            foreach ($structure as $partId) {
-                $part = mailparse_msg_get_part($message, $partId);
-
-                /**
-                 * @var PPartData $partData
-                 */
-                $partData = mailparse_msg_get_part_data($part);
-
-                yield $partData => trim(mb_substr(
-                    $content,
-                    $partData['starting-pos-body'],
-                    $partData['ending-pos-body'] - $partData['starting-pos-body']
-                ));
-            }
-        } finally {
-            if (!mailparse_msg_free($message)) {
-                throw new RuntimeException('Unable to free the message resource');
-            }
-        }
-    }
 }
diff --git a/src/PostfixBounceReport.php b/src/PostfixBounceReport.php
new file mode 100644
index 0000000000000000000000000000000000000000..1ffc078c8c3a7cce8ee40b50dc6f0f16f651eaeb
--- /dev/null
+++ b/src/PostfixBounceReport.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Distantmagic\Resonance;
+
+use Symfony\Component\Mime\Address;
+
+/**
+ * @psalm-suppress PossiblyUnusedProperty used in listeners
+ */
+final readonly class PostfixBounceReport extends Event implements MailBounceReportInterface
+{
+    /**
+     * @param null|non-empty-string $diagnosticCode
+     * @param null|non-empty-string $notification
+     * @param null|non-empty-string $status
+     */
+    public function __construct(
+        private Address $recipient,
+        private ?string $diagnosticCode,
+        private ?string $notification,
+        private ?Address $sender,
+        private ?string $status,
+    ) {}
+
+    /**
+     * @return null|non-empty-string
+     */
+    public function getDiagnosticCode(): ?string
+    {
+        return $this->diagnosticCode;
+    }
+
+    /**
+     * @return null|non-empty-string
+     */
+    public function getNotification(): ?string
+    {
+        return $this->notification;
+    }
+
+    public function getRecipient(): Address
+    {
+        return $this->recipient;
+    }
+
+    public function getSender(): ?Address
+    {
+        return $this->sender;
+    }
+
+    /**
+     * @return null|non-empty-string
+     */
+    public function getStatus(): ?string
+    {
+        return $this->status;
+    }
+}
diff --git a/src/PostfixBounceTest.php b/src/PostfixBounceTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..40d32dca5973d5c46a319e1fe4841f20c90e6e36
--- /dev/null
+++ b/src/PostfixBounceTest.php
@@ -0,0 +1,96 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Distantmagic\Resonance;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @coversNothing
+ *
+ * @internal
+ */
+final class PostfixBounceTest extends TestCase
+{
+    use TestsDependencyInectionContainerTrait;
+
+    public const DELIVERY_REPORT = <<<'REPORT'
+    From double-bounce@myhost  Sat Feb  3 08:52:39 2024
+    Return-Path: <double-bounce@myhost>
+    Received: by myhost (Postfix)
+        id 01862807342C; Sat,  3 Feb 2024 08:52:39 +0100 (CET)
+    Date: Sat,  3 Feb 2024 08:52:39 +0100 (CET)
+    From: Mail Delivery System <MAILER-DAEMON@myhost>
+    Subject: Postmaster Copy: Undelivered Mail
+    To: bounces@localhost
+    Auto-Submitted: auto-generated
+    MIME-Version: 1.0
+    Content-Type: multipart/report; report-type=delivery-status;
+        boundary="EC0F4807342B.1706946759/myhost"
+    Content-Transfer-Encoding: 8bit
+    Message-Id: <20240203075239.01862807342C@myhost>
+
+    This is a MIME-encapsulated message.
+
+    --EC0F4807342B.1706946759/myhost
+    Content-Description: Notification
+    Content-Type: text/plain; charset=utf-8
+    Content-Transfer-Encoding: 8bit
+
+
+    <foo@example.com>: Domain example.com does not accept mail (nullMX)
+
+    --EC0F4807342B.1706946759/myhost
+    Content-Description: Delivery report
+    Content-Type: message/delivery-status
+
+    Reporting-MTA: dns; myhost
+    X-Postfix-Queue-ID: EC0F4807342B
+    X-Postfix-Sender: rfc822; test@example.com
+    Arrival-Date: Sat,  3 Feb 2024 08:52:38 +0100 (CET)
+
+    Final-Recipient: rfc822; foo@example.com
+    Original-Recipient: rfc822;foo@example.com
+    Action: failed
+    Status: 5.1.0
+    Diagnostic-Code: X-Postfix; Domain example.com does not accept mail (nullMX)
+
+    --EC0F4807342B.1706946759/myhost
+    Content-Description: Undelivered Message Headers
+    Content-Type: text/rfc822-headers
+    Content-Transfer-Encoding: 8bit
+
+    Return-Path: <test@example.com>
+    Received: by myhost (Postfix, from userid 1026)
+        id EC0F4807342B; Sat,  3 Feb 2024 08:52:38 +0100 (CET)
+    From: test@example.com
+    To: foo@example.com
+    Subject: hello
+    MIME-Version: 1.0
+    Date: Sat, 03 Feb 2024 07:52:38 +0000
+    Message-ID: <10b4a76ac9b4d29dfa3ca9a4cea89de8@example.com>
+    DKIM-Signature: v=1; q=dns/txt; a=rsa-sha256;
+     bh=def; d=mydomain;
+     h=From: To: Subject: MIME-Version: Date: Message-ID; i=@mydomain;
+     s=selector; t=1706946758; c=relaxed/relaxed;
+     b=abc
+    Content-Type: multipart/alternative; boundary=sy7-AJCQ
+
+    --EC0F4807342B.1706946759/myhost--
+    REPORT;
+
+    public function test_delivery_report_is_analyzed(): void
+    {
+        $analyzer = self::$container->make(PostfixBounceAnalyzer::class);
+
+        $report = $analyzer->extractReport(self::DELIVERY_REPORT);
+
+        self::assertNotNull($report);
+        self::assertEquals('5.1.0', $report->getStatus());
+        self::assertEquals('<foo@example.com>: Domain example.com does not accept mail (nullMX)', $report->getNotification());
+        self::assertEquals('foo@example.com', $report->getRecipient()->getAddress());
+        self::assertEquals('test@example.com', $report->getSender()?->getAddress());
+        self::assertEquals('X-Postfix; Domain example.com does not accept mail (nullMX)', $report->getDiagnosticCode());
+    }
+}
diff --git a/src/SessionManager.php b/src/SessionManager.php
index 2b6b7be294e87a60ac32c7bb4c1c711840ccca55..f88d457e7b4f2d3a5234eb3839f74e6166182126 100644
--- a/src/SessionManager.php
+++ b/src/SessionManager.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace Distantmagic\Resonance;
 
+use Distantmagic\Resonance\Attribute\ListensTo;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Event\HttpResponseReady;
 use Swoole\Http\Request;
@@ -13,7 +14,8 @@ use WeakMap;
 /**
  * @template-extends EventListener<HttpResponseReady,void>
  */
-#[Singleton]
+#[ListensTo(HttpResponseReady::class)]
+#[Singleton(collection: SingletonCollection::EventListener)]
 final readonly class SessionManager extends EventListener
 {
     /**
@@ -22,34 +24,16 @@ final readonly class SessionManager extends EventListener
     private WeakMap $sessions;
 
     public function __construct(
-        private EventListenerAggregate $eventListenerAggregate,
         private RedisConfiguration $redisConfiguration,
         private RedisConnectionPoolRepository $redisConnectionPoolRepository,
         private SessionConfiguration $sessionConfiguration,
     ) {
-        /**
-         * False positive, $this IS an EventListenerInterface
-         *
-         * @psalm-suppress InvalidArgument
-         */
-        $this->eventListenerAggregate->addListener(HttpResponseReady::class, $this);
-
         /**
          * @var WeakMap<Request, Session>
          */
         $this->sessions = new WeakMap();
     }
 
-    public function __destruct()
-    {
-        /**
-         * False positive, $this IS an EventListenerInterface
-         *
-         * @psalm-suppress InvalidArgument
-         */
-        $this->eventListenerAggregate->removeListener(HttpResponseReady::class, $this);
-    }
-
     /**
      * @param HttpResponseReady $event
      */
diff --git a/src/SingletonProvider/DatabaseConnectionPoolRepositoryProvider.php b/src/SingletonProvider/DatabaseConnectionPoolRepositoryProvider.php
index 9c19340b4780c330db784555db5f6943156ebf15..e1525f07bce3992d63ef178ed4e6949e84c4dbfa 100644
--- a/src/SingletonProvider/DatabaseConnectionPoolRepositoryProvider.php
+++ b/src/SingletonProvider/DatabaseConnectionPoolRepositoryProvider.php
@@ -7,7 +7,6 @@ namespace Distantmagic\Resonance\SingletonProvider;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\DatabaseConfiguration;
 use Distantmagic\Resonance\DatabaseConnectionPoolRepository;
-use Distantmagic\Resonance\EventDispatcherInterface;
 use Distantmagic\Resonance\PHPProjectFiles;
 use Distantmagic\Resonance\SingletonContainer;
 use Distantmagic\Resonance\SingletonProvider;
@@ -22,12 +21,11 @@ final readonly class DatabaseConnectionPoolRepositoryProvider extends SingletonP
 {
     public function __construct(
         private DatabaseConfiguration $databaseConfiguration,
-        private EventDispatcherInterface $eventDispatcher,
     ) {}
 
     public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): DatabaseConnectionPoolRepository
     {
-        $databaseConnectionPoolRepository = new DatabaseConnectionPoolRepository($this->eventDispatcher);
+        $databaseConnectionPoolRepository = new DatabaseConnectionPoolRepository();
 
         foreach ($this->databaseConfiguration->connectionPoolConfiguration as $name => $connectionPoolConfiguration) {
             $pdoConfig = new PDOConfig();
diff --git a/src/SingletonProvider/RedisConnectionPoolRepositoryProvider.php b/src/SingletonProvider/RedisConnectionPoolRepositoryProvider.php
index 741f99996e787c0fb37a8da26b66b2fa3c08d0bc..f52ab7c6f1561c5ecaf3cf968cb1f5f790d57c53 100644
--- a/src/SingletonProvider/RedisConnectionPoolRepositoryProvider.php
+++ b/src/SingletonProvider/RedisConnectionPoolRepositoryProvider.php
@@ -5,7 +5,6 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\SingletonProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
-use Distantmagic\Resonance\EventDispatcherInterface;
 use Distantmagic\Resonance\PHPProjectFiles;
 use Distantmagic\Resonance\RedisConfiguration;
 use Distantmagic\Resonance\RedisConnectionPoolRepository;
@@ -22,7 +21,6 @@ final readonly class RedisConnectionPoolRepositoryProvider extends SingletonProv
 {
     public function __construct(
         private RedisConfiguration $databaseConfiguration,
-        private EventDispatcherInterface $eventDispatcher,
     ) {}
 
     public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): RedisConnectionPoolRepository
diff --git a/src/TwigFunctionRoute.php b/src/TwigFunctionRoute.php
index 87448cea5e598b083df5c0ee55c71309970722a7..8a617869c583b13d8eaf34bb95a0d9fb6ee14c2d 100644
--- a/src/TwigFunctionRoute.php
+++ b/src/TwigFunctionRoute.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance;
 
 use Distantmagic\Resonance\Attribute\Singleton;
+use RuntimeException;
 
 #[Singleton]
 readonly class TwigFunctionRoute
@@ -15,9 +16,25 @@ readonly class TwigFunctionRoute
      * @param array<string,string> $params
      */
     public function __invoke(
-        HttpRouteSymbolInterface $routeSymbol,
+        HttpRouteSymbolInterface|string $routeSymbol,
         array $params = [],
     ): string {
-        return $this->internalLinkBuilder->build($routeSymbol, $params);
+        if (!is_string($routeSymbol)) {
+            return $this->internalLinkBuilder->build($routeSymbol, $params);
+        }
+
+        $resolvedSymbol = constant(sprintf('App\\HttpRouteSymbol::%s', $routeSymbol));
+
+        if (!($resolvedSymbol instanceof HttpRouteSymbolInterface)) {
+            throw new RuntimeException(sprintf(
+                'Expected "%s"',
+                HttpRouteSymbolInterface::class,
+            ));
+        }
+
+        return $this->internalLinkBuilder->build(
+            $resolvedSymbol,
+            $params,
+        );
     }
 }