winglet/outbox

Winglet-specific transactional outbox package with Doctrine persistence

Maintainers

Package info

bitbucket.org/eventteri/wingletoutbox

pkg:composer/winglet/outbox

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

v0.1 2026-08-01 06:49 UTC

This package is auto-updated.

Last update: 2026-08-01 06:52:28 UTC


README

Winglet-specific optional transactional outbox package. The package intentionally includes its Doctrine persistence implementation in the same Composer package to keep installation, smoke testing and releases simple.

Scope

  • persists Winglet DomainEventInterface events in the same Doctrine transaction as application state
  • unique event id and durable JSON payload/metadata
  • MariaDB-safe batch claiming using FOR UPDATE SKIP LOCKED
  • configurable retry schedule
  • stale processing claim recovery
  • transport abstraction
  • explicit development logging transport
  • CLI inspect, dispatch and recovery commands
  • readiness probe and Doctrine mapping through WingletExtensionInterface

Installation

composer require winglet/outbox

Load the package config and enable the extension:

return [
    'app' => [
        'extensions' => [
            \Winglet\Outbox\Bridge\OutboxExtension::class,
        ],
    ],
    'outbox' => [
        'enabled' => true,
        'dispatch' => [
            // none = persist only; outbox:dispatch refuses to claim messages
            // log  = development/smoke-test transport
            'transport' => 'none',
        ],
    ],
];

Run resources/migrations/001_winglet_outbox.sql through the application's normal migration process.

Transaction semantics

Applications publish through the Core interface:

final class ConfirmRegistrationService
{
    public function __construct(private EventPublisherInterface $events) {}

    public function confirm(): void
    {
        // Called inside the application's Doctrine transaction.
        $this->events->publish(new RegistrationConfirmed(...));
    }
}

TransactionalOutboxEventPublisher calls Doctrine persist() but deliberately does not flush. The application transaction owns flushing and commit. A rollback therefore rolls back both domain mutations and the outbox row.

publish() means accepted for durable asynchronous publication. It does not mean an external consumer has already received the event.

Commands

winglet outbox:inspect all 50
winglet outbox:dispatch 100 worker-1
winglet outbox:recover-stale 100

Transports

The package includes only:

  • none: persistence without delivery; dispatch command aborts before claiming
  • log: development/smoke-test delivery that records event identifiers and marks delivery successful; rejected when app.env=prod

Production transports implement OutboxTransportInterface and are registered by the consuming application or another optional Winglet package.

Webhook delivery may later be implemented as an optional outbox transport. It is not part of this package.