create-outbox-pattern

Generates Transactional Outbox pattern components for PHP 8.4. Creates OutboxMessage entity, repository, publisher, and processor with unit tests.

59 stars

Best use case

create-outbox-pattern is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Generates Transactional Outbox pattern components for PHP 8.4. Creates OutboxMessage entity, repository, publisher, and processor with unit tests.

Teams using create-outbox-pattern should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/create-outbox-pattern/SKILL.md --create-dirs "https://raw.githubusercontent.com/dykyi-roman/awesome-claude-code/main/skills/create-outbox-pattern/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/create-outbox-pattern/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How create-outbox-pattern Compares

Feature / Agentcreate-outbox-patternStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Generates Transactional Outbox pattern components for PHP 8.4. Creates OutboxMessage entity, repository, publisher, and processor with unit tests.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Outbox Pattern Generator

Creates Transactional Outbox pattern infrastructure for reliable event publishing.

## When to Use

- Need reliable event publishing across transaction boundaries
- Prevent message loss if broker is down
- Ensure exactly-once or at-least-once delivery
- Maintain consistency between database and message broker

## Component Characteristics

### OutboxMessage Entity
- Immutable value object in Domain layer
- Contains: id, aggregateType, aggregateId, eventType, payload, timestamps
- Supports reconstitution for persistence
- Methods: withProcessed(), withRetryIncremented()

### OutboxRepository
- Interface in Domain layer
- Implementation in Infrastructure layer
- Methods: save, findUnprocessed, markAsProcessed, incrementRetry, delete

### OutboxProcessor
- Application layer service
- Polls for unprocessed messages
- Publishes to message broker
- Handles failures with retry and dead letter

### Console Command
- Infrastructure layer
- Runs as daemon or one-shot
- Configurable batch size and interval

---

## Generation Process

### Step 1: Generate Domain Layer

**Path:** `src/Domain/Shared/Outbox/`

1. `OutboxMessage.php` — Immutable message entity
2. `OutboxRepositoryInterface.php` — Repository contract

### Step 2: Generate Application Layer

**Path:** `src/Application/Shared/`

1. `Port/Output/MessagePublisherInterface.php` — Publisher port
2. `Port/Output/DeadLetterRepositoryInterface.php` — Dead letter port
3. `Outbox/ProcessingResult.php` — Result value object
4. `Outbox/MessageResult.php` — Result enum
5. `Outbox/OutboxProcessor.php` — Processing service

### Step 3: Generate Infrastructure Layer

**Path:** `src/Infrastructure/`

1. `Persistence/Doctrine/Repository/DoctrineOutboxRepository.php`
2. `Console/OutboxProcessCommand.php`
3. Database migration

### Step 4: Generate Tests

1. `tests/Unit/Domain/Shared/Outbox/OutboxMessageTest.php`
2. `tests/Unit/Application/Shared/Outbox/OutboxProcessorTest.php`

---

## Key Principles

### Transactional Consistency
```php
// In UseCase - save outbox message in SAME transaction
$this->connection->transactional(function () use ($order, $event) {
    $this->orderRepository->save($order);
    $this->outboxRepository->save(
        OutboxMessage::create(
            id: Uuid::uuid4()->toString(),
            aggregateType: 'Order',
            aggregateId: $order->id()->toString(),
            eventType: 'order.placed',
            payload: $event->toArray()
        )
    );
});
```

### Retry with Dead Letter
1. Retry up to MAX_RETRIES times
2. Exponential backoff between retries
3. Move to dead letter queue after max retries
4. Log all failures with context

### Message Headers
Include metadata for tracing:
- message_id, correlation_id, causation_id
- aggregate_type, aggregate_id
- created_at

---

## File Placement

| Layer | Path |
|-------|------|
| Domain Entity | `src/Domain/Shared/Outbox/` |
| Domain Interface | `src/Domain/Shared/Outbox/` |
| Application Service | `src/Application/Shared/Outbox/` |
| Application Port | `src/Application/Shared/Port/Output/` |
| Infrastructure Repo | `src/Infrastructure/Persistence/Doctrine/Repository/` |
| Infrastructure Console | `src/Infrastructure/Console/` |
| Unit Tests | `tests/Unit/{Layer}/{Path}/` |

---

## Naming Conventions

| Component | Pattern | Example |
|-----------|---------|---------|
| Entity | `{Name}` | `OutboxMessage` |
| Repository Interface | `{Name}RepositoryInterface` | `OutboxRepositoryInterface` |
| Repository Impl | `Doctrine{Name}Repository` | `DoctrineOutboxRepository` |
| Service | `{Name}Processor` | `OutboxProcessor` |
| Command | `{Name}Command` | `OutboxProcessCommand` |
| Test | `{ClassName}Test` | `OutboxMessageTest` |

---

## Quick Template Reference

### OutboxMessage

```php
final readonly class OutboxMessage
{
    public static function create(
        string $id,
        string $aggregateType,
        string $aggregateId,
        string $eventType,
        array $payload,
        ?string $correlationId = null,
        ?string $causationId = null
    ): self;

    public function isProcessed(): bool;
    public function isPoisoned(int $maxRetries): bool;
    public function payloadAsArray(): array;
    public function withProcessed(): self;
    public function withRetryIncremented(): self;
}
```

### OutboxRepositoryInterface

```php
interface OutboxRepositoryInterface
{
    public function save(OutboxMessage $message): void;
    public function findUnprocessed(int $limit = 100): array;
    public function markAsProcessed(string $id): void;
    public function incrementRetry(string $id): void;
    public function delete(string $id): void;
}
```

### OutboxProcessor

```php
final readonly class OutboxProcessor
{
    public function process(int $batchSize = 100): ProcessingResult;
}
```

---

## Usage Example

### Saving to Outbox

```php
// In UseCase
$message = OutboxMessage::create(
    id: Uuid::uuid4()->toString(),
    aggregateType: 'Order',
    aggregateId: $order->id()->toString(),
    eventType: 'order.placed',
    payload: [
        'order_id' => $order->id()->toString(),
        'customer_id' => $order->customerId()->toString(),
        'total' => $order->total()->amount(),
    ],
    correlationId: $command->correlationId
);

$this->outboxRepository->save($message);
```

### Console Command

```bash
# One-shot processing
php bin/console outbox:process --batch-size=100

# Daemon mode
php bin/console outbox:process --daemon --interval=1000
```

---

## DI Configuration

```yaml
# Symfony services.yaml
Domain\Shared\Outbox\OutboxRepositoryInterface:
    alias: Infrastructure\Persistence\Doctrine\Repository\DoctrineOutboxRepository

Application\Shared\Port\Output\MessagePublisherInterface:
    alias: Infrastructure\Messaging\RabbitMq\RabbitMqPublisher

Application\Shared\Outbox\OutboxProcessor:
    arguments:
        $maxRetries: 5
```

---

## Database Schema

```sql
CREATE TABLE outbox_messages (
    id VARCHAR(36) PRIMARY KEY,
    aggregate_type VARCHAR(255) NOT NULL,
    aggregate_id VARCHAR(255) NOT NULL,
    event_type VARCHAR(255) NOT NULL,
    payload JSONB NOT NULL,
    correlation_id VARCHAR(255),
    causation_id VARCHAR(255),
    created_at TIMESTAMP(6) NOT NULL,
    processed_at TIMESTAMP(6),
    retry_count INT NOT NULL DEFAULT 0
);

CREATE INDEX idx_outbox_unprocessed
ON outbox_messages (processed_at, created_at)
WHERE processed_at IS NULL;
```

---

## References

For complete PHP templates and test examples, see:
- `references/templates.md` — All component templates
- `references/tests.md` — Unit test examples

Related Skills

stability-patterns-knowledge

59
from dykyi-roman/awesome-claude-code

Stability Patterns knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Circuit Breaker, Retry, Rate Limiter, Bulkhead, and resilience audits.

saga-pattern-knowledge

59
from dykyi-roman/awesome-claude-code

Saga Pattern knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for saga orchestration, choreography, and distributed transaction audits.

outbox-pattern-knowledge

59
from dykyi-roman/awesome-claude-code

Outbox Pattern knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for transactional outbox, polling publisher, and reliable messaging audits.

detect-docker-antipatterns

59
from dykyi-roman/awesome-claude-code

Detects Docker antipatterns in PHP projects. Identifies layer ordering issues, cache invalidation, bloated images, and configuration smells.

detect-ci-antipatterns

59
from dykyi-roman/awesome-claude-code

Detects CI/CD antipatterns in pipeline configurations. Identifies slow pipelines, security issues, maintenance problems, and provides remediation guidance.

detect-architecture-pattern

59
from dykyi-roman/awesome-claude-code

Detects architectural patterns (MVC, DDD, Hexagonal, CQRS, Layered, Event Sourcing, Microservice) from namespace structure, interface placement, and dependency direction. Outputs confidence score per pattern.

create-visitor

59
from dykyi-roman/awesome-claude-code

Generates Visitor pattern for PHP 8.4. Creates operations on object structures without modifying element classes, with visitor interface, concrete visitors, and visitable elements. Includes unit tests.

create-value-object

59
from dykyi-roman/awesome-claude-code

Generates DDD Value Objects for PHP 8.4. Creates immutable, self-validating objects with equality comparison. Includes unit tests.

create-use-case

59
from dykyi-roman/awesome-claude-code

Generates Application Use Cases for PHP 8.4. Creates orchestration services that coordinate domain objects, handle transactions, and dispatch events. Includes unit tests.

create-unit-test

59
from dykyi-roman/awesome-claude-code

Generates PHPUnit unit tests for PHP 8.4. Creates isolated tests with AAA pattern, proper naming, attributes, and one behavior per test. Supports Value Objects, Entities, Services.

create-unit-of-work

59
from dykyi-roman/awesome-claude-code

Generates Unit of Work pattern components for PHP 8.4. Creates transactional consistency infrastructure with aggregate tracking, flush/rollback, domain event collection, and unit tests.

create-timeout

59
from dykyi-roman/awesome-claude-code

Generates Timeout pattern components for PHP 8.4. Creates execution time limit infrastructure with configurable timeouts, fallback support, stream timeouts, and unit tests.