Skip to content

Repository files navigation

GoCommerce - Containerized E-Commerce Backend

This repository contains a containerized e-commerce platform built as ASP.NET Core 8 microservices behind a single YARP API Gateway. The system uses Entity Framework Core, SQL Server, RabbitMQ, Docker Compose, and Podman Compose.

Project Architecture

The platform follows a database-per-service architecture. Client traffic enters through the API Gateway on port 5000, while the Blazor frontend is available on port 5005. Backend services and databases stay on the internal container network and are not exposed directly to the host.

  1. API Gateway (5000): YARP reverse proxy that routes client requests to the correct service and exposes an aggregated endpoint at GET /api/aggregate/orders/{orderId}.
  2. Blazor Frontend (5005): UI client that talks only to the API Gateway.
  3. Product Service (internal): Owns product catalog data and consumes OrderCreated events to reduce stock.
  4. Customer Service (internal): Owns customer records.
  5. Order Service (internal): Creates and cancels orders, validates product/customer existence, and publishes OrderCreated and OrderCancelled events.
  6. Shipping Service (internal): Owns shipment data, validates orders over HTTP, and consumes OrderCancelled events to cancel shipments.
  7. RabbitMQ (15672 management UI): Message broker for asynchronous event delivery.

Key Technical Decisions

  • API Gateway: YARP provides centralized routing for /api/products, /api/customers, /api/orders, and /api/shipments. It also exposes an aggregation endpoint that merges order, customer, and product data for a single client call.
  • Internal service isolation: In line with the course API Gateway guidance, backend services are reachable only through container networking. Clients and browsers do not call service containers directly.
  • DTO design: All services use request and response DTOs so EF Core entities remain internal implementation details.
  • Synchronous communication: Order creation validates customers and products over HTTP. Shipment creation validates the referenced order over HTTP.
  • Asynchronous communication: Order Service publishes OrderCreated and OrderCancelled events through RabbitMQ fanout exchanges. Product Service and Shipping Service consume those events and react independently.
  • Fail-fast event publishing: Order creation and cancellation no longer return success if RabbitMQ publication fails. The request is rolled back and the API returns 503 Service Unavailable, preventing silent inconsistency.
  • Automatic migrations: Each service applies EF Core migrations on startup with retry logic for database readiness.

Running the System

Prerequisites

  • Fedora/RHEL: podman and podman-compose
  • Other platforms: Docker Desktop and docker compose

Podman (Fedora) first-time setup

If Podman Desktop reports Socket not found: /run/user/1000/podman/podman.sock, enable the rootless Podman API socket:

systemctl --user enable --now podman.socket
systemctl --user status podman.socket

Pre-pull shared base images before the first podman-compose up --build. The six .NET services all share aspnet:8.0 / sdk:8.0, and podman-compose does not deduplicate pulls across services — without a pre-pull, each service independently re-downloads the same layers from the slow MCR CDN and stalls the build. Pulling once populates the local cache:

podman pull mcr.microsoft.com/dotnet/aspnet:8.0
podman pull mcr.microsoft.com/dotnet/sdk:8.0
podman pull mcr.microsoft.com/mssql/server:2022-latest
podman pull docker.io/library/rabbitmq:3-management

Intermittent Temporary failure in name resolution during pulls is DNS flapping on the host; simply re-run the pull and Podman will resume skipped layers.

Podman (Fedora) commands

podman-compose up --build

Docker commands

docker compose up --build

This starts 11 containers:

Stopping the System

podman-compose down
docker compose down

To remove volumes for a clean reset:

podman-compose down -v
docker compose down -v

Reclaiming disk from unused Podman artifacts

After iterating on builds, dangling images and build layers add up quickly. To reclaim space without touching running containers:

podman system df                 # show what's using disk
podman image prune -f            # remove dangling (untagged) images
podman builder prune -f          # remove old build cache layers
podman container prune -f        # remove stopped containers

For a full wipe of everything not currently in use (containers, networks, images, build cache), use:

podman system prune -a -f --volumes

Warning: --volumes deletes unused volumes, which wipes SQL Server data from stopped stacks. Skip that flag if you want to preserve database state across podman-compose down and up cycles.

What Is Exposed to the Host

  • http://localhost:5000 -> API Gateway
  • http://localhost:5005 -> Blazor frontend
  • http://localhost:15672 -> RabbitMQ management UI (guest / guest)

The Product, Customer, Order, Shipping, and SQL Server containers are intentionally internal-only.

Testing Through the Gateway

All API requests should go through http://localhost:5000.

  1. Create a product
curl -X POST http://localhost:5000/api/products \
  -H "Content-Type: application/json" \
  -d '{"name":"Widget","description":"A test widget","price":9.99,"stockQuantity":100}'
  1. Create a customer
curl -X POST http://localhost:5000/api/customers \
  -H "Content-Type: application/json" \
  -d '{"firstName":"John","lastName":"Doe","email":"john@example.com","address":"123 Main St"}'
  1. Create an order
curl -X POST http://localhost:5000/api/orders \
  -H "Content-Type: application/json" \
  -d '{"customerId":1,"items":[{"productId":1,"quantity":2}]}'

If publishing succeeds, Product Service consumes the OrderCreated event and decrements stock.

  1. Verify stock through the gateway
curl http://localhost:5000/api/products/1
  1. Create a shipment
curl -X POST http://localhost:5000/api/shipments \
  -H "Content-Type: application/json" \
  -d '{"orderId":1,"shippingAddress":"123 Main St"}'
  1. Use the aggregated endpoint
curl http://localhost:5000/api/aggregate/orders/1
  1. Cancel the order
curl -X DELETE http://localhost:5000/api/orders/1

If publishing succeeds, Shipping Service consumes the OrderCancelled event and cancels the shipment.

  1. Verify the shipment through the gateway
curl http://localhost:5000/api/shipments/1
  1. Test DTO validation
curl -X POST http://localhost:5000/api/customers \
  -H "Content-Type: application/json" \
  -d '{"firstName":"John"}'

This returns a 400 Bad Request with validation errors.

Operational Notes

  • API Gateway Swagger is available at http://localhost:5000 (served at the root) for the gateway-hosted aggregation endpoint.
  • Reverse-proxied YARP routes are configuration-driven, so they do not automatically appear in Swagger even though they are reachable through the gateway.
  • RabbitMQ management is available at http://localhost:15672 and is useful for checking exchanges, queues, and message flow during demos.
  • A 503 Service Unavailable from order creation or cancellation indicates the system intentionally rejected the request because the RabbitMQ publish step did not complete.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages