A mini e-commerce system built with .NET 8 microservices: a Catalog service, an Ordering service, a YARP reverse-proxy gateway, and RabbitMQ for async event-driven communication. Ships with Docker Compose for local runs and Kubernetes manifests for cluster deployment.
Backend developers who understand a monolith and want to see — hands-on — how services split apart, talk through a gateway, and stay decoupled with events.
- Splitting a system into independently deployable services
- API gateway routing with YARP (path-based routing to services)
- Event-driven communication with RabbitMQ (publish/subscribe, durable queues, manual ACKs)
- Keeping services decoupled: Ordering never calls Catalog directly
- Running the whole system with Docker Compose
- Deploying to Kubernetes with Deployments, Services, and a LoadBalancer
- .NET 8 SDK (for running/debugging outside containers) — or click Open in GitHub Codespaces above for a browser-based environment.
- Docker with Docker Compose
kubectl+ a cluster (optional, for the Kubernetes manifests)
git clone https://github.com/varunkonnojutech-lab/dotnet-microservices.git
cd dotnet-microservices
docker-compose up --buildThis starts:
| Service | Host port | Notes |
|---|---|---|
| Gateway | 5000 | Reverse proxy, routes /catalog, /orders |
| Catalog | 5001 | Product inventory API |
| Ordering | 5002 | Order placement API |
| RabbitMQ | 5672 | AMQP broker |
| RabbitMQ UI | 15672 | Management console (guest / guest) |
Open the RabbitMQ management UI at http://localhost:15672 — log in with guest / guest to watch the order-created queue fill as orders are placed.
┌─────────────────────────┐
│ Client │
└────────────┬────────────┘
│
┌────────────▼────────────┐
│ YARP Gateway │ :5000 (compose) / :80 (k8s)
│ /catalog/* → catalog │
│ /orders/* → ordering │
└─────┬───────────┬───────┘
│ │
┌───────────▼───┐ ┌───▼───────────┐
│ Catalog │ │ Ordering │
│ :5001 (GET) │ │ :5002 (POST) │
│ /products │ │ /orders │
└───────▲───────┘ └───────┬───────┘
│ │ publish
│ RabbitMQ ▼
│ (order-created) ┌─────────┐
└──────────────────│ queue │
consume └─────────┘
Event flow:
- A client POSTs an order to
Ordering(via the gateway). Orderingstores the order and publishes anOrderCreatedEvent(OrderId,ProductId,Quantity,OccurredAt) to the durableorder-createdqueue in RabbitMQ.Catalog's background consumer receives the event, deserializes it, and decrements the product's stock (floored at 0, with a warning logged if stock is insufficient), then ACKs the message.
The services stay decoupled: Ordering never calls Catalog directly.
List all products:
curl http://localhost:5000/catalog/productsPlace an order (publishes OrderCreatedEvent; watch stock drop on the product):
curl -X POST http://localhost:5000/orders \
-H "Content-Type: application/json" \
-d '{"productId": 1, "quantity": 2}'Verify the stock was decremented by the consumer:
curl http://localhost:5000/catalog/products/1kubectl apply -f k8s/The gateway is exposed via a LoadBalancer service on port 80. RabbitMQ is internal (ClusterIP); the services reach it at host rabbitmq via cluster DNS.
The deployment manifests use placeholder images (
ecommerce/catalog-service:latest, etc.). Build and push your images, then update theimage:fields before applying.
dotnet-microservices/
├── src/
│ ├── Contracts/ # Shared event contracts (OrderCreatedEvent)
│ ├── CatalogService/ # Product inventory API + RabbitMQ consumer
│ ├── OrderingService/ # Order placement API + RabbitMQ publisher
│ └── Gateway/ # YARP reverse proxy (/catalog/*, /orders/*)
├── k8s/ # Kubernetes Deployments + Services
├── docker-compose.yml # Local run: gateway, services, RabbitMQ
├── Microservices.sln
├── .devcontainer/ # One-click GitHub Codespaces setup
├── .github/workflows/ci.yml# CI: builds all services on push
└── LICENSE
Want the frontend to go with it? Continue with angular-dotnet-starter — a full-stack Angular 18 + .NET 8 app with JWT auth.
MIT — see LICENSE.