Event-driven order processing system (saga, idempotency, outbox, tracing) - #1
Closed
ak1oo3 wants to merge 2 commits into
Closed
Event-driven order processing system (saga, idempotency, outbox, tracing)#1ak1oo3 wants to merge 2 commits into
ak1oo3 wants to merge 2 commits into
Conversation
Five independent services (order, inventory, payment, shipping, notification) coordinate an order through RabbitMQ instead of direct calls. The order service owns the saga: it issues each command, consumes every reply, and on failure walks the completed steps in reverse issuing one compensating command at a time (a carrier failure after capture refunds before releasing stock). Delivery guarantees are handled explicitly rather than assumed: - Idempotent consumers. Every handler claims the envelope's event_id in a processed_events ledger inside the same transaction as its business write, turning at-least-once delivery into effectively-once processing. - Transactional outbox. Domain writes and outgoing messages commit together; a relay publishes afterwards, so a crash between the two cannot strand a saga. - Broker-side retries. A failed handler republishes into an exponential backoff delay tier (queue per tier, delay in the queue name so tuning does not collide with immutable queue arguments) and is parked in a dead-letter queue after max attempts. - Distributed tracing. W3C traceparent rides on the message headers with hand-built producer/consumer spans, so one order is one trace across all five services. Each service owns its own PostgreSQL database and role, so the only way state crosses a boundary is a message. Verified end to end: 19 unit tests, 29 acceptance assertions covering every saga path, 11 failure-injection drills (duplicate command, replayed reply, poison message), and a cross-service consistency audit. Under simultaneous chaos (25% payment declines, 25% carrier failures, 5% handler crashes, every message published twice) 200/200 orders reached a consistent terminal state with 1,922 duplicates dropped and no stranded payments or reservations. Batching publisher confirms in the outbox relay cut saga p50 latency from 9.3s to 1.9s and raised throughput 2.8x; see docs/benchmarks.md.
ak1oo3
force-pushed
the
claude/event-driven-order-processing-fcyb5l
branch
from
August 22, 2026 20:39
4338203 to
fa57bd1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
A simulated e-commerce backend where placing an order triggers a chain of async events across 5 independent services coordinated over RabbitMQ instead of one synchronous function call. The order service owns a saga: it issues each command, consumes every reply, and on failure walks the completed steps in reverse issuing compensating transactions one hop at a time.
The three problems it's built around
event_idin aprocessed_eventsledger in the same transaction as its business write, turning at-least-once delivery into effectively-once processing.Plus broker-side exponential-backoff retries with a dead-letter parking lot, per-service PostgreSQL databases and roles (the only cross-boundary state is a message), and OpenTelemetry tracing where one order renders as a single Jaeger trace across all five services.
Changes
packages/shared/— platform library: broker (publish-with-confirms, consume-with-retry/DLQ, hand-built producer/consumer spans), topology (exchanges + backoff delay tiers), transactional outbox + relay, idempotency claim, service bootstrap, OTel + Prometheus.services/{order,inventory,payment,shipping,notification}-service/— the five services, each with its own migrations and database.order-service/src/saga.jsis a pure, unit-tested state machine.infra/+docker-compose.yml+Dockerfile— RabbitMQ, PostgreSQL (one DB/role per service), Jaeger, Prometheus.scripts/— smoke, chaos, bench, reconcile, verify-tracing, dev runner.tests/— 19 unit tests (saga transitions, backoff tiers, validation).docs/— architecture (Mermaid sequence diagrams + state machine + retry topology), design decisions with trade-offs, measured benchmarks, repo summary..github/workflows/ci.yml— unit + end-to-end + chaos + reconcile suites.Verification (run against a live stack)
docs/benchmarks.md.Notes for the reviewer
mainas its base; the diff is the entire initial system.