PHPackages                             rasuvaeff/yii3-outbox - 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. [Queues &amp; Workers](/categories/queues)
4. /
5. rasuvaeff/yii3-outbox

ActiveLibrary[Queues &amp; Workers](/categories/queues)

rasuvaeff/yii3-outbox
=====================

Transactional outbox pattern for Yii3

v1.0.2(4w ago)0531—4.1%4BSD-3-ClausePHPPHP 8.3 - 8.5CI passing

Since Jun 13Pushed 1w agoCompare

[ Source](https://github.com/rasuvaeff/yii3-outbox)[ Packagist](https://packagist.org/packages/rasuvaeff/yii3-outbox)[ Docs](https://github.com/rasuvaeff/yii3-outbox)[ RSS](/packages/rasuvaeff-yii3-outbox/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (21)Versions (5)Used By (4)

rasuvaeff/yii3-outbox
=====================

[](#rasuvaeffyii3-outbox)

[![Stable Version](https://camo.githubusercontent.com/24e04437e9c0ae7f9f2c62a56236da3c085f5477d8bd0de53a1e66ecf0748485/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d6f7574626f782f762f737461626c65)](https://packagist.org/packages/rasuvaeff/yii3-outbox)[![Total Downloads](https://camo.githubusercontent.com/0d97102f2fb095c77c3bff9e69721aa77e6a55638d4e243bf0f4be51b3437f40/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d6f7574626f782f646f776e6c6f616473)](https://packagist.org/packages/rasuvaeff/yii3-outbox)[![Build](https://github.com/rasuvaeff/yii3-outbox/actions/workflows/build.yml/badge.svg)](https://github.com/rasuvaeff/yii3-outbox/actions)[![Static analysis](https://github.com/rasuvaeff/yii3-outbox/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/rasuvaeff/yii3-outbox/actions)[![Psalm Level](https://camo.githubusercontent.com/82cf0d0e2e61ed3fe40a07559750d296476879a179dd853e979b48445c14982e/68747470733a2f2f73686570686572642e6465762f6769746875622f7261737576616566662f796969332d6f7574626f782f6c6576656c2e737667)](https://shepherd.dev/github/rasuvaeff/yii3-outbox)[![PHP](https://camo.githubusercontent.com/21270dd9d1586f1a0a5b5592042001dbcba5b8997bfc09141ab051f00e0a9c83/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f7261737576616566662f796969332d6f7574626f782f706870)](https://packagist.org/packages/rasuvaeff/yii3-outbox)[![License](https://camo.githubusercontent.com/ee7bc886391ca5fe860d7a5777e9a4041ba4e0e708df0f3adab52e90c8753fcf/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d6f7574626f782f6c6963656e7365)](https://packagist.org/packages/rasuvaeff/yii3-outbox)[Русская версия](README.ru.md)

Transactional outbox pattern implementation for Yii3. Provides a stateless core for reliably publishing messages with configurable retry policies.

> Using an AI coding assistant? [llms.txt](llms.txt) has a compact API reference you can use.

Requirements
------------

[](#requirements)

- PHP 8.3+
- `psr/clock` ^1.0
- `psr/log` ^3.0

Installation
------------

[](#installation)

```
composer require rasuvaeff/yii3-outbox
```

Usage
-----

[](#usage)

### Recording a message

[](#recording-a-message)

```
use DateTimeImmutable;
use Psr\Clock\ClockInterface;
use Rasuvaeff\Yii3Outbox\InMemoryStorage;
use Rasuvaeff\Yii3Outbox\Outbox;

$clock = new class implements ClockInterface {
    public function now(): DateTimeImmutable { return new DateTimeImmutable(); }
};

$outbox = new Outbox(storage: $storage, clock: $clock);

$message = $outbox->record(
    type: 'order.created',
    payload: json_encode(['orderId' => 42]),
    aggregateId: 'order-42',
);
```

### Implementing storage

[](#implementing-storage)

```
use Rasuvaeff\Yii3Outbox\StorageInterface;
use Rasuvaeff\Yii3Outbox\OutboxMessage;

final class DbStorage implements StorageInterface
{
    public function save(OutboxMessage $message): void
    {
        // INSERT INTO outbox ... ON CONFLICT(id) DO UPDATE ...
    }

    public function findPending(array $types = [], int $limit = 1000): array
    {
        // SELECT * FROM outbox WHERE status = 'pending'
        //   [AND type IN (:types)] LIMIT :limit  -- empty $types = all types
        // For retry support, also return status = 'pending' with attempts > 0
    }

    public function markPublished(OutboxMessage $message): void
    {
        // UPDATE outbox SET status = 'published' WHERE id = ?
    }

    public function markFailed(OutboxMessage $message): void
    {
        // UPDATE outbox SET status = 'failed' WHERE id = ?
    }

    public function getById(string $id): ?OutboxMessage
    {
        // SELECT * FROM outbox WHERE id = ?
    }
}
```

### Implementing a publisher

[](#implementing-a-publisher)

```
use Rasuvaeff\Yii3Outbox\PublisherInterface;
use Rasuvaeff\Yii3Outbox\OutboxMessage;
use Rasuvaeff\Yii3Outbox\PublishException;

final class RabbitPublisher implements PublisherInterface
{
    public function publish(OutboxMessage $message): void
    {
        try {
            // publish to RabbitMQ, Kafka, etc.
        } catch (\Throwable $e) {
            throw new PublishException(
                message: $e->getMessage(),
                outboxMessage: $message,
                previous: $e,
            );
        }
    }
}
```

### Processing the outbox

[](#processing-the-outbox)

```
use Rasuvaeff\Yii3Outbox\Processor;
use Rasuvaeff\Yii3Outbox\RetryPolicy;

$processor = new Processor(
    storage: $storage,
    publisher: $publisher,
    retryPolicy: new RetryPolicy(maxAttempts: 3, delaySeconds: 60),
    clock: $clock,
    batchSize: 100,
);

$result = $processor->process();
// $result->published — successfully published
// $result->failed   — publish exceptions (message kept Pending if retries remain)
// $result->skipped  — not yet ready for retry
```

### Retry behaviour

[](#retry-behaviour)

When a publish fails:

- If attempts &lt; `maxAttempts` → message stays `Pending`, will be retried after `delaySeconds`
- If attempts &gt;= `maxAttempts` → message is marked `Failed` (terminal)

```
$policy = new RetryPolicy(maxAttempts: 3, delaySeconds: 60);

$policy->shouldRetry($message);          // bool — attempts remaining?
$policy->isReadyForRetry($message, $now); // bool — delay elapsed?

```

### Using InMemoryStorage for tests

[](#using-inmemorystorage-for-tests)

```
use Rasuvaeff\Yii3Outbox\InMemoryStorage;

$storage = new InMemoryStorage();
$storage->save($message);

$pending = $storage->findPending();
$storage->count();
$storage->clear();
```

API reference
-------------

[](#api-reference)

### Outbox

[](#outbox)

MethodDescription`__construct(storage, clock)`Main entry point`record(type, payload, aggregateId?)`Create and persist message, returns `OutboxMessage`### OutboxMessage

[](#outboxmessage)

MethodDescription`create(type, payload, aggregateId?, createdAt?)`Factory with auto-generated ID`getId()`Message ID (32-char hex)`getType()`Message type`getPayload()`Raw payload string`getStatus()``OutboxStatus` enum`getCreatedAt()``DateTimeImmutable``getAttempts()`Number of publish attempts`getLastAttemptAt()``?DateTimeImmutable``getAggregateId()``?string``withStatus(status)`Returns new instance with status`withAttempt(at)`Returns new instance with incremented attempts and timestamp### OutboxStatus

[](#outboxstatus)

CaseValue`Pending``'pending'``Published``'published'``Failed``'failed'`### RetryPolicy

[](#retrypolicy)

MethodDescription`__construct(maxAttempts, delaySeconds)`Default: 3 attempts, 60s delay`shouldRetry(message)`Checks attempt count`isReadyForRetry(message, now)`Checks attempts + delay elapsed### Processor

[](#processor)

MethodDescription`__construct(storage, publisher, retryPolicy, clock, batchSize, logger)`Default batch: 100`process()`Returns `ProcessingResult`### ProcessingResult

[](#processingresult)

Property/MethodDescription`$published`Count of successfully published messages`$failed`Count of publish exceptions this run`$skipped`Count of messages not ready for retry`total()`Sum of all counters### Serializer

[](#serializer)

MethodDescription`serialize(message)`Message to JSON`deserialize(data)`JSON to MessageSecurity
--------

[](#security)

- Storage implementations must use parameterized queries for all user values.
- Message payload is stored as-is; validate before saving if needed.

Examples
--------

[](#examples)

See [examples/](examples/) for complete usage examples.

Development
-----------

[](#development)

```
make install
make build
make cs-fix
make test
make test-coverage
make mutation
make release-check
```

`make test-coverage` and `make mutation` bootstrap `pcov` inside the `composer:2` container because the base image has no coverage driver.

License
-------

[](#license)

BSD-3-Clause. See [LICENSE.md](LICENSE.md).

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance97

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity54

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

Every ~8 days

Total

3

Last Release

28d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b0812d5572a7041dfe36e222d295b2e6dc55833a605350fcde58a51a5965ed30?d=identicon)[rasuvaeff](/maintainers/rasuvaeff)

---

Top Contributors

[![rasuvaeff](https://avatars.githubusercontent.com/u/1352718?v=4)](https://github.com/rasuvaeff "rasuvaeff (29 commits)")

---

Tags

event-drivenoutbox-patternphpreliabilitytransactional-messagingyii3event-drivenmessage queuetransactionalyii3outbox

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rasuvaeff-yii3-outbox/health.svg)

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

###  Alternatives

[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

564576.7k54](/packages/ecotone-ecotone)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k16](/packages/tempest-framework)[symfony/messenger

Helps applications send and receive messages to/from other applications or via message queues

1.1k132.9M1.6k](/packages/symfony-messenger)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

85036.3k](/packages/flow-php-flow)[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.7k38.9k](/packages/matomo-matomo)

PHPackages © 2026

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