PHPackages                             agluh/outbox-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. agluh/outbox-bundle

ActiveSymfony-bundle[Framework](/categories/framework)

agluh/outbox-bundle
===================

Implements Outbox pattern for DDD-based Symfony applications

v0.1.0(5y ago)916MITPHPPHP ^7.4

Since Sep 3Pushed 5y ago2 watchersCompare

[ Source](https://github.com/agluh/outbox-bundle)[ Packagist](https://packagist.org/packages/agluh/outbox-bundle)[ RSS](/packages/agluh-outbox-bundle/feed)WikiDiscussions master Synced 5d ago

READMEChangelog (1)Dependencies (16)Versions (2)Used By (0)

Outbox bundle
=============

[](#outbox-bundle)

[![Build Status](https://camo.githubusercontent.com/b810aa2b3e61def67c2d9eb9f85458438aa86c4b09439bc49c332fe324e3903c/68747470733a2f2f7472617669732d63692e636f6d2f61676c75682f6f7574626f782d62756e646c652e7376673f6272616e63683d6d6173746572)](https://travis-ci.com/agluh/outbox-bundle)[![Latest Stable Version](https://camo.githubusercontent.com/f17fb1bf3d750883761c3ba26e0df76b87198528ceb2375c2dbd5c3baf9816d2/68747470733a2f2f706f7365722e707567782e6f72672f61676c75682f6f7574626f782d62756e646c652f76)](//packagist.org/packages/agluh/outbox-bundle)[![Total Downloads](https://camo.githubusercontent.com/e00213e8af5580be36f731ed73cab7a8e894e447e18f6519108a3784a6214b6a/68747470733a2f2f706f7365722e707567782e6f72672f61676c75682f6f7574626f782d62756e646c652f646f776e6c6f616473)](//packagist.org/packages/agluh/outbox-bundle)[![License](https://camo.githubusercontent.com/319b510eaa99bc460ddf109d696027f0e1d3cf2ff1371fac6d40bec0116a80a1/68747470733a2f2f706f7365722e707567782e6f72672f61676c75682f6f7574626f782d62756e646c652f6c6963656e7365)](//packagist.org/packages/agluh/outbox-bundle)

Implements [Outbox pattern](https://microservices.io/patterns/data/transactional-outbox.html) for DDD-based Symfony applications.

### How it works:

[](#how-it-works)

1. Bundle collects domain events from aggregate being persisted and save them in a separate table within a single database transaction.
2. After a successful commit those domain events are enqueued for publication.
    1. If bundle configured with `auto_publish=true` option, then domain events from outbox table will be processed using a Symfony event listener in the kernel.TERMINATE or console.TERMINATE events.
    2. If bundle configured with `auto_publish=false` option, then you should use CLI interface described below to periodically run worker for processing of stored events.

**Important note:** events are enqueued for publishing on-by-one in ascending order sorted by expected publication date (witch by default is date of registration domain event in outbox). If for some reason on *DomainEventEnqueuedForPublishingEvent* you did not marked domain event as published (i.e. publication date not set) then next time outbox will try to enqueue for publishing *the same domain event* until it succeeded. This ensures time consistency of published domain events.

**Note:** you can combine auto publishing with CLI-based publication at the same time. Locking mechanism ensure all events will be published in the right order.

### Acknowledgments

[](#acknowledgments)

Inspired by [Domain Event Bundle](https://github.com/headsnet/domain-events-bundle). Worker class mainly based on Worker from symfony/messenger component.

### Installation

[](#installation)

*Requires Symfony 4.4, or Symfony 5.x and PHP 7.4 and above*

Make sure Composer is installed globally, as explained in the [installation chapter](https://getcomposer.org/doc/00-intro.md)of the Composer documentation.

#### Applications that use Symfony Flex

[](#applications-that-use-symfony-flex)

Open a command console, enter your project directory and execute:

```
$ composer require agluh/outbox-bundle
```

#### Applications that don't use Symfony Flex

[](#applications-that-dont-use-symfony-flex)

Step 1: Download the Bundle

Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:

```
$ composer require agluh/outbox-bundle
```

Step 2: Enable the Bundle

Then, enable the bundle by adding it to the list of registered bundles in the `config/bundles.php` file of your project:

```
// config/bundles.php

return [
    // ...
    AGluh\Bundle\OutboxBundle\OutboxBundle::class => ['all' => true],
];
```

### Usage

[](#usage)

Outbox bundle integrates into your Symfony application mostly by application events. You should implement listeners for them as shown below.

```
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use AGluh\Bundle\OutboxBundle\Event\AggregateRootPreparedForPersistenceEvent;
use AGluh\Bundle\OutboxBundle\Event\DomainEventPreparedForPersistenceEvent;
use AGluh\Bundle\OutboxBundle\Event\DomainEventEnqueuedForPublishingEvent;
use AGluh\Bundle\OutboxBundle\Event\DomainEventPublishedEvent;

class OutboxIntegrationEventsListener implements EventSubscriberInterface
{
    /**
     * In this method you should collect domain events from your aggregate root
     * and than call $event->addDomainEvent() to persist them to outbox.
     */
    public function onAggregateRootPreparedForPersistence (AggregateRootPreparedForPersistenceEvent $event): void
    {
        $aggregateRoot = $event->aggregateRoot();

        /**
         * Example: DomainEventPublisher is a base class or an interface
         * of your aggregate.
         */
        if($aggregateRoot instanceof DomainEventPublisher) {

            /**
             * Here DomainEventPublisher::popEvents() is the method
             * that returns array of domain events for aggregate.
             */
            foreach ($aggregateRoot->popEvents() as $domainEvent) {
                /**
                 * Basically domain event can be any PHP type that supports serialization.
                 * See Serialization section below in docs.
                 */
                $event->addDomainEvent($domainEvent);
            }
        }
    }

    /**
     * In this method you can alter date of expected publication for domain event.
     * By default it will be the date of registration of the event in outbox.
     */
    public function onDomainEventPreparedForPersistence(DomainEventPreparedForPersistenceEvent $event): void
    {
        $domainEvent = $event->domainEvent();

        /**
         * Here DomainEvent is an interface or base class for your domain event.
         */
        if($domainEvent instanceof DomainEvent) {

            /**
             * In this example we use event occurrence date as date of expected publication.
             */
            $event->changeExpectedPublicationDate($domainEvent->occurrenceDate());
        }
    }

    /**
     * This function will be called by outbox bundle for each domain event should be published.
     */
    public function onDomainEventEnqueuedForPublishing(DomainEventEnqueuedForPublishingEvent $event): void
    {
        // It makes sense to stop propagation for event
        $event->stopPropagation();

        $domainEvent = $event->domainEvent();

        // Do whatever you mention under 'publish event' here. For example, send message to RabbitMQ.

        // You MUST set publication date here to mark event as published in outbox table.
        $event->setPublicationDate(new \DateTimeImmutable());
    }

    /**
     * This function will be called after outbox bundle persists domain event as published.
     */
    public function onDomainEventPublished(DomainEventPublishedEvent $event): void
    {
        // Do something if you want
    }

    public static function getSubscribedEvents(): array
    {
        return [
            AggregateRootPreparedForPersistenceEvent::class => 'onAggregateRootPreparedForPersistence',
            DomainEventPreparedForPersistenceEvent::class => 'onDomainEventPreparedForPersistence',
            DomainEventEnqueuedForPublishingEvent::class => 'onDomainEventEnqueuedForPublishing',
            DomainEventPublishedEvent::class => 'onDomainEventPublished'
        ];
    }
}
```

### Console commands

[](#console-commands)

#### Publish domain events

[](#publish-domain-events)

```
outbox:publish [options]

Options:
  -l, --limit=LIMIT                Limit the number of published events
  -m, --memory-limit=MEMORY-LIMIT  The memory limit the worker can consume
  -t, --time-limit=TIME-LIMIT      The time limit in seconds the worker can run
  -s, --sleep=SLEEP                Seconds to sleep before asking for new unpublished events after no unpublished events were found. Applicable only for demonized worker [default: 1]
  -d, --daemonize                  Daemonize worker
  -b, --batch-size=BATCH-SIZE      Limit the number of events worker can query at every iteration [default: 20]
  -h, --help                       Display this help message
  -q, --quiet                      Do not output any message
  -V, --version                    Display this application version
      --ansi                       Force ANSI output
      --no-ansi                    Disable ANSI output
  -n, --no-interaction             Do not ask any interactive question
  -e, --env=ENV                    The Environment name. [default: "dev"]
      --no-debug                   Switches off debug mode.
  -v|vv|vvv, --verbose             Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
```

The php process is not designed to work for a long time. So it has to quit periodically. Or, the command may exit because of error or exception. Something has to bring it back and continue events publication (if not used auto publication). You can use Supervisord for that. It starts processes and keep an eye on them while they are working.

Here an example of supervisord configuration.

```
[program:outbox_worker]
command=/path/to/bin/console outbox:publish --env=prod --no-debug --time-limit=3600 --daemonize
process_name=%(program_name)s_%(process_num)02d
numprocs=1 # There is no reason to use multiple workers here because of locking nature of events publication process
autostart=true
autorestart=true
startsecs=0
redirect_stderr=true
```

*Note: Pay attention to --time-limit it tells the command to exit after 60 minutes.*

#### Prune published domain events

[](#prune-published-domain-events)

```
outbox:prune-published
```

#### Stop workers

[](#stop-workers)

```
outbox:stop-workers
```

### Custom serializer

[](#custom-serializer)

By default, outbox bundle uses serialize/unserialize functions from PHP to convert domain event to a string during persistence. You can use a custom serializer for that purpose. For example, how to use symfony/serializer is shown below.

```
namespace App\Service;

use AGluh\Bundle\OutboxBundle\Serialization\SerializerInterface;
use AGluh\Bundle\OutboxBundle\Exception\DomainEventDecodingFailedException;

class CustomSerializer implements SerializerInterface
{
    private \Symfony\Component\Serializer\SerializerInterface $serializer;

    // Constructor skipped

    public function encode($domainEvent): string
    {
        return $this->serializer->serialize($domainEvent, 'json');
    }

    /**
     * @throws DomainEventDecodingFailedException
     */
    public function decode(string $data)
    {
        // In this example we don't convert json back to an object and simply use it further
        return $data;
    }
}
```

Then register new serializer as a service.

```
# config\services.yaml
services:
    agluh_outbox_bundle.serializer:
        class: App\Service\CustomSerializer
```

### Default configuration

[](#default-configuration)

```
agluh_outbox:
    table_name: outbox      # Name of outbox table for Doctrine mapping
    auto_publish: false     # Publish domain events on kernel.TERMINATE or console.TERMINATE
```

### Contributing

[](#contributing)

Contributions are welcome. Composer scripts are configured for your convenience:

```
> composer test       # Run test suite (you should set accessible MySQL server with DATABASE_URL env variable)
> composer cs         # Run coding standards checks
> composer cs-fix     # Fix coding standards violations
> composer static     # Run static analysis with Phpstan

```

###  Health Score

23

—

LowBetter than 27% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity12

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

2082d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/25745005?v=4)[Alexey Glukharev](/maintainers/agluh)[@agluh](https://github.com/agluh)

---

Top Contributors

[![agluh](https://avatars.githubusercontent.com/u/25745005?v=4)](https://github.com/agluh "agluh (21 commits)")

###  Code Quality

Static AnalysisPHPStan

Code StyleECS

Type Coverage Yes

### Embed Badge

![Health badge](/badges/agluh-outbox-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/agluh-outbox-bundle/health.svg)](https://phpackages.com/packages/agluh-outbox-bundle)
```

###  Alternatives

[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M651](/packages/sylius-sylius)[ec-cube/ec-cube

EC-CUBE EC open platform.

78527.0k1](/packages/ec-cube-ec-cube)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[contao/core-bundle

Contao Open Source CMS

1231.6M2.4k](/packages/contao-core-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
