Five .NET microservices run a real e-commerce order flow at shop.emeraldleaf.dev. Log in as buyer1 / buyer1 and run the demo in four steps. The panel here simulates the same topology under its real service and queue names, and acts out each step as you reach it.
POST /orders returns 202 Accepted: placement is asynchronous, and the order row itself is the tracking record.
Orders flow from the storefront through every service and out as shipped.
2 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.
Slowed to a third of its speed, so each hop along the bus can be followed.
3 Break it
The demo closes on a button that kills the payment service in production. Kill it and place another order: the order holds at Placed while OrderPlacedEvent sits in the durable payment-orders queue.
Payment is stopped. The count on it climbs as orders queue for it, and shipping stalls.
4 Revive it
Start the consumer again and the saga drains through: the order completes the moment the service is back. Nothing on the live site is simulated, so "no message is ever lost" is a claim you verify yourself, in the browser.
Payment is back. Its queue drains, every held order ships, and lost stays at 0.
Then take over: payment, shipping, notification and the broker are switches, and your first click hands the panel to you.
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, so “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).
Built for scale, shown on one box
The architecture is a large-scale distributed deployment: each service independently
deployable with its own database, a durable broker between them, horizontal scale-out per
service, no shared process. The public demo intentionally packs all of it into one Aspire
host, one repository, and one server so it can be developed, demonstrated, and broken on
purpose without a cloud bill. What you’re seeing is the architecture under demo
conditions, not the deployment shape it was designed for.
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.
The full method is written up here: five encoding surfaces, from a lean
always-on canon to CI gates that fail the build.
The tests run against real engines. Four Testcontainers integration slices (Postgres,
SQL Server, Redis) prove the outbox, the concurrency tokens, the saga handlers, and the
IDOR shape against actual databases, which is what makes the repository wrapper
unnecessary and lets handlers take the DbContext directly. Four analyzers run at build
time with warnings as errors, architecture tests hold the domain’s dependency rule without
a four-project split, and CodeRabbit re-checks a dozen path-scoped conventions on every
diff.
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.