A reference .NET application implementing an e-commerce platform using a cloud-native microservices architecture, built with .NET 10 and orchestrated by .NET Aspire.
Note
This project serves as a canonical example of building modern distributed systems with .NET, showcasing industry best practices for microservices, Domain-Driven Design, event-driven communication, and AI-powered features.
eShop demonstrates how to architect and build a production-grade e-commerce application composed of multiple interdependent microservices. The platform enables product catalog browsing with AI-powered search, shopping cart management, complex order workflows, OAuth 2.0 authentication, webhook integrations, and cross-platform mobile support — all orchestrated through .NET Aspire for seamless local development and cloud deployment.
The application uses an event-driven architecture where services communicate asynchronously through RabbitMQ integration events, ensuring loose coupling and independent scalability. Each microservice owns its data store (PostgreSQL databases), follows Domain-Driven Design principles where appropriate, and exposes well-defined API contracts through HTTP REST or gRPC protocols.
- Architecture
- Features
- Prerequisites
- Quick Start
- Deployment
- Usage
- Configuration
- Project Structure
- Testing
- Contributing
- License
The system follows a microservices architecture orchestrated by .NET Aspire, with services communicating through RabbitMQ integration events and exposing APIs via HTTP REST and gRPC. Each service owns its dedicated PostgreSQL database, and a YARP reverse proxy provides a unified API gateway for mobile clients.
The architecture separates concerns into distinct layers: client applications (Blazor web, .NET MAUI mobile), API services (Catalog, Basket, Ordering, Identity, Webhooks), background processors (OrderProcessor, PaymentProcessor), shared infrastructure (EventBus, ServiceDefaults), and backing services (PostgreSQL, Redis, RabbitMQ).
---
title: "eShop Microservices Architecture"
config:
theme: base
look: classic
layout: dagre
themeVariables:
fontSize: '16px'
flowchart:
htmlLabels: true
---
flowchart TB
accTitle: eShop Microservices Architecture
accDescr: Shows the complete eShop platform architecture with client apps, API services, background processors, and backing infrastructure orchestrated by .NET Aspire
%% ═══════════════════════════════════════════════════════════════════════════
%% AZURE / FLUENT ARCHITECTURE PATTERN v1.1
%% (Semantic + Structural + Font + Accessibility Governance)
%% ═══════════════════════════════════════════════════════════════════════════
%% PHASE 1 - FLUENT UI: All styling uses approved Fluent UI palette only
%% PHASE 2 - GROUPS: Every subgraph has semantic color via style directive
%% PHASE 3 - COMPONENTS: Every node has semantic classDef + icon prefix
%% PHASE 4 - ACCESSIBILITY: accTitle/accDescr present, WCAG AA contrast
%% PHASE 5 - STANDARD: Governance block present, classDefs centralized
%% ═══════════════════════════════════════════════════════════════════════════
subgraph clients["🖥️ Client Applications"]
direction LR
webapp("🌐 WebApp<br/>Blazor Server"):::core
hybrid("📱 HybridApp<br/>.NET MAUI"):::core
webhookclient("🔔 WebhookClient<br/>Blazor Server"):::core
end
subgraph gateway["🔀 API Gateway"]
yarp("🔌 YARP<br/>Mobile BFF"):::core
end
subgraph services["⚙️ API Services"]
direction LR
identity("🔐 Identity.API<br/>OAuth 2.0 / OIDC"):::warning
catalog("📦 Catalog.API<br/>REST + AI Search"):::core
basket("🛒 Basket.API<br/>gRPC"):::core
ordering("📋 Ordering.API<br/>REST + DDD"):::core
webhooks("🔗 Webhooks.API<br/>REST"):::core
end
subgraph processors["🔄 Background Processors"]
direction LR
orderproc("⚙️ OrderProcessor<br/>State Machine"):::success
paymentproc("💳 PaymentProcessor<br/>Payment Handler"):::success
end
subgraph infra["🏗️ Infrastructure"]
direction LR
postgres[("🗄️ PostgreSQL<br/>pgvector")]:::data
redis[("⚡ Redis<br/>Cache")]:::data
rabbitmq("📨 RabbitMQ<br/>Event Bus"):::external
end
webapp -->|"HTTP/gRPC"| catalog
webapp -->|"gRPC"| basket
webapp -->|"HTTP"| ordering
hybrid -->|"HTTP"| yarp
yarp -->|"routes"| catalog
yarp -->|"routes"| ordering
webhookclient -->|"HTTP"| webhooks
identity -->|"JWT tokens"| basket
identity -->|"JWT tokens"| ordering
identity -->|"JWT tokens"| webhooks
catalog -->|"queries"| postgres
ordering -->|"queries"| postgres
identity -->|"queries"| postgres
webhooks -->|"queries"| postgres
basket -->|"caches"| redis
catalog -->|"publishes"| rabbitmq
ordering -->|"publishes"| rabbitmq
basket -->|"subscribes"| rabbitmq
orderproc -->|"subscribes"| rabbitmq
paymentproc -->|"subscribes"| rabbitmq
webhooks -->|"subscribes"| rabbitmq
%% Centralized semantic classDefs
classDef core fill:#EFF6FC,stroke:#0078D4,stroke-width:2px,color:#323130
classDef success fill:#DFF6DD,stroke:#107C10,stroke-width:2px,color:#323130
classDef warning fill:#FFF4CE,stroke:#FFB900,stroke-width:2px,color:#323130
classDef data fill:#F0E6FA,stroke:#8764B8,stroke-width:2px,color:#323130
classDef external fill:#E0F7F7,stroke:#038387,stroke-width:2px,color:#323130
%% Subgraph styling (5 subgraphs = 5 style directives)
style clients fill:#F3F2F1,stroke:#8A8886,stroke-width:2px,color:#323130
style gateway fill:#F3F2F1,stroke:#8A8886,stroke-width:2px,color:#323130
style services fill:#F3F2F1,stroke:#8A8886,stroke-width:2px,color:#323130
style processors fill:#F3F2F1,stroke:#8A8886,stroke-width:2px,color:#323130
style infra fill:#F3F2F1,stroke:#8A8886,stroke-width:2px,color:#323130
Component Roles:
| Component | Role | Protocol |
|---|---|---|
| 🌐 WebApp | Primary e-commerce storefront for browsers | Blazor Server |
| 📱 HybridApp | Cross-platform mobile client (Android, iOS, macOS, Windows) | .NET MAUI |
| 🔌 YARP | Reverse proxy / mobile BFF aggregating API calls | HTTP |
| 🔐 Identity.API | OAuth 2.0 / OpenID Connect provider with user management | HTTP |
| 📦 Catalog.API | Product catalog with AI-powered vector search | HTTP REST |
| 🛒 Basket.API | Shopping cart management with Redis-backed caching | gRPC |
| 📋 Ordering.API | Order lifecycle management with DDD and state machine | HTTP REST |
| 🔗 Webhooks.API | Webhook registration and delivery for external integrations | HTTP REST |
| ⚙️ OrderProcessor | Background orchestrator for order state transitions | Event-driven |
| 💳 PaymentProcessor | Background service for payment processing | Event-driven |
| 🗄️ PostgreSQL | Relational data store with pgvector for AI embeddings | TCP |
| ⚡ Redis | In-memory cache for basket data | TCP |
| 📨 RabbitMQ | Message broker for asynchronous integration events | AMQP |
eShop provides a comprehensive set of e-commerce capabilities distributed across specialized microservices. Each service is independently deployable, owns its data, and communicates through well-defined contracts — enabling teams to develop, test, and scale services independently.
The platform combines traditional CRUD operations with advanced patterns including Domain-Driven Design, CQRS via MediatR, event sourcing through the outbox pattern, and AI-powered product search using vector embeddings — demonstrating how modern .NET applications can leverage cutting-edge technologies while maintaining clean architecture.
| Feature | Description | Service | Status |
|---|---|---|---|
| 🛍️ Product Catalog | Browse, search, and filter products by category and brand | Catalog.API | ✅ Stable |
| 🤖 AI-Powered Search | Semantic product search using vector embeddings (pgvector) | Catalog.API | ✅ Stable |
| 🛒 Shopping Cart | Add, update, and remove items with Redis-backed persistence | Basket.API | ✅ Stable |
| 📋 Order Management | Full order lifecycle with DDD aggregates and state machine | Ordering.API | ✅ Stable |
| 🔐 Authentication | OAuth 2.0 / OpenID Connect with multi-client support | Identity.API | ✅ Stable |
| 🔗 Webhooks | Register endpoints and receive order/product change notifications | Webhooks.API | ✅ Stable |
| 💳 Payment Processing | Configurable payment handler with success/failure event flows | PaymentProcessor | ✅ Stable |
| 📨 Event-Driven Integration | Asynchronous service communication via RabbitMQ | EventBus | ✅ Stable |
| 🌐 Blazor Web UI | Responsive server-rendered e-commerce storefront | WebApp | ✅ Stable |
| 📱 Cross-Platform Mobile | Native mobile app for Android, iOS, macOS, and Windows | HybridApp | ✅ Stable |
| 📊 Distributed Tracing | End-to-end observability with OpenTelemetry instrumentation | ServiceDefaults | ✅ Stable |
| 🔀 API Gateway | YARP reverse proxy providing unified mobile BFF endpoint | Mobile BFF | ✅ Stable |
Running eShop requires the .NET 10 SDK, a container runtime for infrastructure services, and .NET Aspire workload for orchestration. The application uses .NET Aspire to automatically provision and configure PostgreSQL, Redis, and RabbitMQ containers — no manual infrastructure setup is needed for local development.
All infrastructure dependencies (databases, cache, message broker) are managed by .NET Aspire through container orchestration, so you only need Docker Desktop (or a compatible container runtime) installed and running before launching the application.
| Prerequisite | Version | Purpose |
|---|---|---|
| 🛠️ .NET SDK | 10.0.100 or later |
Build and run .NET projects |
| 🐳 Docker Desktop | Latest stable | Container runtime for infrastructure services |
| ☁️ .NET Aspire Workload | 13.1.2 or later |
Orchestrate distributed services |
| 💻 Visual Studio 2022 | 17.12+ (optional) |
IDE with Aspire tooling support |
| 💻 VS Code | Latest (optional) | Lightweight editor with C# Dev Kit |
| 🔒 Duende IdentityServer | License required for production | OAuth 2.0 / OIDC provider (dev license included) |
Warning
Duende IdentityServer requires a commercial license for production use. A development and testing license is included in this project.
dotnet workload install aspireExpected output:
Successfully installed workload(s) aspire.
git clone https://github.com/Evilazaro/eShop.git
cd eShopVerify Docker is available:
docker infoExpected output:
Client: Docker Engine - Community
Version: ...
Server: Docker Engine - Community
...
dotnet run --project src/eShop.AppHostExpected output:
Building...
info: Aspire.Hosting.DistributedApplication[0]
Aspire version: 13.1.2
info: Aspire.Hosting.DistributedApplication[0]
Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
Now listening on: https://localhost:17225
info: Aspire.Hosting.DistributedApplication[0]
Login to the dashboard at: https://localhost:17225/login?t=<token>
| Endpoint | URL | Description |
|---|---|---|
| 🖥️ Aspire Dashboard | https://localhost:17225 |
Service orchestration dashboard |
| 🌐 Web Store | https://localhost:7298 |
Main e-commerce storefront |
| 🔐 Identity Server | https://localhost:5001 |
OAuth 2.0 login and registration |
Tip
The Aspire Dashboard provides a centralized view of all running services, their health status, logs, traces, and metrics. Open it first to verify all services are healthy before browsing the store.
eShop is designed for containerized deployment and can be published to any container-compatible environment. The application supports Azure Container Apps through .NET Aspire deployment tooling, Azure Container Registry for image hosting, and Azure Pipelines for CI/CD automation.
For production deployment, each microservice runs as an independent container with its own scaling configuration, health checks, and environment-specific settings injected through the Aspire orchestrator or container platform.
dotnet build eShop.Web.slnfExpected output:
Build succeeded.
0 Warning(s)
0 Error(s)
dotnet testExpected output:
Passed! - Failed: 0, Passed: X, Skipped: 0, Total: X
./build/acr-build/queue-all.ps1./build/multiarch-manifests/create-manifests.ps1The project includes an Azure Pipelines configuration in ci.yml that triggers on the main branch:
trigger:
branches:
include:
- mainThe pipeline uses the .NET SDK version specified in global.json, builds the eShop.Web.slnf solution filter, and runs all tests automatically.
The eShop platform exposes multiple API endpoints through its microservices, each documented via OpenAPI (Scalar UI). The web storefront provides a complete shopping experience including product browsing, cart management, checkout, and order tracking — all backed by the microservice APIs.
Each API service exposes a Scalar documentation UI at its root endpoint for interactive exploration.
curl https://localhost:5301/api/v1/catalog/items?pageSize=10&pageIndex=0Expected output:
{
"pageIndex": 0,
"pageSize": 10,
"count": 101,
"data": [
{
"id": 1,
"name": ".NET Bot Black Hoodie",
"price": 19.5,
"catalogBrand": "Other",
"catalogType": "T-Shirt"
}
]
}The Basket service uses gRPC. Interact through the WebApp UI or programmatically using a gRPC client with the protobuf contract defined in src/Basket.API/Proto/basket.proto.
curl -X POST https://localhost:5102/api/v1/orders \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"city": "Redmond",
"street": "One Microsoft Way",
"state": "WA",
"country": "US",
"zipCode": "98052",
"cardNumber": "4111111111111111",
"cardHolderName": "Test User",
"cardExpiration": "2027-12-31T00:00:00Z",
"cardSecurityNumber": "123",
"cardTypeId": 1
}'Orders follow a defined lifecycle managed by the OrderProcessor:
Submitted → AwaitingValidation → StockConfirmed → Paid → Shipped
↘ StockRejected → Cancelled
↘ PaymentFailed → Cancelled
curl -X POST https://localhost:5103/api/v1/webhooks \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-endpoint.com/webhook",
"token": "your-secret-token",
"type": "OrderStatus"
}'eShop uses a layered configuration model where .NET Aspire automatically injects connection strings, service endpoints, and environment variables into each microservice at startup. For local development, all infrastructure is auto-provisioned — no manual configuration is needed beyond optional AI integration settings.
In production environments, configuration flows through environment variables set by the container orchestrator, with sensitive values managed through secrets providers. Each service's appsettings.json provides sensible defaults that are overridden by environment-specific settings.
| Setting | Scope | Description | Default |
|---|---|---|---|
⚙️ ConnectionStrings__catalogdb |
Catalog.API | PostgreSQL connection for product catalog | Auto-injected by Aspire |
⚙️ ConnectionStrings__identitydb |
Identity.API | PostgreSQL connection for user data | Auto-injected by Aspire |
⚙️ ConnectionStrings__orderingdb |
Ordering.API | PostgreSQL connection for orders | Auto-injected by Aspire |
⚙️ ConnectionStrings__webhooksdb |
Webhooks.API | PostgreSQL connection for webhook registrations | Auto-injected by Aspire |
⚙️ ConnectionStrings__redis |
Basket.API | Redis connection for cart caching | Auto-injected by Aspire |
⚙️ ConnectionStrings__eventbus |
All services | RabbitMQ connection for integration events | Auto-injected by Aspire |
🔒 Identity__Url |
API services | Identity server base URL for JWT validation | Auto-injected by Aspire |
🔒 Identity__Audience |
API services | OAuth audience identifier per service | basket, orders, webhooks |
🤖 useOpenAI |
AppHost | Enable Azure OpenAI for AI search features | false |
🤖 useOllama |
AppHost | Enable local Ollama LLM for AI search | false |
To enable AI-powered semantic search in the product catalog, modify src/eShop.AppHost/Program.cs:
// Set to true to enable OpenAI integration
bool useOpenAI = true;
// Or use local Ollama instead
bool useOllama = true;Note
Azure OpenAI requires a valid Azure subscription and API key. Ollama runs locally and does not require cloud credentials. Only enable one AI provider at a time.
All services expose a health check endpoint:
curl https://localhost:5102/healthExpected output:
Healthy
eShop/
├── src/
│ ├── eShop.AppHost/ # .NET Aspire orchestrator
│ ├── eShop.ServiceDefaults/ # Shared service configuration
│ ├── Basket.API/ # Shopping cart service (gRPC)
│ ├── Catalog.API/ # Product catalog service (REST)
│ ├── Identity.API/ # OAuth 2.0 / OIDC provider
│ ├── Ordering.API/ # Order management service (REST + DDD)
│ ├── Ordering.Domain/ # Domain model (aggregates, events)
│ ├── Ordering.Infrastructure/ # EF Core repositories
│ ├── OrderProcessor/ # Order orchestration (background)
│ ├── PaymentProcessor/ # Payment handling (background)
│ ├── Webhooks.API/ # Webhook delivery service
│ ├── WebApp/ # Blazor Server storefront
│ ├── WebAppComponents/ # Shared Razor component library
│ ├── HybridApp/ # .NET MAUI mobile app
│ ├── ClientApp/ # Legacy MAUI client
│ ├── WebhookClient/ # Webhook receiver client
│ ├── EventBus/ # Event bus abstraction
│ ├── EventBusRabbitMQ/ # RabbitMQ implementation
│ ├── IntegrationEventLogEF/ # Outbox pattern for events
│ └── Shared/ # Cross-cutting utilities
├── tests/
│ ├── Basket.UnitTests/ # Basket service unit tests
│ ├── Ordering.UnitTests/ # Ordering domain unit tests
│ ├── Ordering.FunctionalTests/ # Ordering integration tests
│ ├── Catalog.FunctionalTests/ # Catalog integration tests
│ ├── ClientApp.UnitTests/ # MAUI client tests
│ └── WebApp.EmailValidation.UnitTests/
├── e2e/ # Playwright browser tests
├── build/ # Container build scripts
├── Directory.Build.props # Global MSBuild properties
├── Directory.Packages.props # Centralized NuGet versions
└── global.json # .NET SDK version pinning
The project includes unit tests, functional (integration) tests, and end-to-end browser tests:
# Run all tests
dotnet test
# Run specific test project
dotnet test tests/Ordering.UnitTests
# Run end-to-end tests with Playwright
npx playwright test| Test Suite | Type | Framework | Scope |
|---|---|---|---|
| 🧪 Basket.UnitTests | Unit | MSTest | Basket service logic and gRPC behavior |
| 🧪 Ordering.UnitTests | Unit | MSTest | Domain aggregates, state machine, and business rules |
| 🧪 ClientApp.UnitTests | Unit | MSTest | MAUI client view model logic |
| 🧪 WebApp.EmailValidation.UnitTests | Unit | MSTest | Email validation rules |
| 🔗 Catalog.FunctionalTests | Integration | MSTest | Catalog API endpoints with database |
| 🔗 Ordering.FunctionalTests | Integration | MSTest | Order flow with service integration |
| 🌐 e2e/ | End-to-End | Playwright | Browser automation for shopping workflows |
Contributions to eShop are welcome and encouraged. The project follows established .NET best practices and values architectural integrity, performance, and reliability. Whether you are fixing a typo, improving documentation, or proposing an architectural enhancement, all contributions help make this reference application better.
Before contributing, review the project's guiding principles: best practices alignment, selectivity in tools and libraries, architectural integrity, and measurable improvements for reliability and performance enhancements.
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Make your changes following the project's coding standards
- Add or update tests for your changes
- Submit a Pull Request
Tip
Look for issues tagged "help wanted" or "good first issue" to get started. For typos and small fixes, no separate issue is needed — open a Pull Request directly.
See CONTRIBUTING.md for detailed guidelines and CODE-OF-CONDUCT.md for community standards.
This project is licensed under the MIT License — see the LICENSE file for details.
Copyright (c) .NET Foundation and Contributors.