The MP Core sample: an online store you can run, read and take apart.
Three backends behind a gateway, built with MP Core.
Nothing is a mock-up: real tokens, real brokers, real databases, and twenty-two scenarios that prove it.
Run it · Why MP Core · Build with AI agents · Learning path · Architecture · The business · Variations · What building it taught · MP Frontend · MP ecosystem
One task was given to Claude Code and to Codex, each with MP Core's skill and nothing else. Both changed the same seven files, kept the architecture without being reminded, and said what they had not proved. Read the run, as it happened.
| Ten skills at the root, ten in every backend | for Claude Code (.claude/skills) and for Codex (.agents/skills), checked with both |
| What to type, for each skill | docs/building-with-ai-agents.md, section 5 |
| The task, both answers and both changes, unedited | docs/agent-runs/2026-09-27 |
| The rules an agent holds to in this repository | AGENTS.md, CLAUDE.md |
Most samples show the day everything works. A real backend is judged on the other days: the request that arrives twice, the eight checkouts of one basket at the same moment, the payment provider that is down, the message that is delivered again after a restart.
Storefront is built for those days. It is small enough to read in a day and complete enough to be true:
| You want to know | Storefront shows it | By running |
|---|---|---|
| Where does a business rule live, and what does a caller see when it is broken? | A rule is a named class, checked by the aggregate, answered under its own code, in the caller's language | S3, S13 |
| How does one module tell another, without sharing a transaction? | A message that commits with the change; the answer to the shopper is "accepted" | S1 |
| What happens when a request arrives twice? | It runs once, and the second answer is the first | S14 |
| What happens when eight requests arrive at once? | One order, one charge | S15, S16 |
| Who changed this price, and who tried and was refused? | An audit trail in the backend's own database, written in the commit of the change; a refused attempt is kept | S3 |
| Can a message be read in the caller's language, and changed without a release? | A key and its arguments become a text when it is shown; support edits a text while the backend runs | S13 |
| Where does a card token go, and where does it never go? | To Payments, and into no message, queue table or log line | S17 |
| How do two services work together with no shared code? | Each declares what it reads; a test holds the two together | S18 |
| How is a stream read by a service that was not there when it was written? | From its start, at its own pace, into a hypertable | S19 |
| What does a backend believe of the gateway in front of it? | The scheme and the address, if the gateway is trusted. Never who the caller is | S20 |
| If the gateway verifies tokens, may the backend stop? | No. The edge cannot know for which backend a token was issued, or what its holder may do | S21 |
| Backend | Shape | Transport | Messaging | Storage | Also |
|---|---|---|---|---|---|
commerce/ |
modular monolith, four modules | REST and gRPC | Kafka and RabbitMQ | PostgreSQL | hybrid cache (memory and Redis), business audit, request idempotency, inbox, stored translations |
fulfillment/ |
service | gRPC | RabbitMQ | PostgreSQL | business audit, inbox |
analytics/ |
service | REST | Kafka | TimescaleDB hypertable | memory cache, inbox |
Around them, each a real product in a container: Apache APISIX at the edge, Keycloak for identity, Apache Kafka and RabbitMQ, PostgreSQL, TimescaleDB and Redis, and OpenTelemetry with Jaeger, Prometheus and Grafana. Only the payment provider is simulated.
The three backends share no code. Each declares the messages it reads in its own project, and a test holds the copies together.
Four modules in one process and two services of their own. Each part owns its data and its rules, 55 in all. One part learns what another did from a message, and none writes another's data. The top of each column is the sample's code. The bottom is MP Core, and the same questions get the same answers in every part: how a rule is refused, when a message leaves, what happens the second time. docs/business.md lists every rule by its code and the class that holds it.
Every box is one transaction that changes one module. Between the boxes there is a message, and each message leaves with the change that caused it. The code of the whole journey is business: a rule, a decision, a name. What makes it safe is MP Core's, and it is the same in all three backends.
A shop is judged on its bad days, and the guarantees for those days are the same in every shop: a checkout sent twice, eight at once, a message delivered again, a provider that is down. Without a framework that holds them, each team builds them again, and has to prove them again: an outbox, a key store, an inbox, retries that drop the messages of the attempt that failed, an error model for two transports, an audit trail in the commit of the change.
In Storefront each of those is one line, or nothing at all, and the scenario in the last column proves it against the running system. The code Storefront wrote is its business: the rules, the decisions, the names.
How the code was counted. Lines of C# that are neither blank, nor comments, nor a brace alone;
migrations, bin and obj left out. Storefront: the Domain and Application projects and folders of the
three backends. MP Core: the src of its 28 runtime packages at 1.0.0; its tests are another 12,481
lines, 845 tests, run against PostgreSQL, TimescaleDB, Redis, RabbitMQ and Kafka. A count of lines says how much there is,
not what it guarantees; what it guarantees is in the column of scenarios.
| The sample's code | MP Core |
|---|---|
| Says that a price may not move by more than half in one change | Reports the broken rule as 422, under the rule's code, with the numbers that broke it, in English, Simplified Chinese, or Arabic; audits the refused attempt |
| Empties the basket and announces what it held | Saves both in one commit; releases the message only after it |
Marks the checkout endpoint RequireIdempotencyKey() |
Stores the key and the answer with the change; answers a repeat with the first answer |
| Declares which exception means "lost a race" | Retries with growing, random pauses; discards the messages of the attempt that failed |
| Names a role for an endpoint | Validates the token, refuses by default, names the actor in every audit record |
Raises OrderPaid |
Delivers it to Kafka after the commit, partitioned by the order; the reader's inbox stops a second delivery |
| Nothing | Traces that cross three services, metrics, logs without secrets, health that tells alive from ready |
You need the .NET SDK 10.0.400, Docker with about 8 GB of memory, curl, jq and openssl.
grpcurl is needed for the gRPC scenarios. docs/running.md has the details, the
addresses you can open, and the problems people meet.
git clone https://github.com/panahister/mpcore-storefront-sample.gitcd mpcore-storefront-samplescripts/up.shscripts/setup.shThen each backend in a terminal of its own:
scripts/run.sh commercescripts/run.sh fulfillmentscripts/run.sh analyticsAnd the twenty-two scenarios, against the running system:
scripts/scenarios.shEvery step prints what it expects and what it got. The script is a demonstration and an end-to-end test at the same time: it exits non-zero when an expectation fails.
Everything is on main, and everything on main runs on every change. What you choose is a switch:
| You want | Switch | Default |
|---|---|---|
| The edge to verify tokens with Keycloak, as a second wall | EDGE_AUTH=keycloak scripts/up.sh |
off |
| MP Core from nuget.org, or from a clone next to this repository | -p:MPCoreSource=NuGet or Local |
decided by what is there |
| A lighter run, without the observability stack | scripts/up.sh --no-observability |
with |
docs/variations.md says what each changes, what it does not, and which scenario proves it.
| You want to | Read |
|---|---|
| Follow a path through the code, one idea at a time | docs/learning-path.md |
| Build here, or on MP Core, with Claude Code or Codex | docs/building-with-ai-agents.md |
| Know the business: roles, rules, scenarios, API | docs/business.md |
| Know why the system is cut this way, and what each decision costs | docs/architecture.md |
| Find where a capability of MP Core is used | docs/mpcore-coverage.md |
| Know what building this taught, including what went wrong | docs/lessons.md |
| Choose a variation | docs/variations.md |
| See the whole platform, part by part | MP Core: reference architecture |
| Start a backend of your own | MP Core: getting started |
Each backend also carries the guide the MP Core template generated for it (README.md and docs/ inside
commerce/, fulfillment/ and analytics/). Those describe a generated backend in general; the documents
above describe this system.
| What | Command | Needs |
|---|---|---|
| Commerce: rules, handlers, validators, messages, architecture, the EF model | dotnet test commerce/Storefront.Commerce.Backend.sln |
nothing |
| Fulfillment | dotnet test fulfillment/Storefront.Fulfillment.Backend.sln |
nothing |
| Analytics | dotnet test analytics/Storefront.Analytics.Backend.sln |
nothing |
| The contracts between the three | dotnet test tests/Storefront.Contracts.Tests |
nothing |
| Every business scenario, end to end | scripts/scenarios.sh |
the dependencies and the three backends |
| All of it against packed MP Core packages | scripts/verify-against-packages.sh |
a clone of MP Core next to this repository |
The tests and the scenarios run on GitHub on every change to this repository. docs/lessons.md lists what they found: defects in MP Core, which were fixed in MP Core, and mistakes in this sample's own design, which are the ones a team is most likely to repeat.
On 2026-10-14, a Release build against the MP Core 1.0.0 packages from nuget.org, with an empty package cache, completed with zero warnings and zero errors; all 290 tests passed (Commerce 241, Fulfillment 19, Analytics 22, Contracts 8). See the running guide for the reproducible command and scope.
From nuget.org, version 1.0.0. When a clone of MP Core sits next to this repository (../mpcore), the
backends build against its source instead: a breakpoint in framework code works, and a change there is
picked up by the next build. Directory.Build.targets decides, and says so in
the build output. A build of MP Core's source needs the API baseline of its last published version: run
./eng/restore-api-baseline.sh in that clone once, and again when its baseline moves, or the build stops with
MPCORE0005 (MP Core's ADR-010, addendum of 2026-10-12). To choose for one build:
dotnet build commerce/Storefront.Commerce.Backend.sln -p:MPCoreSource=NuGetApache-2.0. See LICENSE and NOTICE. Storefront, DemoPay and the people in the scenarios are fictional. The names and marks of the products in the pictures belong to their owners and are used only to name those products.