diff --git a/README.md b/README.md
index 502e84c6..0b0e41b4 100755
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ phpList is an open source newsletter manager. This project is a rewrite of the
 
 ## About this package
 
-This is the core module of the successor to phpList 3. It will have the 
+This is the core module of the successor to phpList 3. It will have the
 following responsibilities:
 
 * provide access to the DB via Doctrine models and repositories (and raw SQL
@@ -41,7 +41,7 @@ Since this package is only a service required to run a full installation of **ph
 
 ## Contributing to this package
 
-Contributions to phpList repositories are highly welcomed! To get started please take a look at the [contribution guide](.github/CONTRIBUTING.md). It contains everything you would need to make your first contribution including how to run local style checks and run tests. 
+Contributions to phpList repositories are highly welcomed! To get started please take a look at the [contribution guide](.github/CONTRIBUTING.md). It contains everything you would need to make your first contribution including how to run local style checks and run tests.
 
 ### Code of Conduct
 
@@ -53,9 +53,11 @@ this code.
 ## Structure
 
 * [Class Docs][docs/phpdoc/]
+* [Mailer Transports](docs/mailer-transports.md) - How to use different email providers (Gmail, Amazon SES, Mailchimp, SendGrid)
 * [Class structure overview](docs/ClassStructure.md)
 * [Graphic domain model](docs/DomainModel/DomainModel.svg) and
   a [description of the domain entities](docs/DomainModel/Entities.md)
+* [Mailer Transports](docs/mailer-transports.md) - How to use different email providers (Gmail, Amazon SES, Mailchimp, SendGrid)
 
 
 ## Running the web server
@@ -79,9 +81,9 @@ already in use, on the next free port after 8000).
 
 You can stop the server with CTRL + C.
 
-#### Development and Documentation 
+#### Development and Documentation
 
-We use `phpDocumentor` to automatically generate documentation for classes. To make this process efficient and easier, you are required to properly "document" your  `classes`,`properties`, `methods` ... by annotating them with [docblocks](https://docs.phpdoc.org/latest/guide/guides/docblocks.html). 
+We use `phpDocumentor` to automatically generate documentation for classes. To make this process efficient and easier, you are required to properly "document" your  `classes`,`properties`, `methods` ... by annotating them with [docblocks](https://docs.phpdoc.org/latest/guide/guides/docblocks.html).
 
 More about generatings docs in [PHPDOC.md](PHPDOC.md)
 
@@ -124,12 +126,12 @@ listed in the `extra` section of the module's `composer.json` like this:
 
 ```json
 "extra": {
-    "phplist/core": {
-        "bundles": [
-            "Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle",
-            "PhpList\\Core\\EmptyStartPageBundle\\PhpListEmptyStartPageBundle"
-        ]
-    }
+  "phplist/core": {
+    "bundles": [
+      "Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle",
+      "PhpList\\Core\\EmptyStartPageBundle\\PhpListEmptyStartPageBundle"
+    ]
+  }
 }
 ```
 
@@ -144,14 +146,14 @@ the `extra` section of the module's `composer.json` like this:
 
 ```json
 "extra": {
-    "phplist/core": {
-        "routes": {
-            "homepage": {
-                "resource": "@PhpListEmptyStartPageBundle/Controller/",
-                "type": "annotation"
-            }
-        }
+  "phplist/core": {
+    "routes": {
+      "homepage": {
+        "resource": "@PhpListEmptyStartPageBundle/Controller/",
+        "type": "annotation"
+      }
     }
+  }
 }
 ```
 
@@ -159,17 +161,17 @@ You can also provide system configuration for your module:
 
 ```json
 "extra": {
-    "phplist/core": {
-        "configuration": {
-            "framework": {
-                "templating": {
-                    "engines": [
-                        "twig"
-                    ]
-                }
-            }
+  "phplist/core": {
+    "configuration": {
+      "framework": {
+        "templating": {
+          "engines": [
+            "twig"
+          ]
         }
+      }
     }
+  }
 }
 ```
 
@@ -204,6 +206,17 @@ phpList module), please use the
 [REST API](https://github.com/phpList/rest-api).
 
 
+## Email Configuration
+
+phpList supports multiple email transport providers through Symfony Mailer. The following transports are included:
+
+* Gmail
+* Amazon SES
+* Mailchimp Transactional (Mandrill)
+* SendGrid
+
+For detailed configuration instructions, see the [Mailer Transports documentation](docs/mailer-transports.md).
+
 ## Copyright
 
 phpList is copyright (C) 2000-2021 [phpList Ltd](https://www.phplist.com/).
diff --git a/composer.json b/composer.json
index 89a2fcde..2f898e3b 100644
--- a/composer.json
+++ b/composer.json
@@ -59,7 +59,14 @@
         "masterminds/html5": "^2.9",
         "ext-dom": "*",
         "league/csv": "^9.23.0",
-        "doctrine/doctrine-migrations-bundle": "^3.4"
+        "doctrine/doctrine-migrations-bundle": "^3.4",
+        "symfony/mailer": "^6.4",
+        "symfony/google-mailer": "^6.4",
+        "symfony/amazon-mailer": "^6.4",
+        "symfony/mailchimp-mailer": "^6.4",
+        "symfony/sendgrid-mailer": "^6.4",
+        "symfony/twig-bundle": "^6.4",
+        "symfony/messenger": "^6.4"
     },
     "require-dev": {
         "phpunit/phpunit": "^9.5",
@@ -127,9 +134,11 @@
             "bundles": [
                 "Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle",
                 "Symfony\\Bundle\\MonologBundle\\MonologBundle",
+                "Symfony\\Bundle\\TwigBundle\\TwigBundle",
                 "Doctrine\\Bundle\\DoctrineBundle\\DoctrineBundle",
                 "Doctrine\\Bundle\\MigrationsBundle\\DoctrineMigrationsBundle",
-                "PhpList\\Core\\EmptyStartPageBundle\\EmptyStartPageBundle"
+                "PhpList\\Core\\EmptyStartPageBundle\\EmptyStartPageBundle",
+                "FOS\\RestBundle\\FOSRestBundle"
             ],
             "routes": {
                 "homepage": {
diff --git a/config/PHPMD/rules.xml b/config/PHPMD/rules.xml
index 8c88111a..2d88410b 100644
--- a/config/PHPMD/rules.xml
+++ b/config/PHPMD/rules.xml
@@ -51,7 +51,7 @@
     <!-- rules from the "naming" rule set -->
     <rule ref="rulesets/naming.xml/ShortVariable">
         <properties>
-            <property name="exceptions" value="id,ip"/>
+            <property name="exceptions" value="id,ip,cc"/>
         </properties>
     </rule>
     <rule ref="rulesets/naming.xml/LongVariable">
diff --git a/config/config.yml b/config/config.yml
index fd4705bb..93df3f5c 100644
--- a/config/config.yml
+++ b/config/config.yml
@@ -40,3 +40,5 @@ framework:
     serializer:
         enabled: true
         enable_attributes: true
+    mailer:
+        dsn: '%app.mailer_dsn%'
diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml
new file mode 100644
index 00000000..3ae2402d
--- /dev/null
+++ b/config/packages/messenger.yaml
@@ -0,0 +1,26 @@
+# This file is the Symfony Messenger configuration for asynchronous processing
+framework:
+    messenger:
+        # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
+        # failure_transport: failed
+
+        transports:
+            # https://symfony.com/doc/current/messenger.html#transport-configuration
+            async_email:
+                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
+                options:
+                    auto_setup: true
+                    use_notify: true
+                    check_delayed_interval: 60000
+                retry_strategy:
+                    max_retries: 3
+                    # milliseconds delay
+                    delay: 1000
+                    multiplier: 2
+                    max_delay: 0
+
+            # failed: 'doctrine://default?queue_name=failed'
+
+        routing:
+            # Route your messages to the transports
+            'PhpList\Core\Domain\Messaging\Message\AsyncEmailMessage': async_email
diff --git a/config/parameters.yml.dist b/config/parameters.yml.dist
index 00ce9a48..988628dc 100644
--- a/config/parameters.yml.dist
+++ b/config/parameters.yml.dist
@@ -22,6 +22,12 @@ parameters:
     database_password: '%%env(PHPLIST_DATABASE_PASSWORD)%%'
     env(PHPLIST_DATABASE_PASSWORD): 'phplist'
 
+    # mailer configs
+    app.mailer_from: '%%env(MAILER_FROM)%%'
+    env(MAILER_FROM): 'noreply@phplist.com'
+    app.mailer_dsn: '%%env(MAILER_DSN)%%'
+    env(MAILER_DSN): 'smtp://username:password@smtp.mailtrap.io:2525'
+
     # A secret key that's used to generate certain security-related tokens
     secret: '%%env(PHPLIST_SECRET)%%'
     env(PHPLIST_SECRET): %1$s
diff --git a/config/services.yml b/config/services.yml
index b83adce3..e3329d6c 100644
--- a/config/services.yml
+++ b/config/services.yml
@@ -37,6 +37,11 @@ services:
         public: true
         tags: [controller.service_arguments]
 
+    # Register message handlers for Symfony Messenger
+    PhpList\Core\Domain\Messaging\MessageHandler\:
+        resource: '../src/Domain/Messaging/MessageHandler'
+        tags: ['messenger.message_handler']
+
     doctrine.orm.metadata.annotation_reader:
         alias: doctrine.annotation_reader
 
diff --git a/config/services/commands.yml b/config/services/commands.yml
new file mode 100644
index 00000000..61dad7a5
--- /dev/null
+++ b/config/services/commands.yml
@@ -0,0 +1,10 @@
+services:
+  _defaults:
+    autowire: true
+    autoconfigure: true
+    public: false
+
+  PhpList\Core\Domain\Messaging\Command\:
+    resource: '../../src/Domain/Messaging/Command'
+    tags: ['console.command']
+
diff --git a/config/services/managers.yml b/config/services/managers.yml
index cdb1eb63..41284f2c 100644
--- a/config/services/managers.yml
+++ b/config/services/managers.yml
@@ -61,3 +61,13 @@ services:
     autowire: true
     autoconfigure: true
     public: true
+
+  PhpList\Core\Domain\Messaging\Service\EmailService:
+    autowire: true
+    autoconfigure: true
+    arguments:
+      $defaultFromEmail: '%app.mailer_from%'
+
+  PhpList\Core\Domain\Configuration\Service\Manager\ConfigManager:
+    autowire: true
+    autoconfigure: true
diff --git a/config/packages/app.yml b/config/services/parameters.yml
similarity index 87%
rename from config/packages/app.yml
rename to config/services/parameters.yml
index 61cf7460..ebf1d99b 100644
--- a/config/packages/app.yml
+++ b/config/services/parameters.yml
@@ -1,5 +1,5 @@
-app:
-  config:
+parameters:
+  app.config:
     message_from_address: 'news@example.com'
     admin_address: 'admin@example.com'
     default_message_age: 15768000
@@ -8,3 +8,4 @@ app:
     notify_start_default: 'start@example.com'
     notify_end_default: 'end@example.com'
     always_add_google_tracking: true
+    click_track: true
diff --git a/config/services/repositories.yml b/config/services/repositories.yml
index 21ce7114..5e4f4473 100644
--- a/config/services/repositories.yml
+++ b/config/services/repositories.yml
@@ -60,3 +60,28 @@ services:
         parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
         arguments:
             - PhpList\Core\Domain\Messaging\Model\TemplateImage
+
+    PhpList\Core\Domain\Configuration\Repository\ConfigRepository:
+        parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
+        arguments:
+            - PhpList\Core\Domain\Configuration\Model\Config
+
+    PhpList\Core\Domain\Messaging\Repository\UserMessageBounceRepository:
+        parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
+        arguments:
+            - PhpList\Core\Domain\Messaging\Model\UserMessageBounce
+
+    PhpList\Core\Domain\Messaging\Repository\UserMessageForwardRepository:
+        parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
+        arguments:
+            - PhpList\Core\Domain\Messaging\Model\UserMessageForward
+
+    PhpList\Core\Domain\Analytics\Repository\LinkTrackRepository:
+        parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
+        arguments:
+            - PhpList\Core\Domain\Analytics\Model\LinkTrack
+
+    PhpList\Core\Domain\Analytics\Repository\UserMessageViewRepository:
+        parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
+        arguments:
+            - PhpList\Core\Domain\Analytics\Model\UserMessageView
diff --git a/config/services/validators.yml b/config/services/validators.yml
index 3d15e4a5..e7a90910 100644
--- a/config/services/validators.yml
+++ b/config/services/validators.yml
@@ -6,3 +6,7 @@ services:
   PhpList\Core\Domain\Messaging\Validator\TemplateImageValidator:
     autowire: true
     autoconfigure: true
+
+  PhpList\Core\Domain\Subscription\Validator\AttributeTypeValidator:
+    autowire: true
+    autoconfigure: true
diff --git a/docs/AsyncEmailSending.md b/docs/AsyncEmailSending.md
new file mode 100644
index 00000000..da4f247c
--- /dev/null
+++ b/docs/AsyncEmailSending.md
@@ -0,0 +1,102 @@
+# Asynchronous Email Sending in phpList
+
+This document explains how to use the asynchronous email sending functionality in phpList.
+
+## Overview
+
+phpList now supports sending emails asynchronously using Symfony Messenger. This means that when you send an email, it is queued for delivery rather than being sent immediately. This has several benefits:
+
+1. **Improved Performance**: Your application doesn't have to wait for the email to be sent before continuing execution
+2. **Better Reliability**: If the email server is temporarily unavailable, the message remains in the queue and will be retried automatically
+3. **Scalability**: You can process the email queue separately from your main application, allowing for better resource management
+
+## Configuration
+
+The asynchronous email functionality is configured in `config/packages/messenger.yaml` and uses the `MESSENGER_TRANSPORT_DSN` environment variable defined in `config/parameters.yml`.
+
+By default, the system uses Doctrine (database) as the transport for queued messages:
+
+```yaml
+env(MESSENGER_TRANSPORT_DSN): 'doctrine://default?auto_setup=true'
+```
+
+You can change this to use other transports supported by Symfony Messenger, such as:
+
+- **AMQP (RabbitMQ)**: `amqp://guest:guest@localhost:5672/%2f/messages`
+- **Redis**: `redis://localhost:6379/messages`
+- **In-Memory (for testing)**: `in-memory://`
+
+## Using Asynchronous Email Sending
+
+### Basic Usage
+
+The `EmailService` class now sends emails asynchronously by default:
+
+```php
+// This will queue the email for sending
+$emailService->sendEmail($email);
+```
+
+### Synchronous Sending
+
+If you need to send an email immediately (synchronously), you can use the `sendEmailSync` method:
+
+```php
+// This will send the email immediately
+$emailService->sendEmailSync($email);
+```
+
+### Bulk Emails
+
+For sending to multiple recipients:
+
+```php
+// Asynchronous (queued)
+$emailService->sendBulkEmail($recipients, $subject, $text, $html);
+
+// Synchronous (immediate)
+$emailService->sendBulkEmailSync($recipients, $subject, $text, $html);
+```
+
+## Testing Email Sending
+
+You can test the email functionality using the built-in command:
+
+```bash
+# Queue an email for asynchronous sending
+bin/console app:send-test-email recipient@example.com
+
+# Send an email synchronously (immediately)
+bin/console app:send-test-email recipient@example.com --sync
+```
+
+## Processing the Email Queue
+
+To process queued emails, you need to run the Symfony Messenger worker:
+
+```bash
+bin/console messenger:consume async_email
+```
+
+For production environments, it's recommended to run this command as a background service or using a process manager like Supervisor.
+
+## Monitoring
+
+You can monitor the queue status using the following commands:
+
+```bash
+# View the number of messages in the queue
+bin/console messenger:stats
+
+# View failed messages
+bin/console messenger:failed:show
+```
+
+## Troubleshooting
+
+If emails are not being sent:
+
+1. Make sure the messenger worker is running
+2. Check for failed messages using `bin/console messenger:failed:show`
+3. Verify your mailer configuration in `config/parameters.yml`
+4. Try sending an email synchronously to test the mailer configuration
diff --git a/docs/ClassStructure.md b/docs/ClassStructure.md
index ff4da328..e7cf6801 100644
--- a/docs/ClassStructure.md
+++ b/docs/ClassStructure.md
@@ -21,7 +21,7 @@ database access code in these classes.
 
 ### Repository/
 
-These classes are reponsible for reading domain models from the database,
+These classes are responsible for reading domain models from the database,
 for writing them there, and for other database queries.
 
 
diff --git a/docs/MailerTransports.md b/docs/MailerTransports.md
new file mode 100644
index 00000000..cde763da
--- /dev/null
+++ b/docs/MailerTransports.md
@@ -0,0 +1,105 @@
+# Using Different Mailer Transports in phpList
+
+This document explains how to use the various mailer transports that are included in the phpList core dependencies:
+
+- Google Mailer (Gmail)
+- Amazon SES
+- Mailchimp Transactional (Mandrill)
+- SendGrid
+
+## Configuration
+
+The phpList core uses Symfony Mailer for sending emails. The mailer transport is configured using the `MAILER_DSN` environment variable, which is defined in `config/parameters.yml`.
+
+## Available Transports
+
+### 1. Google Mailer (Gmail)
+
+To use Gmail as your email transport:
+
+```
+# Using Gmail with OAuth (recommended)
+MAILER_DSN=gmail://USERNAME:APP_PASSWORD@default
+
+# Using Gmail with SMTP
+MAILER_DSN=smtp://USERNAME:PASSWORD@smtp.gmail.com:587
+```
+
+Notes:
+- Replace `USERNAME` with your Gmail address
+- For OAuth setup, follow [Symfony Gmail documentation](https://symfony.com/doc/current/mailer.html#using-gmail-to-send-emails)
+- For the SMTP method, you may need to enable "Less secure app access" or use an App Password
+
+### 2. Amazon SES
+
+To use Amazon SES:
+
+```
+# Using API credentials
+MAILER_DSN=ses://ACCESS_KEY:SECRET_KEY@default?region=REGION
+
+# Using SMTP interface
+MAILER_DSN=smtp://USERNAME:PASSWORD@email-smtp.REGION.amazonaws.com:587
+```
+
+Notes:
+- Replace `ACCESS_KEY` and `SECRET_KEY` with your AWS credentials
+- Replace `REGION` with your AWS region (e.g., us-east-1)
+- For SMTP, use the credentials generated in the Amazon SES console
+
+### 3. Mailchimp Transactional (Mandrill)
+
+To use Mailchimp Transactional (formerly Mandrill):
+
+```
+MAILER_DSN=mandrill://API_KEY@default
+```
+
+Notes:
+- Replace `API_KEY` with your Mailchimp Transactional API key
+- You can find your API key in the Mailchimp Transactional (Mandrill) dashboard
+
+### 4. SendGrid
+
+To use SendGrid:
+
+```
+# Using API
+MAILER_DSN=sendgrid://API_KEY@default
+
+# Using SMTP
+MAILER_DSN=smtp://apikey:API_KEY@smtp.sendgrid.net:587
+```
+
+Notes:
+- Replace `API_KEY` with your SendGrid API key
+- For SMTP, the username is literally "apikey" and the password is your actual API key
+
+## Testing Your Configuration
+
+After setting up your preferred mailer transport, you can test it using the built-in test command:
+
+```bash
+bin/console app:send-test-email recipient@example.com
+```
+
+## Switching Between Transports
+
+You can easily switch between different mailer transports by changing the `MAILER_DSN` environment variable. This can be done in several ways:
+
+1. Edit the `config/parameters.yml` file directly
+2. Set the environment variable in your server configuration
+3. Set the environment variable before running a command:
+   ```bash
+   MAILER_DSN=sendgrid://API_KEY@default bin/console app:send-test-email recipient@example.com
+   ```
+
+## Additional Configuration
+
+Some transports may require additional configuration options. Refer to the Symfony documentation for more details:
+
+- [Symfony Mailer Documentation](https://symfony.com/doc/current/mailer.html)
+- [Gmail Transport](https://symfony.com/doc/current/mailer.html#using-gmail-to-send-emails)
+- [Amazon SES Transport](https://symfony.com/doc/current/mailer.html#using-amazon-ses)
+- [Mailchimp Transport](https://symfony.com/doc/current/mailer.html#using-mailchimp)
+- [SendGrid Transport](https://symfony.com/doc/current/mailer.html#using-sendgrid)
diff --git a/src/Core/ApplicationKernel.php b/src/Core/ApplicationKernel.php
index 1d3c64ab..97249b45 100644
--- a/src/Core/ApplicationKernel.php
+++ b/src/Core/ApplicationKernel.php
@@ -122,6 +122,11 @@ public function registerContainerConfiguration(LoaderInterface $loader): void
         $loader->load($this->getApplicationDir() . '/config/parameters.yml');
         $loader->load($this->getRootDir() . '/config/config_' . $this->getEnvironment() . '.yml');
         $loader->load($this->getApplicationDir() . '/config/config_modules.yml');
+
+        $twigConfigFile = $this->getApplicationDir() . '/config/packages/twig.yaml';
+        if (file_exists($twigConfigFile)) {
+            $loader->load($twigConfigFile);
+        }
     }
 
     /**
diff --git a/src/Core/Bootstrap.php b/src/Core/Bootstrap.php
index fe79a805..c56b6e4a 100644
--- a/src/Core/Bootstrap.php
+++ b/src/Core/Bootstrap.php
@@ -138,7 +138,7 @@ public function ensureDevelopmentOrTestingEnvironment(): self
     }
 
     /**
-     * Main entry point called at every request usually from global scope. Checks if everything is correct
+     * The main entry point called at every request usually from global scope. Checks if everything is correct
      * and loads the configuration.
      *
      * @return Bootstrap fluent interface
diff --git a/src/Domain/Analytics/Repository/UserMessageViewRepository.php b/src/Domain/Analytics/Repository/UserMessageViewRepository.php
index b5c8495b..5458ea01 100644
--- a/src/Domain/Analytics/Repository/UserMessageViewRepository.php
+++ b/src/Domain/Analytics/Repository/UserMessageViewRepository.php
@@ -16,7 +16,7 @@ public function countByMessageId(int $messageId): int
     {
         return (int) $this->createQueryBuilder('umv')
             ->select('COUNT(umv.id)')
-            ->where('umv.message_id = :messageId')
+            ->where('umv.messageId = :messageId')
             ->setParameter('messageId', $messageId)
             ->getQuery()
             ->getSingleScalarResult();
diff --git a/src/Domain/Configuration/Exception/ConfigNotEditableException.php b/src/Domain/Configuration/Exception/ConfigNotEditableException.php
new file mode 100644
index 00000000..eb0ed456
--- /dev/null
+++ b/src/Domain/Configuration/Exception/ConfigNotEditableException.php
@@ -0,0 +1,15 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Configuration\Exception;
+
+use Exception;
+
+class ConfigNotEditableException extends Exception
+{
+    public function __construct(string $configKey)
+    {
+        parent::__construct(sprintf('Configuration item "%s" is not editable.', $configKey));
+    }
+}
diff --git a/src/Domain/Configuration/Model/Config.php b/src/Domain/Configuration/Model/Config.php
index 57b44ba1..00f0a6c5 100644
--- a/src/Domain/Configuration/Model/Config.php
+++ b/src/Domain/Configuration/Model/Config.php
@@ -13,8 +13,8 @@
 class Config implements DomainModel
 {
     #[ORM\Id]
-    #[ORM\Column(type: 'string', length: 35)]
-    private string $item;
+    #[ORM\Column(name: 'item', type: 'string', length: 35)]
+    private string $key;
 
     #[ORM\Column(type: 'text', nullable: true)]
     private ?string $value = null;
@@ -25,14 +25,14 @@ class Config implements DomainModel
     #[ORM\Column(type: 'string', length: 25, nullable: true)]
     private ?string $type = null;
 
-    public function getItem(): string
+    public function getKey(): string
     {
-        return $this->item;
+        return $this->key;
     }
 
-    public function setItem(string $item): self
+    public function setKey(string $key): self
     {
-        $this->item = $item;
+        $this->key = $key;
         return $this;
     }
 
diff --git a/src/Domain/Configuration/Service/Manager/ConfigManager.php b/src/Domain/Configuration/Service/Manager/ConfigManager.php
new file mode 100644
index 00000000..cae380be
--- /dev/null
+++ b/src/Domain/Configuration/Service/Manager/ConfigManager.php
@@ -0,0 +1,67 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Configuration\Service\Manager;
+
+use PhpList\Core\Domain\Configuration\Model\Config;
+use PhpList\Core\Domain\Configuration\Repository\ConfigRepository;
+use PhpList\Core\Domain\Configuration\Exception\ConfigNotEditableException;
+
+class ConfigManager
+{
+    private ConfigRepository $configRepository;
+
+    public function __construct(ConfigRepository $configRepository)
+    {
+        $this->configRepository = $configRepository;
+    }
+
+    /**
+     * Get a configuration item by its key
+     */
+    public function getByItem(string $item): ?Config
+    {
+        return $this->configRepository->findOneBy(['item' => $item]);
+    }
+
+    /**
+     * Get all configuration items
+     *
+     * @return Config[]
+     */
+    public function getAll(): array
+    {
+        return $this->configRepository->findAll();
+    }
+
+    /**
+     * Update a configuration item
+     * @throws ConfigNotEditableException
+     */
+    public function update(Config $config, string $value): void
+    {
+        if (!$config->isEditable()) {
+            throw new ConfigNotEditableException($config->getKey());
+        }
+        $config->setValue($value);
+
+        $this->configRepository->save($config);
+    }
+
+    public function create(string $key, string $value, bool $editable, ?string $type = null): void
+    {
+        $config = (new Config())
+            ->setKey($key)
+            ->setValue($value)
+            ->setEditable($editable)
+            ->setType($type);
+
+        $this->configRepository->save($config);
+    }
+
+    public function delete(Config $config): void
+    {
+        $this->configRepository->remove($config);
+    }
+}
diff --git a/src/Domain/Identity/Model/Administrator.php b/src/Domain/Identity/Model/Administrator.php
index d45e6c0e..34cfc9e8 100644
--- a/src/Domain/Identity/Model/Administrator.php
+++ b/src/Domain/Identity/Model/Administrator.php
@@ -6,6 +6,7 @@
 
 use DateTime;
 use Doctrine\ORM\Mapping as ORM;
+use InvalidArgumentException;
 use PhpList\Core\Domain\Common\Model\Interfaces\CreationDate;
 use PhpList\Core\Domain\Common\Model\Interfaces\DomainModel;
 use PhpList\Core\Domain\Common\Model\Interfaces\Identity;
@@ -72,6 +73,7 @@ public function __construct()
         $this->createdAt = new DateTime();
         $this->updatedAt = null;
         $this->email = '';
+        $this->privileges = null;
     }
 
     public function getLoginName(): string
@@ -152,16 +154,37 @@ public function getNameLc(): string
         return $this->namelc;
     }
 
-    public function setPrivileges(string $privileges): self
+    public function setPrivileges(Privileges $privileges): self
     {
-        $this->privileges = $privileges;
+        $this->privileges = $privileges->toSerialized();
 
         return $this;
     }
 
-    public function getPrivileges(): string
+    /**
+     * @SuppressWarnings(PHPMD.StaticAccess)
+     * @throws InvalidArgumentException
+     */
+    public function setPrivilegesFromArray(array $privilegesData): void
     {
-        return $this->privileges;
+        $privileges = new Privileges();
+        foreach ($privilegesData as $key => $value) {
+            $flag = PrivilegeFlag::tryFrom($key);
+            if (!$flag) {
+                throw new InvalidArgumentException('Unknown privilege key: '. $key);
+            }
+
+            $privileges = $value ? $privileges->grant($flag) : $privileges->revoke($flag);
+        }
+        $this->setPrivileges($privileges);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.StaticAccess)
+     */
+    public function getPrivileges(): Privileges
+    {
+        return Privileges::fromSerialized($this->privileges);
     }
 
     public function getCreatedAt(): ?DateTime
diff --git a/src/Domain/Identity/Model/AdministratorToken.php b/src/Domain/Identity/Model/AdministratorToken.php
index 41ff75aa..1a6c511f 100644
--- a/src/Domain/Identity/Model/AdministratorToken.php
+++ b/src/Domain/Identity/Model/AdministratorToken.php
@@ -43,7 +43,7 @@ class AdministratorToken implements DomainModel, Identity, CreationDate
     private string $key = '';
 
     #[ORM\ManyToOne(targetEntity: Administrator::class)]
-    #[ORM\JoinColumn(name: 'adminid')]
+    #[ORM\JoinColumn(name: 'adminid', referencedColumnName: 'id', onDelete: 'CASCADE')]
     private ?Administrator $administrator = null;
 
     public function __construct()
diff --git a/src/Domain/Identity/Model/Dto/CreateAdministratorDto.php b/src/Domain/Identity/Model/Dto/CreateAdministratorDto.php
index adfe9ec6..6d058d3d 100644
--- a/src/Domain/Identity/Model/Dto/CreateAdministratorDto.php
+++ b/src/Domain/Identity/Model/Dto/CreateAdministratorDto.php
@@ -14,6 +14,7 @@ public function __construct(
         public readonly string $password,
         public readonly string $email,
         public readonly bool $isSuperUser = false,
+        public readonly array $privileges = [],
     ) {
     }
 }
diff --git a/src/Domain/Identity/Model/Dto/UpdateAdministratorDto.php b/src/Domain/Identity/Model/Dto/UpdateAdministratorDto.php
index a8ad06f3..399a1d48 100644
--- a/src/Domain/Identity/Model/Dto/UpdateAdministratorDto.php
+++ b/src/Domain/Identity/Model/Dto/UpdateAdministratorDto.php
@@ -12,6 +12,7 @@ public function __construct(
         public readonly ?string $password = null,
         public readonly ?string $email = null,
         public readonly ?bool $superAdmin = null,
+        public readonly array $privileges = [],
     ) {
     }
 }
diff --git a/src/Domain/Identity/Model/PrivilegeFlag.php b/src/Domain/Identity/Model/PrivilegeFlag.php
new file mode 100644
index 00000000..e8e9151e
--- /dev/null
+++ b/src/Domain/Identity/Model/PrivilegeFlag.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Identity\Model;
+
+enum PrivilegeFlag: string
+{
+    case Subscribers = 'subscribers';
+    case Campaigns   = 'campaigns';
+    case Statistics  = 'statistics';
+    case Settings    = 'settings';
+}
diff --git a/src/Domain/Identity/Model/Privileges.php b/src/Domain/Identity/Model/Privileges.php
new file mode 100644
index 00000000..0b53de69
--- /dev/null
+++ b/src/Domain/Identity/Model/Privileges.php
@@ -0,0 +1,79 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Identity\Model;
+
+use InvalidArgumentException;
+use UnexpectedValueException;
+
+class Privileges
+{
+    /**
+     * @var array<string, bool>
+     */
+    private array $flags = [];
+
+    /**
+     * @SuppressWarnings(PHPMD.StaticAccess)
+     */
+    public function __construct(?array $flags = [])
+    {
+        foreach (PrivilegeFlag::cases() as $flag) {
+            $key = $flag->value;
+            $this->flags[$key] = $flags[$key] ?? false;
+        }
+    }
+
+    public static function fromSerialized(?string $serialized): self
+    {
+        if (!$serialized) {
+            return new self();
+        }
+
+        set_error_handler(function () {
+            throw new InvalidArgumentException('Invalid serialized privileges string.');
+        });
+
+        try {
+            $data = unserialize($serialized);
+        } finally {
+            restore_error_handler();
+        }
+
+        if (!is_array($data)) {
+            throw new UnexpectedValueException('Privileges must deserialize to an array.');
+        }
+
+        return new self($data);
+    }
+
+    public function toSerialized(): string
+    {
+        return serialize($this->flags);
+    }
+
+    public function has(PrivilegeFlag $flag): bool
+    {
+        return $this->flags[$flag->value] ?? false;
+    }
+
+    public function grant(PrivilegeFlag $flag): self
+    {
+        $clone = clone $this;
+        $clone->flags[$flag->value] = true;
+        return $clone;
+    }
+
+    public function revoke(PrivilegeFlag $flag): self
+    {
+        $clone = clone $this;
+        $clone->flags[$flag->value] = false;
+        return $clone;
+    }
+
+    public function all(): array
+    {
+        return $this->flags;
+    }
+}
diff --git a/src/Domain/Identity/Service/AdminAttributeDefinitionManager.php b/src/Domain/Identity/Service/AdminAttributeDefinitionManager.php
index c1397e09..f0a18e07 100644
--- a/src/Domain/Identity/Service/AdminAttributeDefinitionManager.php
+++ b/src/Domain/Identity/Service/AdminAttributeDefinitionManager.php
@@ -8,14 +8,19 @@
 use PhpList\Core\Domain\Identity\Model\Dto\AdminAttributeDefinitionDto;
 use PhpList\Core\Domain\Identity\Repository\AdminAttributeDefinitionRepository;
 use PhpList\Core\Domain\Identity\Exception\AttributeDefinitionCreationException;
+use PhpList\Core\Domain\Subscription\Validator\AttributeTypeValidator;
 
 class AdminAttributeDefinitionManager
 {
     private AdminAttributeDefinitionRepository $definitionRepository;
+    private AttributeTypeValidator $attributeTypeValidator;
 
-    public function __construct(AdminAttributeDefinitionRepository $definitionRepository)
-    {
+    public function __construct(
+        AdminAttributeDefinitionRepository $definitionRepository,
+        AttributeTypeValidator $attributeTypeValidator
+    ) {
         $this->definitionRepository = $definitionRepository;
+        $this->attributeTypeValidator = $attributeTypeValidator;
     }
 
     public function create(AdminAttributeDefinitionDto $attributeDefinitionDto): AdminAttributeDefinition
@@ -24,6 +29,7 @@ public function create(AdminAttributeDefinitionDto $attributeDefinitionDto): Adm
         if ($existingAttribute) {
             throw new AttributeDefinitionCreationException('Attribute definition already exists', 409);
         }
+        $this->attributeTypeValidator->validate($attributeDefinitionDto->type);
 
         $attributeDefinition = (new AdminAttributeDefinition($attributeDefinitionDto->name))
             ->setType($attributeDefinitionDto->type)
@@ -45,6 +51,7 @@ public function update(
         if ($existingAttribute && $existingAttribute->getId() !== $attributeDefinition->getId()) {
             throw new AttributeDefinitionCreationException('Another attribute with this name already exists.', 409);
         }
+        $this->attributeTypeValidator->validate($attributeDefinitionDto->type);
 
         $attributeDefinition
             ->setName($attributeDefinitionDto->name)
diff --git a/src/Domain/Identity/Service/AdministratorManager.php b/src/Domain/Identity/Service/AdministratorManager.php
index 41731a14..82d3d36f 100644
--- a/src/Domain/Identity/Service/AdministratorManager.php
+++ b/src/Domain/Identity/Service/AdministratorManager.php
@@ -29,6 +29,7 @@ public function createAdministrator(CreateAdministratorDto $dto): Administrator
         $administrator->setSuperUser($dto->isSuperUser);
         $hashedPassword = $this->hashGenerator->createPasswordHash($dto->password);
         $administrator->setPasswordHash($hashedPassword);
+        $administrator->setPrivilegesFromArray($dto->privileges);
 
         $this->entityManager->persist($administrator);
         $this->entityManager->flush();
@@ -51,6 +52,7 @@ public function updateAdministrator(Administrator $administrator, UpdateAdminist
             $hashedPassword = $this->hashGenerator->createPasswordHash($dto->password);
             $administrator->setPasswordHash($hashedPassword);
         }
+        $administrator->setPrivilegesFromArray($dto->privileges);
 
         $this->entityManager->flush();
     }
diff --git a/src/Domain/Messaging/Command/SendTestEmailCommand.php b/src/Domain/Messaging/Command/SendTestEmailCommand.php
new file mode 100644
index 00000000..bb6ba06b
--- /dev/null
+++ b/src/Domain/Messaging/Command/SendTestEmailCommand.php
@@ -0,0 +1,93 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Messaging\Command;
+
+use Exception;
+use PhpList\Core\Domain\Messaging\Service\EmailService;
+use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Mime\Address;
+use Symfony\Component\Mime\Email;
+
+#[AsCommand(
+    name: 'phplist:test-email',
+    description: 'Send a test email to verify email configuration'
+)]
+class SendTestEmailCommand extends Command
+{
+    private EmailService $emailService;
+
+    public function __construct(EmailService $emailService)
+    {
+        parent::__construct();
+        $this->emailService = $emailService;
+    }
+
+    protected function configure(): void
+    {
+        $this
+            ->addArgument(
+                name: 'recipient',
+                mode: InputArgument::OPTIONAL,
+                description: 'Recipient email address'
+            )
+            ->addOption(
+                name: 'sync',
+                mode: InputArgument::OPTIONAL,
+                description: 'Send email synchronously instead of queuing it',
+                default: false
+            );
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $recipient = $input->getArgument('recipient');
+        if (!$recipient) {
+            $output->writeln('Recipient email address not provided');
+
+            return Command::FAILURE;
+        }
+
+        if (!filter_var($recipient, FILTER_VALIDATE_EMAIL)) {
+            $output->writeln('Invalid email address: ' . $recipient);
+
+            return Command::FAILURE;
+        }
+
+        try {
+            $syncMode = $input->getOption('sync');
+
+            if ($syncMode) {
+                $output->writeln('Sending test email synchronously to ' . $recipient);
+            } else {
+                $output->writeln('Queuing test email for ' . $recipient);
+            }
+
+            $email = (new Email())
+                ->from(new Address('admin@example.com', 'Admin Team'))
+                ->to($recipient)
+                ->subject('Test Email from phpList')
+                ->text('This is a test email sent from phpList Email Service.')
+                ->html('<h1>Test</h1><p>This is a <strong>test email</strong> sent from phpList Email Service</p>');
+
+            if ($syncMode) {
+                $this->emailService->sendEmailSync($email);
+                $output->writeln('Test email sent successfully!');
+            } else {
+                $this->emailService->sendEmail($email);
+                $output->writeln('Test email queued successfully! It will be sent asynchronously.');
+            }
+
+            return Command::SUCCESS;
+        } catch (Exception $e) {
+            $output->writeln('Failed to send test email: ' . $e->getMessage());
+
+            return Command::FAILURE;
+        }
+    }
+}
diff --git a/src/Domain/Messaging/Message/AsyncEmailMessage.php b/src/Domain/Messaging/Message/AsyncEmailMessage.php
new file mode 100644
index 00000000..0ea834ba
--- /dev/null
+++ b/src/Domain/Messaging/Message/AsyncEmailMessage.php
@@ -0,0 +1,58 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Messaging\Message;
+
+use Symfony\Component\Mime\Email;
+
+/**
+ * Message class for asynchronous email processing
+ */
+class AsyncEmailMessage
+{
+    private Email $email;
+    private array $cc;
+    private array $bcc;
+    private array $replyTo;
+    private array $attachments;
+
+    public function __construct(
+        Email $email,
+        array $cc = [],
+        array $bcc = [],
+        array $replyTo = [],
+        array $attachments = []
+    ) {
+        $this->email = $email;
+        $this->cc = $cc;
+        $this->bcc = $bcc;
+        $this->replyTo = $replyTo;
+        $this->attachments = $attachments;
+    }
+
+    public function getEmail(): Email
+    {
+        return $this->email;
+    }
+
+    public function getCc(): array
+    {
+        return $this->cc;
+    }
+
+    public function getBcc(): array
+    {
+        return $this->bcc;
+    }
+
+    public function getReplyTo(): array
+    {
+        return $this->replyTo;
+    }
+
+    public function getAttachments(): array
+    {
+        return $this->attachments;
+    }
+}
diff --git a/src/Domain/Messaging/MessageHandler/AsyncEmailMessageHandler.php b/src/Domain/Messaging/MessageHandler/AsyncEmailMessageHandler.php
new file mode 100644
index 00000000..42d91417
--- /dev/null
+++ b/src/Domain/Messaging/MessageHandler/AsyncEmailMessageHandler.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Messaging\MessageHandler;
+
+use PhpList\Core\Domain\Messaging\Message\AsyncEmailMessage;
+use PhpList\Core\Domain\Messaging\Service\EmailService;
+use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+
+/**
+ * Handler for processing asynchronous email messages
+ */
+#[AsMessageHandler]
+class AsyncEmailMessageHandler
+{
+    private EmailService $emailService;
+
+    public function __construct(EmailService $emailService)
+    {
+        $this->emailService = $emailService;
+    }
+
+    /**
+     * Process an asynchronous email message by sending the email
+     */
+    public function __invoke(AsyncEmailMessage $message): void
+    {
+        $this->emailService->sendEmailSync(
+            $message->getEmail(),
+            $message->getCc(),
+            $message->getBcc(),
+            $message->getReplyTo(),
+            $message->getAttachments()
+        );
+    }
+}
diff --git a/src/Domain/Messaging/Model/UserMessageBounce.php b/src/Domain/Messaging/Model/UserMessageBounce.php
index 3469fe1b..c842867f 100644
--- a/src/Domain/Messaging/Model/UserMessageBounce.php
+++ b/src/Domain/Messaging/Model/UserMessageBounce.php
@@ -28,7 +28,7 @@ class UserMessageBounce implements DomainModel, Identity
     private int $user;
 
     #[ORM\Column(name: 'message', type: 'integer')]
-    private int $message;
+    private int $messageId;
 
     #[ORM\Column(name: 'bounce', type: 'integer')]
     private int $bounce;
@@ -51,9 +51,9 @@ public function getUser(): int
         return $this->user;
     }
 
-    public function getMessage(): int
+    public function getMessageId(): int
     {
-        return $this->message;
+        return $this->messageId;
     }
 
     public function getBounce(): int
@@ -72,9 +72,9 @@ public function setUser(int $user): self
         return $this;
     }
 
-    public function setMessage(int $message): self
+    public function setMessageId(int $messageId): self
     {
-        $this->message = $message;
+        $this->messageId = $messageId;
         return $this;
     }
 
diff --git a/src/Domain/Messaging/Model/UserMessageForward.php b/src/Domain/Messaging/Model/UserMessageForward.php
index 6dbbcc15..a9432e45 100644
--- a/src/Domain/Messaging/Model/UserMessageForward.php
+++ b/src/Domain/Messaging/Model/UserMessageForward.php
@@ -26,7 +26,7 @@ class UserMessageForward implements DomainModel, Identity
     private int $user;
 
     #[ORM\Column(name: 'message', type: 'integer')]
-    private int $message;
+    private int $messageId;
 
     #[ORM\Column(name: 'forward', type: 'string', length: 255, nullable: true)]
     private ?string $forward = null;
@@ -52,9 +52,9 @@ public function getUser(): int
         return $this->user;
     }
 
-    public function getMessage(): int
+    public function getMessageId(): int
     {
-        return $this->message;
+        return $this->messageId;
     }
 
     public function getForward(): ?string
@@ -78,9 +78,9 @@ public function setUser(int $user): self
         return $this;
     }
 
-    public function setMessage(int $message): self
+    public function setMessageId(int $messageId): self
     {
-        $this->message = $message;
+        $this->messageId = $messageId;
         return $this;
     }
 
diff --git a/src/Domain/Messaging/Repository/UserMessageBounceRepository.php b/src/Domain/Messaging/Repository/UserMessageBounceRepository.php
index 5beb9b89..16f07f79 100644
--- a/src/Domain/Messaging/Repository/UserMessageBounceRepository.php
+++ b/src/Domain/Messaging/Repository/UserMessageBounceRepository.php
@@ -16,7 +16,7 @@ public function getCountByMessageId(int $messageId): int
     {
         return (int) $this->createQueryBuilder('umb')
             ->select('COUNT(umb.id)')
-            ->where('IDENTITY(umb.message) = :messageId')
+            ->where('umb.messageId = :messageId')
             ->setParameter('messageId', $messageId)
             ->getQuery()
             ->getSingleScalarResult();
diff --git a/src/Domain/Messaging/Repository/UserMessageForwardRepository.php b/src/Domain/Messaging/Repository/UserMessageForwardRepository.php
index b0fa5e58..e2a48b46 100644
--- a/src/Domain/Messaging/Repository/UserMessageForwardRepository.php
+++ b/src/Domain/Messaging/Repository/UserMessageForwardRepository.php
@@ -16,7 +16,7 @@ public function getCountByMessageId(int $messageId): int
     {
         return (int) $this->createQueryBuilder('umf')
             ->select('COUNT(umf.id)')
-            ->where('IDENTITY(umf.message) = :messageId')
+            ->where('umf.messageId = :messageId')
             ->setParameter('messageId', $messageId)
             ->getQuery()
             ->getSingleScalarResult();
diff --git a/src/Domain/Messaging/Service/EmailService.php b/src/Domain/Messaging/Service/EmailService.php
new file mode 100644
index 00000000..c3c3fc30
--- /dev/null
+++ b/src/Domain/Messaging/Service/EmailService.php
@@ -0,0 +1,174 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Messaging\Service;
+
+use PhpList\Core\Domain\Messaging\Message\AsyncEmailMessage;
+use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
+use Symfony\Component\Mailer\MailerInterface;
+use Symfony\Component\Messenger\MessageBusInterface;
+use Symfony\Component\Mime\Email;
+use Symfony\Component\Mime\Address;
+
+class EmailService
+{
+    private MailerInterface $mailer;
+    private string $defaultFromEmail;
+    private MessageBusInterface $messageBus;
+
+    public function __construct(
+        MailerInterface $mailer,
+        string $defaultFromEmail,
+        MessageBusInterface $messageBus
+    ) {
+        $this->mailer = $mailer;
+        $this->defaultFromEmail = $defaultFromEmail;
+        $this->messageBus = $messageBus;
+    }
+
+    /**
+     * Send a simple email asynchronously
+     *
+     * @param Email $email
+     * @param array $cc
+     * @param array $bcc
+     * @param array $replyTo
+     * @param array $attachments
+     * @return void
+     */
+    public function sendEmail(
+        Email $email,
+        array $cc = [],
+        array $bcc = [],
+        array $replyTo = [],
+        array $attachments = []
+    ): void {
+        if (count($email->getFrom()) === 0) {
+            $email->from($this->defaultFromEmail);
+        }
+
+        $message = new AsyncEmailMessage($email, $cc, $bcc, $replyTo, $attachments);
+        $this->messageBus->dispatch($message);
+    }
+
+    /**
+     * Send a simple email synchronously
+     *
+     * @param Email $email
+     * @param array $cc
+     * @param array $bcc
+     * @param array $replyTo
+     * @param array $attachments
+     * @return void
+     * @throws TransportExceptionInterface
+     */
+    public function sendEmailSync(
+        Email $email,
+        array $cc = [],
+        array $bcc = [],
+        array $replyTo = [],
+        array $attachments = []
+    ): void {
+        if (count($email->getFrom()) === 0) {
+            $email->from($this->defaultFromEmail);
+        }
+
+        foreach ($cc as $ccAddress) {
+            $email->addCc($ccAddress);
+        }
+
+        foreach ($bcc as $bccAddress) {
+            $email->addBcc($bccAddress);
+        }
+
+        foreach ($replyTo as $replyToAddress) {
+            $email->addReplyTo($replyToAddress);
+        }
+
+        foreach ($attachments as $attachment) {
+            $email->attachFromPath($attachment);
+        }
+
+        $this->mailer->send($email);
+    }
+
+    /**
+     * Email multiple recipients asynchronously
+     *
+     * @param array $toAddresses Array of recipient email addresses
+     * @param string $subject Email subject
+     * @param string $text Plain text content
+     * @param string $html HTML content (optional)
+     * @param string|null $from Sender email address (optional, uses default if not provided)
+     * @param string|null $fromName Sender name (optional)
+     * @param array $attachments Array of file paths to attach (optional)
+     *
+     * @return void
+     */
+    public function sendBulkEmail(
+        array $toAddresses,
+        string $subject,
+        string $text,
+        string $html = '',
+        ?string $from = null,
+        ?string $fromName = null,
+        array $attachments = []
+    ): void {
+        $baseEmail = (new Email())
+            ->subject($subject)
+            ->text($text)
+            ->html($html);
+
+        if ($from) {
+            $baseEmail->from($fromName ? new Address($from, $fromName) : $from);
+        }
+
+        foreach ($toAddresses as $recipient) {
+            $email = clone $baseEmail;
+            $email->to($recipient);
+
+            $this->sendEmail($email, [], [], [], $attachments);
+        }
+    }
+
+    /**
+     * Email multiple recipients synchronously
+     *
+     * @param array $toAddresses Array of recipient email addresses
+     * @param string $subject Email subject
+     * @param string $text Plain text content
+     * @param string $html HTML content (optional)
+     * @param string|null $from Sender email address (optional, uses default if not provided)
+     * @param string|null $fromName Sender name (optional)
+     * @param array $attachments Array of file paths to attach (optional)
+     *
+     * @return void
+     * @throws TransportExceptionInterface
+     */
+    public function sendBulkEmailSync(
+        array $toAddresses,
+        string $subject,
+        string $text,
+        string $html = '',
+        ?string $from = null,
+        ?string $fromName = null,
+        array $attachments = []
+    ): void {
+        $baseEmail = (new Email())
+            ->subject($subject)
+            ->text($text)
+            ->html($html);
+
+        if ($from) {
+            $baseEmail->from($fromName ? new Address($from, $fromName) : $from);
+        }
+
+        foreach ($toAddresses as $recipient) {
+            $email = clone $baseEmail;
+            $email->to($recipient);
+
+            $this->sendEmailSync($email, [], [], [], $attachments);
+        }
+    }
+}
diff --git a/src/Domain/Subscription/Service/Manager/AttributeDefinitionManager.php b/src/Domain/Subscription/Service/Manager/AttributeDefinitionManager.php
index 8e2a0307..d8983e65 100644
--- a/src/Domain/Subscription/Service/Manager/AttributeDefinitionManager.php
+++ b/src/Domain/Subscription/Service/Manager/AttributeDefinitionManager.php
@@ -8,14 +8,19 @@
 use PhpList\Core\Domain\Subscription\Model\Dto\AttributeDefinitionDto;
 use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition;
 use PhpList\Core\Domain\Subscription\Repository\SubscriberAttributeDefinitionRepository;
+use PhpList\Core\Domain\Subscription\Validator\AttributeTypeValidator;
 
 class AttributeDefinitionManager
 {
     private SubscriberAttributeDefinitionRepository $definitionRepository;
+    private AttributeTypeValidator $attributeTypeValidator;
 
-    public function __construct(SubscriberAttributeDefinitionRepository $definitionRepository)
-    {
+    public function __construct(
+        SubscriberAttributeDefinitionRepository $definitionRepository,
+        AttributeTypeValidator $attributeTypeValidator
+    ) {
         $this->definitionRepository = $definitionRepository;
+        $this->attributeTypeValidator = $attributeTypeValidator;
     }
 
     public function create(AttributeDefinitionDto $attributeDefinitionDto): SubscriberAttributeDefinition
@@ -24,6 +29,7 @@ public function create(AttributeDefinitionDto $attributeDefinitionDto): Subscrib
         if ($existingAttribute) {
             throw new AttributeDefinitionCreationException('Attribute definition already exists', 409);
         }
+        $this->attributeTypeValidator->validate($attributeDefinitionDto->type);
 
         $attributeDefinition = (new SubscriberAttributeDefinition())
             ->setName($attributeDefinitionDto->name)
@@ -46,6 +52,7 @@ public function update(
         if ($existingAttribute && $existingAttribute->getId() !== $attributeDefinition->getId()) {
             throw new AttributeDefinitionCreationException('Another attribute with this name already exists.', 409);
         }
+        $this->attributeTypeValidator->validate($attributeDefinitionDto->type);
 
         $attributeDefinition
             ->setName($attributeDefinitionDto->name)
diff --git a/src/Domain/Subscription/Validator/AttributeTypeValidator.php b/src/Domain/Subscription/Validator/AttributeTypeValidator.php
new file mode 100644
index 00000000..3923cdfc
--- /dev/null
+++ b/src/Domain/Subscription/Validator/AttributeTypeValidator.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Domain\Subscription\Validator;
+
+use InvalidArgumentException;
+use PhpList\Core\Domain\Common\Model\ValidationContext;
+use PhpList\Core\Domain\Common\Validator\ValidatorInterface;
+use Symfony\Component\Validator\Exception\ValidatorException;
+
+class AttributeTypeValidator implements ValidatorInterface
+{
+    private const VALID_TYPES = [
+        'textline',
+        'checkbox',
+        'checkboxgroup',
+        'radio',
+        'select',
+        'hidden',
+        'textarea',
+        'date',
+    ];
+
+    public function validate(mixed $value, ValidationContext $context = null): void
+    {
+        if (!is_string($value)) {
+            throw new InvalidArgumentException('Value must be a string.');
+        }
+
+        $errors = [];
+        if (!in_array($value, self::VALID_TYPES, true)) {
+            $errors[] = sprintf(
+                'Invalid attribute type: "%s". Valid types are: %s',
+                $value,
+                implode(', ', self::VALID_TYPES)
+            );
+        }
+
+        if (!empty($errors)) {
+            throw new ValidatorException(implode("\n", $errors));
+        }
+    }
+}
diff --git a/tests/Unit/Domain/Configuration/Service/Manager/ConfigManagerTest.php b/tests/Unit/Domain/Configuration/Service/Manager/ConfigManagerTest.php
new file mode 100644
index 00000000..3b14122b
--- /dev/null
+++ b/tests/Unit/Domain/Configuration/Service/Manager/ConfigManagerTest.php
@@ -0,0 +1,146 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Tests\Unit\Domain\Configuration\Service\Manager;
+
+use PhpList\Core\Domain\Configuration\Model\Config;
+use PhpList\Core\Domain\Configuration\Repository\ConfigRepository;
+use PhpList\Core\Domain\Configuration\Service\Manager\ConfigManager;
+use PHPUnit\Framework\TestCase;
+
+class ConfigManagerTest extends TestCase
+{
+    public function testGetByItemReturnsConfigFromRepository(): void
+    {
+        $configRepository = $this->createMock(ConfigRepository::class);
+        $manager = new ConfigManager($configRepository);
+
+        $config = new Config();
+        $config->setKey('test_item');
+        $config->setValue('test_value');
+
+        $configRepository->expects($this->once())
+            ->method('findOneBy')
+            ->with(['item' => 'test_item'])
+            ->willReturn($config);
+
+        $result = $manager->getByItem('test_item');
+
+        $this->assertSame($config, $result);
+        $this->assertSame('test_item', $result->getKey());
+        $this->assertSame('test_value', $result->getValue());
+    }
+
+    public function testGetAllReturnsAllConfigsFromRepository(): void
+    {
+        $configRepository = $this->createMock(ConfigRepository::class);
+        $manager = new ConfigManager($configRepository);
+
+        $config1 = new Config();
+        $config1->setKey('item1');
+        $config1->setValue('value1');
+
+        $config2 = new Config();
+        $config2->setKey('item2');
+        $config2->setValue('value2');
+
+        $configs = [$config1, $config2];
+
+        $configRepository->expects($this->once())
+            ->method('findAll')
+            ->willReturn($configs);
+
+        $result = $manager->getAll();
+
+        $this->assertSame($configs, $result);
+        $this->assertCount(2, $result);
+        $this->assertSame('item1', $result[0]->getKey());
+        $this->assertSame('value1', $result[0]->getValue());
+        $this->assertSame('item2', $result[1]->getKey());
+        $this->assertSame('value2', $result[1]->getValue());
+    }
+
+    public function testUpdateSavesConfigToRepository(): void
+    {
+        $configRepository = $this->createMock(ConfigRepository::class);
+        $manager = new ConfigManager($configRepository);
+
+        $config = new Config();
+        $config->setKey('test_item');
+        $config->setValue('test_value');
+        $config->setEditable(true);
+
+        $configRepository->expects($this->once())
+            ->method('save')
+            ->with($config);
+
+        $manager->update($config, 'new_value');
+    }
+
+    public function testCreateSavesNewConfigToRepository(): void
+    {
+        $configRepository = $this->createMock(ConfigRepository::class);
+        $manager = new ConfigManager($configRepository);
+
+        $configRepository->expects($this->once())
+            ->method('save')
+            ->with($this->callback(function (Config $config) {
+                return $config->getKey() === 'test_key' &&
+                    $config->getValue() === 'test_value' &&
+                    $config->isEditable() === true &&
+                    $config->getType() === 'test_type';
+            }));
+
+        $manager->create('test_key', 'test_value', true, 'test_type');
+    }
+    public function testGetByItemReturnsNullWhenItemDoesNotExist(): void
+    {
+        $configRepository = $this->createMock(ConfigRepository::class);
+        $manager = new ConfigManager($configRepository);
+
+        $configRepository->expects($this->once())
+            ->method('findOneBy')
+            ->with(['item' => 'non_existent_item'])
+            ->willReturn(null);
+
+        $result = $manager->getByItem('non_existent_item');
+
+        $this->assertNull($result);
+    }
+
+    public function testUpdateThrowsExceptionWhenConfigIsNotEditable(): void
+    {
+        $configRepository = $this->createMock(ConfigRepository::class);
+        $manager = new ConfigManager($configRepository);
+
+        $config = new Config();
+        $config->setKey('test_item');
+        $config->setValue('test_value');
+        $config->setEditable(false);
+
+        $configRepository->expects($this->never())
+            ->method('save');
+
+        $this->expectException(\PhpList\Core\Domain\Configuration\Exception\ConfigNotEditableException::class);
+        $this->expectExceptionMessage('Configuration item "test_item" is not editable.');
+
+        $manager->update($config, 'new_value');
+    }
+
+    public function testDeleteRemovesConfigFromRepository(): void
+    {
+        $configRepository = $this->createMock(ConfigRepository::class);
+        $manager = new ConfigManager($configRepository);
+
+        $config = new Config();
+        $config->setKey('test_item');
+        $config->setValue('test_value');
+
+        $configRepository->expects($this->once())
+            ->method('remove')
+            ->with($config);
+
+        $manager->delete($config);
+    }
+}
diff --git a/tests/Unit/Domain/Identity/Model/AdministratorTest.php b/tests/Unit/Domain/Identity/Model/AdministratorTest.php
index 10508b26..f721f58b 100644
--- a/tests/Unit/Domain/Identity/Model/AdministratorTest.php
+++ b/tests/Unit/Domain/Identity/Model/AdministratorTest.php
@@ -4,8 +4,11 @@
 
 namespace PhpList\Core\Tests\Unit\Domain\Identity\Model;
 
+use DateTime;
 use PhpList\Core\Domain\Common\Model\Interfaces\DomainModel;
 use PhpList\Core\Domain\Identity\Model\Administrator;
+use PhpList\Core\Domain\Identity\Model\Privileges;
+use PhpList\Core\Domain\Identity\Model\PrivilegeFlag;
 use PhpList\Core\TestingSupport\Traits\ModelTestTrait;
 use PhpList\Core\TestingSupport\Traits\SimilarDatesAssertionTrait;
 use PHPUnit\Framework\TestCase;
@@ -75,7 +78,7 @@ public function testUpdateModificationDateSetsModificationDateToNow(): void
     {
         $this->subject->updateUpdatedAt();
 
-        self::assertSimilarDates(new \DateTime(), $this->subject->getUpdatedAt());
+        self::assertSimilarDates(new DateTime(), $this->subject->getUpdatedAt());
     }
 
     public function testGetPasswordHashInitiallyReturnsEmptyString(): void
@@ -98,7 +101,7 @@ public function testGetPasswordChangeDateInitiallyReturnsNull(): void
 
     public function testSetPasswordHashSetsPasswordChangeDateToNow(): void
     {
-        $date = new \DateTime();
+        $date = new DateTime();
         $this->subject->setPasswordHash('Zaphod Beeblebrox');
 
         self::assertSimilarDates($date, $this->subject->getPasswordChangeDate());
@@ -127,4 +130,42 @@ public function testSetSuperUserSetsSuperUser(): void
 
         self::assertTrue($this->subject->isSuperUser());
     }
+
+    public function testGetPrivilegesInitiallyReturnsEmptyPrivileges(): void
+    {
+        $privileges = $this->subject->getPrivileges();
+
+        self::assertInstanceOf(Privileges::class, $privileges);
+
+        foreach (PrivilegeFlag::cases() as $flag) {
+            self::assertFalse($privileges->has($flag));
+        }
+    }
+
+    public function testSetPrivilegesSetsPrivileges(): void
+    {
+        $privileges = Privileges::fromSerialized('');
+        $privileges = $privileges->grant(PrivilegeFlag::Subscribers);
+
+        $this->subject->setPrivileges($privileges);
+
+        $retrievedPrivileges = $this->subject->getPrivileges();
+        self::assertTrue($retrievedPrivileges->has(PrivilegeFlag::Subscribers));
+        self::assertFalse($retrievedPrivileges->has(PrivilegeFlag::Campaigns));
+    }
+
+    public function testSetPrivilegesWithMultiplePrivileges(): void
+    {
+        $privileges = Privileges::fromSerialized('');
+        $privileges = $privileges
+            ->grant(PrivilegeFlag::Subscribers)
+            ->grant(PrivilegeFlag::Campaigns);
+
+        $this->subject->setPrivileges($privileges);
+
+        $retrievedPrivileges = $this->subject->getPrivileges();
+        self::assertTrue($retrievedPrivileges->has(PrivilegeFlag::Subscribers));
+        self::assertTrue($retrievedPrivileges->has(PrivilegeFlag::Campaigns));
+        self::assertFalse($retrievedPrivileges->has(PrivilegeFlag::Statistics));
+    }
 }
diff --git a/tests/Unit/Domain/Identity/Model/PrivilegeFlagTest.php b/tests/Unit/Domain/Identity/Model/PrivilegeFlagTest.php
new file mode 100644
index 00000000..d7a6ad26
--- /dev/null
+++ b/tests/Unit/Domain/Identity/Model/PrivilegeFlagTest.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Tests\Unit\Domain\Identity\Model;
+
+use PhpList\Core\Domain\Identity\Model\PrivilegeFlag;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Testcase for the PrivilegeFlag enum.
+ */
+class PrivilegeFlagTest extends TestCase
+{
+    public function testEnumHasSubscribersCase(): void
+    {
+        self::assertSame('subscribers', PrivilegeFlag::Subscribers->value);
+    }
+
+    public function testEnumHasCampaignsCase(): void
+    {
+        self::assertSame('campaigns', PrivilegeFlag::Campaigns->value);
+    }
+
+    public function testEnumHasStatisticsCase(): void
+    {
+        self::assertSame('statistics', PrivilegeFlag::Statistics->value);
+    }
+
+    public function testEnumHasSettingsCase(): void
+    {
+        self::assertSame('settings', PrivilegeFlag::Settings->value);
+    }
+
+    public function testEnumHasFourCases(): void
+    {
+        $cases = PrivilegeFlag::cases();
+        
+        self::assertCount(4, $cases);
+        self::assertContains(PrivilegeFlag::Subscribers, $cases);
+        self::assertContains(PrivilegeFlag::Campaigns, $cases);
+        self::assertContains(PrivilegeFlag::Statistics, $cases);
+        self::assertContains(PrivilegeFlag::Settings, $cases);
+    }
+}
diff --git a/tests/Unit/Domain/Identity/Model/PrivilegesTest.php b/tests/Unit/Domain/Identity/Model/PrivilegesTest.php
new file mode 100644
index 00000000..e81b4afc
--- /dev/null
+++ b/tests/Unit/Domain/Identity/Model/PrivilegesTest.php
@@ -0,0 +1,102 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Tests\Unit\Domain\Identity\Model;
+
+use InvalidArgumentException;
+use PhpList\Core\Domain\Identity\Model\Privileges;
+use PhpList\Core\Domain\Identity\Model\PrivilegeFlag;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Testcase for the Privileges class.
+ */
+class PrivilegesTest extends TestCase
+{
+    private Privileges $subject;
+
+    protected function setUp(): void
+    {
+        $this->subject = Privileges::fromSerialized('');
+    }
+
+    public function testFromSerializedWithInvalidDataThrowsError(): void
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Invalid serialized privileges string.');
+
+        Privileges::fromSerialized('invalid data');
+    }
+
+    public function testFromSerializedWithValidDataReturnsPopulatedPrivileges(): void
+    {
+        $data = [PrivilegeFlag::Subscribers->value => true];
+        $serialized = serialize($data);
+        
+        $privileges = Privileges::fromSerialized($serialized);
+        
+        self::assertTrue($privileges->has(PrivilegeFlag::Subscribers));
+    }
+
+    public function testToSerializedReturnsSerializedData(): void
+    {
+        $privileges = Privileges::fromSerialized('');
+        $privileges = $privileges->grant(PrivilegeFlag::Subscribers);
+        
+        $serialized = $privileges->toSerialized();
+        $data = unserialize($serialized);
+        
+        self::assertTrue($data[PrivilegeFlag::Subscribers->value]);
+    }
+
+    public function testHasReturnsFalseForUnsetPrivilege(): void
+    {
+        self::assertFalse($this->subject->has(PrivilegeFlag::Subscribers));
+    }
+
+    public function testHasReturnsTrueForSetPrivilege(): void
+    {
+        $this->subject = $this->subject->grant(PrivilegeFlag::Subscribers);
+        
+        self::assertTrue($this->subject->has(PrivilegeFlag::Subscribers));
+    }
+
+    public function testGrantSetsPrivilege(): void
+    {
+        $result = $this->subject->grant(PrivilegeFlag::Subscribers);
+        
+        self::assertTrue($result->has(PrivilegeFlag::Subscribers));
+    }
+
+    public function testGrantReturnsNewInstance(): void
+    {
+        $result = $this->subject->grant(PrivilegeFlag::Subscribers);
+        
+        self::assertNotSame($this->subject, $result);
+    }
+
+    public function testRevokeClearsPrivilege(): void
+    {
+        $this->subject = $this->subject->grant(PrivilegeFlag::Subscribers);
+        $result = $this->subject->revoke(PrivilegeFlag::Subscribers);
+        
+        self::assertFalse($result->has(PrivilegeFlag::Subscribers));
+    }
+
+    public function testRevokeReturnsNewInstance(): void
+    {
+        $result = $this->subject->revoke(PrivilegeFlag::Subscribers);
+        
+        self::assertNotSame($this->subject, $result);
+    }
+
+    public function testAllReturnsAllPrivileges(): void
+    {
+        $this->subject = $this->subject->grant(PrivilegeFlag::Subscribers);
+        $all = $this->subject->all();
+        
+        self::assertTrue($all[PrivilegeFlag::Subscribers->value]);
+        self::assertFalse($all[PrivilegeFlag::Campaigns->value]);
+    }
+}
diff --git a/tests/Unit/Domain/Identity/Service/AdminAttributeDefinitionManagerTest.php b/tests/Unit/Domain/Identity/Service/AdminAttributeDefinitionManagerTest.php
index b557e2f0..e42aba74 100644
--- a/tests/Unit/Domain/Identity/Service/AdminAttributeDefinitionManagerTest.php
+++ b/tests/Unit/Domain/Identity/Service/AdminAttributeDefinitionManagerTest.php
@@ -9,6 +9,7 @@
 use PhpList\Core\Domain\Identity\Repository\AdminAttributeDefinitionRepository;
 use PhpList\Core\Domain\Identity\Service\AdminAttributeDefinitionManager;
 use PhpList\Core\Domain\Identity\Exception\AttributeDefinitionCreationException;
+use PhpList\Core\Domain\Subscription\Validator\AttributeTypeValidator;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 
@@ -20,7 +21,8 @@ class AdminAttributeDefinitionManagerTest extends TestCase
     protected function setUp(): void
     {
         $this->repository = $this->createMock(AdminAttributeDefinitionRepository::class);
-        $this->subject = new AdminAttributeDefinitionManager($this->repository);
+        $attributeTypeValidator = $this->createMock(AttributeTypeValidator::class);
+        $this->subject = new AdminAttributeDefinitionManager($this->repository, $attributeTypeValidator);
     }
 
     public function testCreateCreatesNewAttributeDefinition(): void
diff --git a/tests/Unit/Domain/Messaging/Command/SendTestEmailCommandTest.php b/tests/Unit/Domain/Messaging/Command/SendTestEmailCommandTest.php
new file mode 100644
index 00000000..c1b4a92c
--- /dev/null
+++ b/tests/Unit/Domain/Messaging/Command/SendTestEmailCommandTest.php
@@ -0,0 +1,198 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Tests\Unit\Domain\Messaging\Command;
+
+use PhpList\Core\Domain\Messaging\Command\SendTestEmailCommand;
+use PhpList\Core\Domain\Messaging\Service\EmailService;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Tester\CommandTester;
+use Symfony\Component\Mime\Email;
+
+class SendTestEmailCommandTest extends TestCase
+{
+    private EmailService&MockObject $emailService;
+    private CommandTester $commandTester;
+
+    protected function setUp(): void
+    {
+        $this->emailService = $this->createMock(EmailService::class);
+        $command = new SendTestEmailCommand($this->emailService);
+
+        $application = new Application();
+        $application->add($command);
+
+        $this->commandTester = new CommandTester($command);
+    }
+
+    public function testExecuteWithValidEmail(): void
+    {
+        $this->emailService->expects($this->once())
+            ->method('sendEmail')
+            ->with($this->callback(function (Email $email) {
+                $this->assertEquals('Test Email from phpList', $email->getSubject());
+                $this->assertStringContainsString('This is a test email', $email->getTextBody());
+                $this->assertStringContainsString('<h1>Test</h1>', $email->getHtmlBody());
+
+                $toAddresses = $email->getTo();
+                $this->assertCount(1, $toAddresses);
+                $this->assertEquals('test@example.com', $toAddresses[0]->getAddress());
+
+                $fromAddresses = $email->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals('admin@example.com', $fromAddresses[0]->getAddress());
+                $this->assertEquals('Admin Team', $fromAddresses[0]->getName());
+
+                return true;
+            }));
+
+        $this->commandTester->execute([
+            'recipient' => 'test@example.com',
+        ]);
+
+        $output = $this->commandTester->getDisplay();
+        $this->assertStringContainsString('Queuing test email for', $output);
+        $this->assertStringContainsString('Test email queued successfully', $output);
+        $this->assertStringContainsString('It will be sent asynchronously', $output);
+
+        $this->assertEquals(0, $this->commandTester->getStatusCode());
+    }
+
+    public function testExecuteWithValidEmailSync(): void
+    {
+        $this->emailService->expects($this->once())
+            ->method('sendEmailSync')
+            ->with($this->callback(function (Email $email) {
+                $this->assertEquals('Test Email from phpList', $email->getSubject());
+                $this->assertStringContainsString('This is a test email', $email->getTextBody());
+                $this->assertStringContainsString('<h1>Test</h1>', $email->getHtmlBody());
+
+                $toAddresses = $email->getTo();
+                $this->assertCount(1, $toAddresses);
+                $this->assertEquals('test@example.com', $toAddresses[0]->getAddress());
+
+                $fromAddresses = $email->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals('admin@example.com', $fromAddresses[0]->getAddress());
+                $this->assertEquals('Admin Team', $fromAddresses[0]->getName());
+
+                return true;
+            }));
+
+        $this->commandTester->execute([
+            'recipient' => 'test@example.com',
+            '--sync' => true,
+        ]);
+
+        $output = $this->commandTester->getDisplay();
+        $this->assertStringContainsString('Sending test email synchronously to', $output);
+        $this->assertStringContainsString('Test email sent successfully', $output);
+
+        $this->assertEquals(0, $this->commandTester->getStatusCode());
+    }
+
+    public function testExecuteWithoutRecipient(): void
+    {
+        $this->emailService->expects($this->never())
+            ->method('sendEmail');
+        $this->emailService->expects($this->never())
+            ->method('sendEmailSync');
+
+        $this->commandTester->execute([]);
+
+        $output = $this->commandTester->getDisplay();
+        $this->assertStringContainsString('Recipient email address not provided', $output);
+
+        $this->assertEquals(1, $this->commandTester->getStatusCode());
+    }
+
+    public function testExecuteWithoutRecipientSync(): void
+    {
+        $this->emailService->expects($this->never())
+            ->method('sendEmail');
+        $this->emailService->expects($this->never())
+            ->method('sendEmailSync');
+
+        $this->commandTester->execute([
+            '--sync' => true,
+        ]);
+
+        $output = $this->commandTester->getDisplay();
+        $this->assertStringContainsString('Recipient email address not provided', $output);
+
+        $this->assertEquals(1, $this->commandTester->getStatusCode());
+    }
+
+    public function testExecuteWithInvalidEmail(): void
+    {
+        $this->emailService->expects($this->never())
+            ->method('sendEmail');
+        $this->emailService->expects($this->never())
+            ->method('sendEmailSync');
+
+        $this->commandTester->execute([
+            'recipient' => 'invalid-email',
+        ]);
+
+        $output = $this->commandTester->getDisplay();
+        $this->assertStringContainsString('Invalid email address', $output);
+
+        $this->assertEquals(1, $this->commandTester->getStatusCode());
+    }
+
+    public function testExecuteWithInvalidEmailSync(): void
+    {
+        $this->emailService->expects($this->never())
+            ->method('sendEmail');
+        $this->emailService->expects($this->never())
+            ->method('sendEmailSync');
+
+        $this->commandTester->execute([
+            'recipient' => 'invalid-email',
+            '--sync' => true,
+        ]);
+
+        $output = $this->commandTester->getDisplay();
+        $this->assertStringContainsString('Invalid email address', $output);
+
+        $this->assertEquals(1, $this->commandTester->getStatusCode());
+    }
+
+    public function testExecuteWithEmailServiceException(): void
+    {
+        $this->emailService->expects($this->once())
+            ->method('sendEmail')
+            ->willThrowException(new \Exception('Test exception'));
+
+        $this->commandTester->execute([
+            'recipient' => 'test@example.com',
+        ]);
+
+        $output = $this->commandTester->getDisplay();
+        $this->assertStringContainsString('Failed to send test email', $output);
+        $this->assertStringContainsString('Test exception', $output);
+
+        $this->assertEquals(1, $this->commandTester->getStatusCode());
+    }
+
+    public function testExecuteWithEmailServiceExceptionSync(): void
+    {
+        $this->emailService->expects($this->once())
+            ->method('sendEmailSync')
+            ->willThrowException(new \Exception('Test sync exception'));
+
+        $this->commandTester->execute([
+            'recipient' => 'test@example.com',
+            '--sync' => true,
+        ]);
+
+        $output = $this->commandTester->getDisplay();
+        $this->assertStringContainsString('Failed to send test email', $output);
+        $this->assertStringContainsString('Test sync exception', $output);
+
+        $this->assertEquals(1, $this->commandTester->getStatusCode());
+    }
+}
diff --git a/tests/Unit/Domain/Messaging/Service/EmailServiceTest.php b/tests/Unit/Domain/Messaging/Service/EmailServiceTest.php
new file mode 100644
index 00000000..9409320b
--- /dev/null
+++ b/tests/Unit/Domain/Messaging/Service/EmailServiceTest.php
@@ -0,0 +1,305 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Tests\Unit\Domain\Messaging\Service;
+
+use PhpList\Core\Domain\Messaging\Message\AsyncEmailMessage;
+use PhpList\Core\Domain\Messaging\Service\EmailService;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Mailer\MailerInterface;
+use Symfony\Component\Messenger\Envelope;
+use Symfony\Component\Messenger\MessageBusInterface;
+use Symfony\Component\Mime\Email;
+
+class EmailServiceTest extends TestCase
+{
+    private EmailService $emailService;
+    private MailerInterface&MockObject $mailer;
+    private MessageBusInterface&MockObject $messageBus;
+    private string $defaultFromEmail = 'default@example.com';
+
+    protected function setUp(): void
+    {
+        $this->mailer = $this->createMock(MailerInterface::class);
+        $this->messageBus = $this->createMock(MessageBusInterface::class);
+        $this->emailService = new EmailService($this->mailer, $this->defaultFromEmail, $this->messageBus);
+    }
+
+    public function testSendEmailWithDefaultFrom(): void
+    {
+        $email = (new Email())
+            ->to('recipient@example.com')
+            ->subject('Test Subject')
+            ->text('Test Content');
+
+        $this->messageBus->expects($this->once())
+            ->method('dispatch')
+            ->with($this->callback(function (AsyncEmailMessage $message) {
+                $sentEmail = $message->getEmail();
+                $fromAddresses = $sentEmail->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals($this->defaultFromEmail, $fromAddresses[0]->getAddress());
+                return true;
+            }))
+            ->willReturn(new Envelope(new AsyncEmailMessage($email)));
+
+        $this->emailService->sendEmail($email);
+    }
+
+    public function testSendEmailSyncWithDefaultFrom(): void
+    {
+        $email = (new Email())
+            ->to('recipient@example.com')
+            ->subject('Test Subject')
+            ->text('Test Content');
+
+        $this->mailer->expects($this->once())
+            ->method('send')
+            ->with($this->callback(function (Email $sentEmail) {
+                $fromAddresses = $sentEmail->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals($this->defaultFromEmail, $fromAddresses[0]->getAddress());
+                return true;
+            }));
+
+        $this->emailService->sendEmailSync($email);
+    }
+
+    public function testSendEmailWithCustomFrom(): void
+    {
+        $customFrom = 'custom@example.com';
+        $email = (new Email())
+            ->from($customFrom)
+            ->to('recipient@example.com')
+            ->subject('Test Subject')
+            ->text('Test Content');
+
+        $this->messageBus->expects($this->once())
+            ->method('dispatch')
+            ->with($this->callback(function (AsyncEmailMessage $message) use ($customFrom) {
+                $sentEmail = $message->getEmail();
+                $fromAddresses = $sentEmail->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals($customFrom, $fromAddresses[0]->getAddress());
+                return true;
+            }))
+            ->willReturn(new Envelope(new AsyncEmailMessage($email)));
+
+        $this->emailService->sendEmail($email);
+    }
+
+    public function testSendEmailSyncWithCustomFrom(): void
+    {
+        $customFrom = 'custom@example.com';
+        $email = (new Email())
+            ->from($customFrom)
+            ->to('recipient@example.com')
+            ->subject('Test Subject')
+            ->text('Test Content');
+
+        $this->mailer->expects($this->once())
+            ->method('send')
+            ->with($this->callback(function (Email $sentEmail) use ($customFrom) {
+                $fromAddresses = $sentEmail->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals($customFrom, $fromAddresses[0]->getAddress());
+                return true;
+            }));
+
+        $this->emailService->sendEmailSync($email);
+    }
+
+    public function testSendEmailWithCcBccAndReplyTo(): void
+    {
+        $email = (new Email())
+            ->to('recipient@example.com')
+            ->subject('Test Subject')
+            ->text('Test Content');
+
+        $cc = ['cc@example.com'];
+        $bcc = ['bcc@example.com'];
+        $replyTo = ['reply@example.com'];
+
+        $this->messageBus->expects($this->once())
+            ->method('dispatch')
+            ->with($this->callback(function (AsyncEmailMessage $message) use ($cc, $bcc, $replyTo) {
+                $this->assertEquals($cc, $message->getCc());
+                $this->assertEquals($bcc, $message->getBcc());
+                $this->assertEquals($replyTo, $message->getReplyTo());
+                return true;
+            }))
+            ->willReturn(new Envelope(new AsyncEmailMessage($email)));
+
+        $this->emailService->sendEmail($email, $cc, $bcc, $replyTo);
+    }
+
+    public function testSendEmailSyncWithCcBccAndReplyTo(): void
+    {
+        $email = (new Email())
+            ->to('recipient@example.com')
+            ->subject('Test Subject')
+            ->text('Test Content');
+
+        $cc = ['cc@example.com'];
+        $bcc = ['bcc@example.com'];
+        $replyTo = ['reply@example.com'];
+
+        $this->mailer->expects($this->once())
+            ->method('send')
+            ->with($this->callback(function (Email $sentEmail) use ($cc, $bcc, $replyTo) {
+                $ccAddresses = $sentEmail->getCc();
+                $bccAddresses = $sentEmail->getBcc();
+                $replyToAddresses = $sentEmail->getReplyTo();
+
+                $this->assertCount(1, $ccAddresses);
+                $this->assertEquals($cc[0], $ccAddresses[0]->getAddress());
+
+                $this->assertCount(1, $bccAddresses);
+                $this->assertEquals($bcc[0], $bccAddresses[0]->getAddress());
+
+                $this->assertCount(1, $replyToAddresses);
+                $this->assertEquals($replyTo[0], $replyToAddresses[0]->getAddress());
+
+                return true;
+            }));
+
+        $this->emailService->sendEmailSync($email, $cc, $bcc, $replyTo);
+    }
+
+    public function testSendEmailWithAttachments(): void
+    {
+        $email = (new Email())
+            ->to('recipient@example.com')
+            ->subject('Test Subject')
+            ->text('Test Content');
+
+        $attachments = ['/path/to/attachment.pdf'];
+
+        $this->messageBus->expects($this->once())
+            ->method('dispatch')
+            ->with($this->callback(function (AsyncEmailMessage $message) use ($attachments) {
+                $this->assertEquals($attachments, $message->getAttachments());
+                return true;
+            }))
+            ->willReturn(new Envelope(new AsyncEmailMessage($email)));
+
+        $this->emailService->sendEmail($email, [], [], [], $attachments);
+    }
+
+    public function testSendEmailSyncWithAttachments(): void
+    {
+        $email = (new Email())
+            ->to('recipient@example.com')
+            ->subject('Test Subject')
+            ->text('Test Content');
+
+        $attachments = ['/path/to/attachment.pdf'];
+
+        $this->mailer->expects($this->once())
+            ->method('send');
+
+        $this->emailService->sendEmailSync($email, [], [], [], $attachments);
+    }
+
+    public function testSendBulkEmail(): void
+    {
+        $recipients = ['user1@example.com', 'user2@example.com', 'user3@example.com'];
+        $subject = 'Bulk Test Subject';
+        $text = 'Bulk Test Content';
+        $html = '<p>Bulk Test HTML Content</p>';
+        $from = 'sender@example.com';
+        $fromName = 'Sender Name';
+
+        $this->messageBus->expects($this->exactly(count($recipients)))
+            ->method('dispatch')
+            ->with($this->callback(function (AsyncEmailMessage $message) use (
+                $subject,
+                $text,
+                $html,
+                $from,
+                $fromName
+            ) {
+                $sentEmail = $message->getEmail();
+                $this->assertEquals($subject, $sentEmail->getSubject());
+                $this->assertEquals($text, $sentEmail->getTextBody());
+                $this->assertEquals($html, $sentEmail->getHtmlBody());
+
+                $fromAddresses = $sentEmail->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals($from, $fromAddresses[0]->getAddress());
+                $this->assertEquals($fromName, $fromAddresses[0]->getName());
+
+                return true;
+            }))
+            ->willReturn(new Envelope($this->createMock(AsyncEmailMessage::class)));
+
+        $this->emailService->sendBulkEmail($recipients, $subject, $text, $html, $from, $fromName);
+    }
+
+    public function testSendBulkEmailSync(): void
+    {
+        $recipients = ['user1@example.com', 'user2@example.com', 'user3@example.com'];
+        $subject = 'Bulk Test Subject';
+        $text = 'Bulk Test Content';
+        $html = '<p>Bulk Test HTML Content</p>';
+        $from = 'sender@example.com';
+        $fromName = 'Sender Name';
+
+        $this->mailer->expects($this->exactly(count($recipients)))
+            ->method('send')
+            ->with($this->callback(function (Email $sentEmail) use ($subject, $text, $html, $from, $fromName) {
+                $this->assertEquals($subject, $sentEmail->getSubject());
+                $this->assertEquals($text, $sentEmail->getTextBody());
+                $this->assertEquals($html, $sentEmail->getHtmlBody());
+
+                $fromAddresses = $sentEmail->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals($from, $fromAddresses[0]->getAddress());
+                $this->assertEquals($fromName, $fromAddresses[0]->getName());
+
+                return true;
+            }));
+
+        $this->emailService->sendBulkEmailSync($recipients, $subject, $text, $html, $from, $fromName);
+    }
+
+    public function testSendBulkEmailWithDefaultFrom(): void
+    {
+        $recipients = ['user1@example.com', 'user2@example.com'];
+        $subject = 'Bulk Test Subject';
+        $text = 'Bulk Test Content';
+
+        $this->messageBus->expects($this->exactly(count($recipients)))
+            ->method('dispatch')
+            ->with($this->callback(function (AsyncEmailMessage $message) {
+                $sentEmail = $message->getEmail();
+                $fromAddresses = $sentEmail->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals($this->defaultFromEmail, $fromAddresses[0]->getAddress());
+                return true;
+            }))
+            ->willReturn(new Envelope($this->createMock(AsyncEmailMessage::class)));
+
+        $this->emailService->sendBulkEmail($recipients, $subject, $text);
+    }
+
+    public function testSendBulkEmailSyncWithDefaultFrom(): void
+    {
+        $recipients = ['user1@example.com', 'user2@example.com'];
+        $subject = 'Bulk Test Subject';
+        $text = 'Bulk Test Content';
+
+        $this->mailer->expects($this->exactly(count($recipients)))
+            ->method('send')
+            ->with($this->callback(function (Email $sentEmail) {
+                $fromAddresses = $sentEmail->getFrom();
+                $this->assertCount(1, $fromAddresses);
+                $this->assertEquals($this->defaultFromEmail, $fromAddresses[0]->getAddress());
+                return true;
+            }));
+
+        $this->emailService->sendBulkEmailSync($recipients, $subject, $text);
+    }
+}
diff --git a/tests/Unit/Domain/Subscription/Service/AttributeDefinitionManagerTest.php b/tests/Unit/Domain/Subscription/Service/AttributeDefinitionManagerTest.php
index bc5a6f18..85b3bb93 100644
--- a/tests/Unit/Domain/Subscription/Service/AttributeDefinitionManagerTest.php
+++ b/tests/Unit/Domain/Subscription/Service/AttributeDefinitionManagerTest.php
@@ -9,6 +9,7 @@
 use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition;
 use PhpList\Core\Domain\Subscription\Repository\SubscriberAttributeDefinitionRepository;
 use PhpList\Core\Domain\Subscription\Service\Manager\AttributeDefinitionManager;
+use PhpList\Core\Domain\Subscription\Validator\AttributeTypeValidator;
 use PHPUnit\Framework\TestCase;
 
 class AttributeDefinitionManagerTest extends TestCase
@@ -16,7 +17,8 @@ class AttributeDefinitionManagerTest extends TestCase
     public function testCreateAttributeDefinition(): void
     {
         $repository = $this->createMock(SubscriberAttributeDefinitionRepository::class);
-        $manager = new AttributeDefinitionManager($repository);
+        $validator = $this->createMock(AttributeTypeValidator::class);
+        $manager = new AttributeDefinitionManager($repository, $validator);
 
         $dto = new AttributeDefinitionDto(
             name: 'Country',
@@ -48,7 +50,8 @@ public function testCreateAttributeDefinition(): void
     public function testCreateThrowsWhenAttributeAlreadyExists(): void
     {
         $repository = $this->createMock(SubscriberAttributeDefinitionRepository::class);
-        $manager = new AttributeDefinitionManager($repository);
+        $validator = $this->createMock(AttributeTypeValidator::class);
+        $manager = new AttributeDefinitionManager($repository, $validator);
 
         $dto = new AttributeDefinitionDto(
             name: 'Country',
@@ -74,7 +77,8 @@ public function testCreateThrowsWhenAttributeAlreadyExists(): void
     public function testUpdateAttributeDefinition(): void
     {
         $repository = $this->createMock(SubscriberAttributeDefinitionRepository::class);
-        $manager = new AttributeDefinitionManager($repository);
+        $validator = $this->createMock(AttributeTypeValidator::class);
+        $manager = new AttributeDefinitionManager($repository, $validator);
 
         $attribute = new SubscriberAttributeDefinition();
         $attribute->setName('Old');
@@ -108,7 +112,8 @@ public function testUpdateAttributeDefinition(): void
     public function testUpdateThrowsWhenAnotherAttributeWithSameNameExists(): void
     {
         $repository = $this->createMock(SubscriberAttributeDefinitionRepository::class);
-        $manager = new AttributeDefinitionManager($repository);
+        $validator = $this->createMock(AttributeTypeValidator::class);
+        $manager = new AttributeDefinitionManager($repository, $validator);
 
         $dto = new AttributeDefinitionDto(
             name: 'Existing',
@@ -138,7 +143,8 @@ public function testUpdateThrowsWhenAnotherAttributeWithSameNameExists(): void
     public function testDeleteAttributeDefinition(): void
     {
         $repository = $this->createMock(SubscriberAttributeDefinitionRepository::class);
-        $manager = new AttributeDefinitionManager($repository);
+        $validator = $this->createMock(AttributeTypeValidator::class);
+        $manager = new AttributeDefinitionManager($repository, $validator);
 
         $attribute = new SubscriberAttributeDefinition();
 
diff --git a/tests/Unit/Domain/Subscription/Validator/AttributeTypeValidatorTest.php b/tests/Unit/Domain/Subscription/Validator/AttributeTypeValidatorTest.php
new file mode 100644
index 00000000..c0ab3a5a
--- /dev/null
+++ b/tests/Unit/Domain/Subscription/Validator/AttributeTypeValidatorTest.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpList\Core\Tests\Unit\Domain\Subscription\Validator;
+
+use InvalidArgumentException;
+use PhpList\Core\Domain\Subscription\Validator\AttributeTypeValidator;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Validator\Exception\ValidatorException;
+
+class AttributeTypeValidatorTest extends TestCase
+{
+    private AttributeTypeValidator $validator;
+
+    protected function setUp(): void
+    {
+        $this->validator = new AttributeTypeValidator();
+    }
+
+    public function testValidatesValidType(): void
+    {
+        $this->validator->validate('textline');
+        $this->validator->validate('checkbox');
+        $this->validator->validate('date');
+        
+        $this->assertTrue(true);
+    }
+
+    public function testThrowsExceptionForInvalidType(): void
+    {
+        $this->expectException(ValidatorException::class);
+        $this->expectExceptionMessage('Invalid attribute type: "invalid_type"');
+        
+        $this->validator->validate('invalid_type');
+    }
+
+    public function testThrowsExceptionForNonStringValue(): void
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Value must be a string.');
+        
+        $this->validator->validate(123);
+    }
+}