Product Decisions This Supports
- Build vs. buy for reliable event publishing: the package implements the transactional outbox pattern (write domain events to a table inside the same DB transaction, relay them to the broker/consumer afterwards). This directly supports the decision to not hand-roll at-least-once delivery, dual-write workarounds, or "publish in an observer and hope" logic — a common source of lost or duplicated events in Laravel apps.
- Stability guarantee is now contractual, not aspirational: v1.0.0 is the first stable release and is functionally identical to 1.0.0-rc1. Per ADR 0008, the PHP API, table schema, relay settings, lock key, message layout, partitioning, metrics and endpoints now change only in a major version. This upgrades the earlier "pin the RC and accept a small pre-GA risk" position to a normal semver contract: green-light integration work against
^1.0, with no RC caveat.
- Operational risk materially de-risked: the release reports a public six-day run of the demo (775,045 paid orders, shop and consumer reconciling to the cent, 25 leader kills). That is direct evidence on the two things we would otherwise have had to validate ourselves — sustained relay throughput and correctness under repeated multi-instance leader failover — so the pilot scope can shrink from "prove the relay works" to "prove our event flow works on it."
- Roadmap sequencing: the RC window is closed; the two-to-four-week window has elapsed and GA has landed. Adoption work (schema migration, relay worker, monitoring) should be scheduled against
1.0.0 now with no gating dependency. Because no bug was reported against the RC, the upgrade from a pinned RC branch is a version bump, not a migration.
- Operability investment: the package exposes metrics, endpoints, a lock key and partitioning — these are inputs to our observability and horizontal-scaling plans. Budget for wiring metrics into our dashboards and for defining the relay's partitioning strategy alongside the infra roadmap, not after launch.
- Image/version pinning is now a real decision, not just a tag: the relay image publishes a moving
1 tag that tracks the latest 1.x patch release, while the Kubernetes base pins 1.0. We must choose deliberately — moving 1 for automatic patch uptake, or an explicit 1.0.x digest pin if we want change control over relay upgrades.
- Use cases this fits: order/payment state changes, cross-service notifications, anything where a DB commit must imply "event will eventually be published." Less compelling for fire-and-forget telemetry or low-value events where duplicate/loss tolerance is high.
When to Consider This Package
Adopt (or pilot) when:
- We already need atomic DB-write + event-publish semantics and currently rely on ad-hoc retries, queues inside transactions, or manual reconciliation jobs.
- The stack is Laravel/PHP and the team is comfortable operating a relay worker (a long-running process with locking — note the locked relay settings and lock key are part of the stable surface).
- We need partitioned, ordered-ish consumption at scale, and the exposed partitioning + metrics hooks map to our infra (multiple relay instances, per-partition ordering).
- We want a stable, semver-backed contract:
^1.0 is safe to build on, and upstream breaking changes now require a major version.
- We want a small, single-purpose dependency rather than a full event-bus platform.
Look elsewhere when:
- We need a managed/broker-native solution with vendor support and SLAs; this is a community package with an issue tracker as the support channel — GA does not change that.
- The team cannot own a relay process, its locking, and its monitoring — the operational burden is the real cost here, not the code.
- Our events are low-value or idempotency is unimplemented downstream — the outbox guarantees delivery, not exactly-once consumption, so consumer-side dedupe remains our problem.
- We're not on a relational DB where the outbox table can share the domain transaction.
- We require vendor-maintained base images or a hardened, audited supply chain: the relay is a Go binary built on franz-go and prometheus/client_golang, and patching cadence is the maintainer's, not ours.
How to Pitch It (Stakeholders)
To executives: we're standardising how the platform publishes business events so that a successful database commit reliably becomes a downstream event, instead of silently vanishing. The candidate library has just reached its first stable release: its interface, schema and operational surface are frozen by a documented architecture decision and now change only in a major version, and its release candidate produced no bug reports. A public six-day reference deployment processed 775,045 paid orders with the shop and the consumer reconciling to the cent across 25 leader failovers. Cost is mostly engineering time to run and monitor one more background worker; benefit is removal of a recurring class of data-integrity incidents and the custom reconciliation code we'd otherwise maintain. Recommendation: approve adoption on ^1.0 and schedule the relay, schema and monitoring work this quarter.
To engineering: v1.0.0 is functionally identical to 1.0.0-rc1 — if you already evaluated the RC, this is a version bump, not a migration. Per ADR 0008, the PHP API, outbox table, relay settings, lock key, message layout, partitioning, metrics and endpoints now change only in a major version. Changes in this release are limited to relay dependencies (franz-go 1.22.2, prometheus/client_golang 1.25.0) and image tagging: the relay image now also publishes a moving 1 tag for the latest 1.x, and the Kubernetes base uses 1.0 — decide explicitly whether we follow 1 for patch uptake or pin an exact digest for change control. Action items: move the RC branch to 1.0.0, run our highest-volume event flow through it, verify our partitioning and multi-instance locking assumptions against our own traffic (the demo's 25 leader kills are a strong signal but not our workload), and wire the provided metrics/endpoints into staging dashboards. Plan consumer-side idempotency regardless: the outbox gives at-least-once, not exactly-once.