Skip to content

Repository files navigation

EShop Microservices

A .NET 8 e-commerce microservices solution. Catalog, Basket, Discount, and Ordering services are connected via Redis cache, gRPC, RabbitMQ (MassTransit), and a YARP API Gateway.


Table of Contents


Architecture Overview

                         ┌─────────────────────┐
                         │   Client / Swagger  │
                         └──────────┬──────────┘
                                    │
                         ┌──────────▼──────────┐
                         │  YARP API Gateway   │
                         │     :6005           │
                         └──────────┬──────────┘
              ┌─────────────────────┼─────────────────────┐
              │                     │                     │
     /catalog-service      /basket-service      /ordering-service
              │                     │                     │
     ┌────────▼────────┐   ┌────────▼────────┐   ┌────────▼────────┐
     │  Catalog.API    │   │   Basket.API    │   │  Ordering.API   │
     │     :6001       │   │     :6002       │   │     :6004       │
     └────────┬────────┘   └────────┬────────┘   └────────┬────────┘
              │                     │                     │
         PostgreSQL            ┌────┴────┐           SQL Server
         (catalogdb)           │         │            (orderdb)
                               │    gRPC │                 ▲
                               ▼         ▼                 │
                         PostgreSQL   Discount.Grpc    RabbitMQ
                         + Redis       :6003           (MassTransit)
                         (basketdb)   (SQLite)         messagebroker

Communication styles

Type Where When
REST (HTTP) Client ↔ Catalog / Basket / Ordering CRUD and queries
gRPC Basket → Discount Apply discounts when storing basket items
Async messaging Basket → RabbitMQ → Ordering Create orders after checkout
YARP proxy Client → Gateway → services Single entry point

Services

Service Type Port Data store Description
Catalog.API REST 6001 PostgreSQL (Marten) Product catalog
Basket.API REST 6002 PostgreSQL + Redis Shopping cart
Discount.Grpc gRPC 6003 SQLite Product-based discounts
Ordering.API REST 6004 SQL Server Order management (Clean Architecture)
YarpApiGateway Reverse Proxy 6005 API Gateway / routing

Infrastructure services (Docker)

Container Image Port Purpose
catalogdb postgres:16-alpine 5432 Catalog DB
basketdb postgres:16-alpine 5433 Basket DB
distributedcache redis:7-alpine 6379 Basket cache
orderdb mssql/server:2022 1433 Ordering DB
messagebroker rabbitmq:3-management 5672 / 15672 (UI) Event bus

Ports and Connections

Source Target Address / notes
Basket (local) Discount gRPC http://localhost:6003
Basket (Docker) Discount gRPC http://discount.grpc:8080
Basket / Ordering RabbitMQ amqp://localhost:5672 (local)
Gateway (local) Catalog / Basket / Ordering localhost:6001 / 6002 / 6004
Gateway (Docker) Services http://*.api:8080

Default credentials (development):

  • Postgres: postgres / postgres
  • SQL Server: sa / SwN12345678
  • RabbitMQ: guest / guest — UI: http://localhost:15672

Project Structure

EShopMicroservices/
├── docker-compose.yml
├── docker-compose.override.yml
├── global.json                          # SDK 8.x
└── src/
    ├── eshop-microservices.sln
    ├── BuildingBlocks/
    │   ├── BuildingBlocks.csproj        # CQRS, behaviors, exceptions, Carter, MediatR…
    │   ├── Behaviors/                   # ValidationBehavior, LoggingBehavior
    │   ├── CQRS/
    │   ├── Exceptions/
    │   ├── Pagination/
    │   └── BuildingBlocks.Messaging/    # MassTransit + BasketCheckoutEvent
    ├── ApiGateways/
    │   └── YarpApiGateway/              # Reverse proxy + rate limiting
    └── Services/
        ├── Catalog/
        │   └── Catalog.API/             # Vertical slice + Marten
        ├── Basket/
        │   └── Basket.API/              # Repository + Redis decorator + gRPC client
        ├── Discount/
        │   └── Discount.Grpc/           # gRPC server + EF Core SQLite
        └── Ordering/
            ├── Ordering.API/
            ├── Ordering.Application/    # CQRS + BasketCheckoutEventHandler
            ├── Ordering.Domain/         # Aggregates, value objects, domain events
            └── Ordering.Infrastructure/ # EF Core SQL Server

Patterns Used

Pattern Where
CQRS + MediatR Catalog, Basket, Ordering
Vertical Slice Catalog, Basket feature folders
Clean Architecture Ordering (Domain / Application / Infrastructure / API)
Repository Basket (IBasketRepository)
Decorator + Cache-aside CachedBasketRepository + Redis
gRPC Discount server, Basket client
Pub/Sub (async) Basket checkout → RabbitMQ → Ordering
API Gateway / Routing YARP
Rate limiting Gateway → ordering-service
Exception handling CustomExceptionHandler (BuildingBlocks)

Technology Stack

  • .NET 8 / ASP.NET Core
  • Carter — minimal API endpoint modules
  • MediatR — command/query pipeline
  • FluentValidation — request validation
  • Marten — PostgreSQL document DB (Catalog, Basket)
  • EF Core — SQLite (Discount), SQL Server (Ordering)
  • StackExchange.Redis — distributed cache
  • gRPC (Grpc.AspNetCore)
  • MassTransit + RabbitMQ
  • YARP (Yarp.ReverseProxy)
  • Mapster — object mapping
  • Swagger / OpenAPI
  • Docker Compose

Prerequisites

  • .NET 8 SDK (compatible with global.json)
  • Docker Desktop (databases, Redis, RabbitMQ, optional full stack)
  • (Optional) Visual Studio 2022 / Rider / VS Code + C# Dev Kit
dotnet --version   # should be 8.x
docker --version

Quick Start

1) Start infrastructure

cd EShopMicroservices
docker compose up -d catalogdb basketdb distributedcache orderdb messagebroker

2) Run services locally (separate terminals)

dotnet run --project src/Services/Catalog/Catalog.API
dotnet run --project src/Services/Basket/Basket.API
dotnet run --project src/Services/Discount/Discount.Grpc
dotnet run --project src/Services/Ordering/Ordering.API
dotnet run --project src/ApiGateways/YarpApiGateway

3) Swagger / Gateway

Service URL
Catalog Swagger http://localhost:6001/swagger
Basket Swagger http://localhost:6002/swagger
Ordering Swagger http://localhost:6004/swagger
API Gateway http://localhost:6005
RabbitMQ Management http://localhost:15672

Local Development

Build the solution:

dotnet build src/eshop-microservices.sln

You can keep only infrastructure in Docker and run APIs with dotnet watch run from the IDE.

Important connection strings (local)

Basket (appsettings.json):

"ConnectionStrings": {
  "Database": "Host=localhost;Port=5433;Database=BasketDb;Username=postgres;Password=postgres",
  "Redis": "localhost:6379"
},
"GrpcSettings": {
  "DiscountUrl": "http://localhost:6003"
},
"MessageBroker": {
  "Host": "amqp://localhost:5672",
  "UserName": "guest",
  "Password": "guest"
}

Discount: SQLite file Data Source=discountdb (created under the project folder).
Ordering: SQL Server localhost,1433 / OrderDb / sa / SwN12345678.


Docker Compose

Full stack:

docker compose up -d --build

Specific services only:

docker compose up -d basketdb distributedcache discount.grpc basket.api

Logs:

docker compose logs -f basket.api
docker compose logs -f ordering.api

Stop:

docker compose down
# Also remove volumes:
docker compose down -v

API Gateway (YARP)

Single entry point: http://localhost:6005

Gateway path Backend
/catalog-service/{**catch-all} Catalog.API
/basket-service/{**catch-all} Basket.API
/ordering-service/{**catch-all} Ordering.API

Path transform example:

GET http://localhost:6005/basket-service/basket/swn
  →  GET http://localhost:6002/basket/swn

The ordering route uses a fixed window rate limit: max 5 requests per 10 seconds.

Discount gRPC is not exposed through the gateway; Basket calls it directly over gRPC.


Service Details

Catalog.API

  • Marten document store + PostgreSQL
  • CQRS (Create / Get / Update / Delete products)
  • Seed data (development)

Basket.API

  • Get / Store / Delete / Checkout basket
  • BasketRepository (Marten) + CachedBasketRepository (Redis decorator, cache-aside)
  • Applies discounts via Discount gRPC on store
  • On checkout: BasketCheckoutEvent → RabbitMQ (MassTransit IPublishEndpoint)

Discount.Grpc

  • Proto: GetDiscount, CreateDiscount, UpdateDiscount, DeleteDiscount
  • EF Core + SQLite + migrations
  • Seed: IPhone X (−150), Samsung 10 (−100)
  • HTTP/2 only (requires a gRPC client; browser/curl HTTP/1.1 will fail)

Ordering (Clean Architecture)

  • Domain: Order aggregate, value objects, domain events
  • Application: Commands/Queries + BasketCheckoutEventHandler (IConsumer<>)
  • Infrastructure: EF Core SQL Server, interceptors, migrations
  • API: Carter endpoints + Swagger

When a checkout event arrives, CreateOrderCommand runs and the order is persisted to OrderDb.


Sample Requests

Store basket via gateway

curl -X POST http://localhost:6005/basket-service/basket \
  -H "Content-Type: application/json" \
  -d '{
    "cart": {
      "userName": "swn",
      "items": [
        {
          "quantity": 1,
          "color": "Black",
          "price": 950,
          "productId": "5334c996-8457-4cf0-815c-ed2b77c4ff61",
          "productName": "IPhone X"
        }
      ]
    }
  }'

Discount applies −150 for IPhone X → stored price becomes 800.

Get basket

curl http://localhost:6005/basket-service/basket/swn

Checkout (RabbitMQ → Ordering)

curl -X POST http://localhost:6005/basket-service/basket/checkout \
  -H "Content-Type: application/json" \
  -d '{
    "basketCheckoutDto": {
      "userName": "swn",
      "customerId": "58c0eec7-3df2-4d8e-9b8f-8f0f0f0f0f0f",
      "firstName": "John",
      "lastName": "Doe",
      "emailAddress": "john@test.com",
      "addressLine": "Street 1",
      "country": "TR",
      "state": "Istanbul",
      "zipCode": "34000",
      "cardName": "John Doe",
      "cardNumber": "1234567890123456",
      "expiration": "12/28",
      "cvv": "123",
      "paymentMethod": 1
    }
  }'

You should then see BasketCheckoutEvent / order creation in Ordering logs, and related queue activity in the RabbitMQ UI.

List orders

curl http://localhost:6005/ordering-service/orders

Checkout Flow

1. POST /basket/checkout
2. Basket: load cart, set TotalPrice
3. Basket: Publish(BasketCheckoutEvent)  →  RabbitMQ
4. Basket: delete cart, return 200
5. Ordering: BasketCheckoutEventHandler.Consume
6. Ordering: CreateOrderCommand → SQL Server

Basket does not wait for Ordering to be available (async / eventual consistency).


Health Checks

Service Endpoint
Catalog http://localhost:6001/health
Basket http://localhost:6002/health
Ordering http://localhost:6004/health

Basket health checks validate the PostgreSQL (NpgSql) connection.


Troubleshooting

Failed to connect to 127.0.0.1:5433

basketdb is not running:

docker compose up -d basketdb

Redis timeout (localhost:6379)

docker compose up -d distributedcache

gRPC / Discount connection error

Start Discount (:6003). Verify Basket GrpcSettings:DiscountUrl.

No order after checkout

  1. Is RabbitMQ up? http://localhost:15672
  2. Is Ordering.API running?
  3. Are MessageBroker host/user/password correct on both services?
  4. Any consumer errors in Ordering logs?

SQL Server (orderdb) fails to start

MSSQL_SA_PASSWORD must meet complexity rules (this project uses SwN12345678). On Apple Silicon, check SQL Server image compatibility.

YARP 502 / connection refused

Backend services (6001/6002/6004) must be running. In Development, gateway cluster addresses in appsettings.Development.json point to localhost ports.

Discount HTTP/1.x error

Kestrel is HTTP/2 only. Use a gRPC client (or BloomRPC / grpcurl) instead of a browser/curl; Basket already uses a gRPC client.


BuildingBlocks

Shared libraries:

  • BuildingBlocks — CQRS interfaces, MediatR behaviors, exception handler, pagination
  • BuildingBlocks.MessagingBasketCheckoutEvent, AddMessageBroker() (MassTransit + RabbitMQ)

Basket only publishes (AddMessageBroker(config)).
Ordering publishes and consumes (AddMessageBroker(config, Assembly) → registers IConsumer<>).


License

This repository is intended for learning / sample microservices architecture.
See the root LICENSE file.


Checklist

  • Docker infrastructure is up (catalogdb, basketdb, Redis, orderdb, RabbitMQ)
  • Catalog / Basket / Discount / Ordering / Gateway are running
  • Basket → Discount gRPC applies discounts
  • Checkout → RabbitMQ → Ordering creates an order
  • Gateway routes /catalog-service, /basket-service, /ordering-service work

About

.NET 8 tabanlı e-ticaret mikroservisleri çözümü. Catalog, Basket, Discount, Ordering servisleri; Redis cache, gRPC, RabbitMQ (MassTransit) ve YARP API Gateway ile birbirine bağlıdır.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages