Outbox Core
The core logic and trait definitions for Oxide Outbox, a high-performance implementation of the Transactional Outbox pattern for Rust.
outbox-core provides the architectural backbone for reliable message delivery, allowing you to decouple database transactions from asynchronous event publishing with full type safety.
Key Features
- Trait-First Architecture: Completely decoupled from specific storage or message brokers. Switch between Postgres, MySQL, Kafka, or RabbitMQ by implementing core traits.
- Type-Safe Generic Payloads: No more
serde_json::Valueoverhead. Define your events using your own domain types:Event<OrderCreated>,Event<UserSignedUp>, etc. - Async Native: Built from the ground up on
tokiofor maximum concurrency. - Flexible Idempotency: Built-in support for multiple deduplication strategies (UUID v7, Provided tokens, or Custom logic).
- Builder API:
OutboxManagerBuildergives one stable construction API regardless of which optional features (dlq, etc.) are enabled — no surprises from workspace feature unification. - Dead Letter Queue (feature
dlq): PluggableDlqHeaptrait + a built-inDlqProcessorthat drains chronically failing events on a timer and hands them off to the storage adapter for quarantine. - Extensible Storage & Transport: Interfaces designed to be implemented by specialized crates (like
outbox-postgres).
Core Concepts
The OutboxManager<S, P, PT>
The central engine that orchestrates event discovery and dispatching. It is generic over your Payload Type (PT), ensuring that every event handled by the manager respects your domain's type constraints.
S(Storage): ImplementsOutboxStorage<PT>. Responsible for DB operations (e.g., PostgreSQL, MySQL).P(Publisher): ImplementsTransport<PT>. Responsible for sending events to brokers (e.g., Kafka, NATS).PT(Payload Type): Your domain event type. Must implementDebug + Clone + Serialize.
Type-Safe Events
As of v0.3.0, events use a Payload<PT> wrapper. This ensures zero unnecessary JSON roundtrips and provides compile-time safety from the moment you record an event until it is sent to the transport layer.
Builder-based construction (v0.4.0)
OutboxManager is now built via OutboxManagerBuilder. This replaces the multiple new(..) overloads that used to switch shape under feature flags. The builder validates required fields at build() time and stays a single, stable API whether dlq is on or off.
Quick Start
Here is a complete, working example using outbox-core alongside outbox-postgres and a custom Tokio-channel based transport.
1. Define your Domain Event & Transport
First, define your event payload and implement the Transport trait to tell the outbox how to publish messages.
use *;
use ;
// 1. Define your strongly-typed event
// 2. Implement Transport for your chosen broker (e.g., Tokio MPSC, Kafka, etc.)
;
;
2. Wire up the Manager and Service
Set up your database pool, configure the outbox, and spawn the background manager task.
use ;
use PgPool;
use Arc;
use Duration;
use watch;
use ;
async
Core Traits
To extend outbox-core, you can implement these primary traits:
| Trait | Responsibility |
|---|---|
OutboxStorage<PT> |
Handles saving, locking, deleting and quarantining events in your DB (Postgres, Mongo, etc). |
Transport<PT> |
Defines how the event is published to the outside world (Kafka, RabbitMQ, HTTP). |
IdempotencyStorageProvider |
Checks if a request has already been processed to prevent duplicates. |
DlqHeap (feature dlq) |
Tracks per-event failure counts and drains entries that crossed the configured threshold. |
DLQ subsystem (feature dlq)
When the dlq feature is enabled:
OutboxConfigexposes two extra knobs:dlq_threshold(how many failures before quarantine) anddlq_interval_secs(how often the reaper ticks).OutboxManagerBuilder::dlq_heap(..)becomes required —build()returns an error if missing.- A background
DlqProcessoris spawned alongside the worker. On each tick it callsDlqHeap::drain_exceeded(threshold)and forwards results toOutboxStorage::quarantine_eventsfor atomic move into the quarantine table.
For a Redis-backed DlqHeap see outbox-redis. For a Postgres quarantine_events impl see outbox-postgres.