Five .NET microservices run a real e-commerce order flow — and the demo’s closing act is a
button that kills the payment service in production, so you can watch the order wait in a
durable queue and complete the moment the service revives. Nothing simulated: the claim
“no message is ever lost” is something you verify yourself, in the browser.
Place an order — POST /orders returns 202 Accepted: placement is asynchronous,
and the order row itself is the tracking record.
Watch the saga — the order page draws the live saga canvas: the real RabbitMQ
topology (three fanout exchanges, seven consumer queues, production names), with your
order’s events replayed hop by hop. Placed → Paid → Shipped in about nine seconds,
each hop captioned with the pattern it demonstrates.
Break it — kill the payment consumer, place another order, watch it hold at Placed
while OrderPlacedEvent sits in the durable payment-orders queue, then revive and
watch the saga drain through.
The architecture underneath
Choreography saga, no orchestrator. Order, Payment, and Shipping each react to events
and publish their own; nobody coordinates. The failure branch (payment declined) rides the
exact same mechanics as the happy path.
Transactional outbox. Every entity write and its event commit in one database
transaction — “order saved but event lost” is structurally impossible, even with the
broker down.
At-least-once delivery, idempotent consumers. Exactly-once delivery is impossible in
a distributed system; the system achieves exactly-once processing by pushing every
failure mode toward duplication and making duplication a no-op. On the first night in
production, RabbitMQ genuinely redelivered an envelope — and the durable inbox rejected
the duplicate, exactly as the tests promised.
Two database engines on purpose. Postgres (Catalog, Shipping) and SQL Server
(Order, Payment) — exercising both EF Core providers’ concurrency tokens (xmin vs
rowversion) and both of Wolverine’s outbox stores.
CQRS without ceremony. Read handlers project to DTOs inside the SQL query, with the
ownership check in the WHERE clause — a non-owner’s row never leaves the database, and
the API returns 404, not 403 (403 would leak existence).
Engineering beyond the code
The docs are load-bearing. A guided tour
follows one order through the codebase, linking every claim to the source and the test
that proves it. An as-built deployment doc
includes an honesty table: every deliberately demo-grade choice, next to what production
would do instead.
A compounding rule system. Every non-obvious failure gets encoded — into the always-on
rules file, the AI code-review config, or a CI check — the same session it’s found. Stale
claims in docs fail the build via a “tombstone” audit; the deployment’s decision history
(Fly.io → Dokploy → plain compose) is recorded with dates and reasons, not rewritten.
Deploys are boring. Merge to main → GitHub Actions builds six images → the server
pulls and rolls them within three minutes. Bot protection (Cloudflare Turnstile,
fail-closed) guards the endpoints that cost money to abuse, because the demo credentials
are deliberately public.