Vortos
AWS SES

AWS SES

First-class email delivery for Vortos with AWS SES, transactional outbox, suppression lists, bounce handling, rate limits, failover, and observability.

AWS SES

vortos/vortos-aws-ses is the first-class email package for Vortos. It gives application code a small typed API while the package owns the enterprise concerns: SES transport mapping, transactional outbox, worker relay, rate limiting, suppression lists, bounce and complaint webhooks, audit logging, metrics, logs, traces, and testing fakes.

The application API stays small

Most business code injects Vortos\AwsSes\Contract\MailerInterface and calls send(). The selected driver, middleware stack, outbox behavior, retry policy, and observability are configured once at the package boundary.

What The Package Guarantees

The package separates email intent from delivery. A command handler can save a domain aggregate and enqueue an email in the same database transaction. A worker later relays the email to SES with bounded retries. If SES is down, the database transaction still commits and the outbox relay keeps trying.

The package also prevents common production mistakes:

  • Invalid email addresses are rejected before the provider call.
  • Suppressed recipients are detected before sending.
  • SES account rate limits are respected by middleware.
  • Provider errors are mapped into Vortos exceptions.
  • Bounce and complaint notifications can update the local suppression list.
  • Logs, traces, and metrics are enabled by default but can be disabled globally or by typed section enum.

Drivers

ses sends through AWS SES.

log writes email attempts to the PSR logger and is the development default.

null silently drops email and wires test-friendly fake services.

Service Guarantees

Vortos does not hide delivery guarantees behind one ambiguous config switch. The interface you inject tells reviewers exactly what will happen.

use Vortos\AwsSes\Contract\MailerInterface;
use Vortos\AwsSes\Contract\StandaloneMailerInterface;
use Vortos\AwsSes\Contract\ImmediateMailerInterface;

final class RegistrationHandler
{
    public function __construct(
        private MailerInterface $mailer,
    ) {}
}

MailerInterface is the normal application path. When outbox is enabled, it writes to aws_ses_outbox and requires an active CommandBus or UnitOfWork transaction.

StandaloneMailerInterface opens a short transaction for only the email outbox row. Use it for maintenance commands or scripts that want async reliability without joining a domain transaction.

ImmediateMailerInterface calls the configured transport directly. Use it for diagnostics, probes, or rare workflows where email must be attempted immediately and no outbox row should exist.

Do not send user-facing business email from HTTP controllers directly

Send from command handlers through MailerInterface so domain persistence and email intent are committed atomically. Controllers should translate HTTP into commands.

Architecture

Command Handler
    |
    | MailerInterface::send(Email)
    v
Email Middleware Stack
    | validate
    | suppression check
    | deduplication
    | rate limit
    | audit/log/trace/metrics
    v
Transactional Outbox Writer
    |
    | same DB transaction as domain save
    v
aws_ses_outbox
    |
    | vortos:ses:outbox:relay
    v
SES Driver
    |
    v
AWS SES

Webhook flow:

AWS SNS -> /webhooks/aws/ses -> SNS signature verifier
    |
    +-> Bounce handlers
    +-> Complaint handlers
    +-> local suppression list

Documentation Map

Package Registration

bootstrap/app.php
use Vortos\AwsSes\DependencyInjection\AwsSesPackage;

$packages = [
    // ... other packages
    new AwsSesPackage(),
];

The package is optional. If a project does not install or register it, no SES services, tables, commands, middleware, or workers are added.

On this page