Demo storefront that shows how Aspire 13.4 lets independent teams ship services on their own cadence while one team composes them into a product. Inspired by dotnet/eshop.
| Repo | Team | Ships |
|---|---|---|
| eshop-catalog-api | Catalog | Container image on GHCR + EShop.Catalog.Hosting NuGet (GitHub Packages) |
| eshop-basket-api | Basket | Container image on GHCR + EShop.Basket.Hosting NuGet (GitHub Packages) |
| this repo | eShop (storefront) | Blazor web app + the Aspire AppHost that composes everything |
The AppHost never checks out the other teams' source. It consumes their published contracts:
var catalogApi = builder.AddCatalogApi("catalog-api", catalogDb) // ghcr.io image + wiring
.WithSeedData("../../seed/eshop-catalog.json"); // replace the team's sample data
var basketApi = builder.AddBasketApi("basket-api", redis);AddCatalogApi / AddBasketApi / WithSeedData come from the teams' hosting-integration packages — that's the cross-team API surface.
Prereqs: .NET 10 SDK, Docker (or Podman), Aspire CLI, GitHub CLI (gh auth login).
One-time setup — GitHub Packages needs auth even for public feeds, and credentials must live in your user-level NuGet config so IDE background restores work too:
gh auth refresh -h github.com -s read:packages
dotnet nuget add source https://nuget.pkg.github.com/lpichet/index.json \
--name github-lpichet --username $(gh api user --jq .login) \
--password $(gh auth token) --store-password-in-clear-text \
--configfile ~/.nuget/NuGet/NuGet.ConfigThen:
aspire run --project src/EShop.AppHost
# or: dotnet run --project src/EShop.AppHostThe dashboard opens with: PostgreSQL + Redis containers, the two team images pulled from GHCR, and the Blazor webapp from source. Browse the storefront, add items to the basket, then restart the webapp resource from the dashboard — the basket survives (it lives in the basket team's Redis).
- Independent repos, one composition — each team also has its own AppHost for the inner loop; this repo pulls their published images.
- Data seeding — the catalog API seeds its database from JSON on first start.
- Seed replacement as a contract —
WithSeedData()(from the catalog team's package) bind-mounts our JSON into their container. This storefront ships its own catalog (seed/eshop-catalog.json); compare with the catalog team's built-in sample data. - Integration tests — each team repo boots its own AppHost with real containers (
Aspire.Hosting.Testing). - End-to-end tests — tests/EShop.E2ETests boots this whole composition (team images included) and replaces the seed a third time with test-only data, proving the same extension serves dev, demo and test scenarios.
dotnet test # runs the e2e suite locally (same one-time setup as above)The AppHost floats on the teams' latest contracts (Version="1.0.*" + image tag latest) to keep the demo fresh. Pin exact versions for anything real — AddCatalogApi("catalog-api", catalogDb, tag: "1.0.42").