Skip to content

Application Architecture

Backend - .NET 8 Clean Architecture

backend/src/
├── ECommerce.API             # Controllers, Program.cs (DI wiring, middleware, health checks)
├── ECommerce.Application      # CQRS handlers (MediatR), FluentValidation, feature logic
├── ECommerce.Infrastructure    # EF Core (AppDbContext), repositories, external service clients
└── ECommerce.Domain            # Entities, domain logic, no framework dependencies
  • Pattern: Clean Architecture (Domain → Application → Infrastructure → API) + CQRS via MediatR.
  • Data access: EF Core 8 with the Pomelo MySQL provider (UseMySql, target server version 8.2.0 - see backend/src/ECommerce.Infrastructure/DependencyInjection.cs).
  • Auth: JWT (access token 60 min, refresh token 7 days, HMAC-SHA256), BCrypt password hashing.
  • Migrations: applied via a dedicated --migrate-only startup path in Program.cs, run as a Kubernetes pre-install/pre-upgrade Helm hook Job (migration-job.yaml) - never inline in the normal web-serving startup path. See Kubernetes → Helm.
  • Health endpoints: /health/live (process-up only, no dependency checks) and /health/ready//health (gates on AppDbContext.CanConnectAsync() and Redis, each with a bounded 3s timeout - see BoundedDbContextHealthCheck.cs). This is what scripts/verify-staging.ps1 reads for DB/Redis connectivity - see Operations → Verification.

Frontend - React 18 + Vite

  • Redux Toolkit for state, TanStack React Query for server-state, React Router v6, Tailwind CSS, Axios (with an interceptor that auto-refreshes the access token on 401).
  • Built into static assets by npm run build, served by nginx in the production Docker image - nginx also proxies /api/* to the backend (frontend/nginx.conf), so the frontend container is the single external entry point in every environment (Docker Compose today; the Kubernetes Ingress routes everything to the frontend Service for the same reason - see Kubernetes → Ingress).

How frontend and backend actually talk

The backend's Kubernetes Service is deliberately never exposed via Ingress - only the frontend's nginx reaches it (see helm/akshayabazaar/templates/backend-service.yaml's own comment: "Not exposed outside the cluster - the frontend's nginx proxies /api/ to this Service by name, and that is the only path to the backend"*). This mirrors docker-compose.yml, where the api service never publishes a host port either - only frontend does (80/443).

Database schema (high level)

Users, Categories (hierarchical), Products, ProductImages, Orders, OrderItems, Carts, CartItems, Reviews, Address, plus the marketplace tables added by PR #4 (Sellers, SellerProducts, SellerInventories, Warehouses, InventoryReservations, InventoryReservationLines, SellerOrders, SellerOrderLines) and the notification-system tables (device tokens, preferences, media assets).

Payment/inventory integrity (PR #4)

Multi-seller checkout reserves inventory (SellerInventory.Reserve) at Razorpay order-creation time, before any payment confirmation; a successful payment verification commits the reservation (CommitReservation - decrements both AvailableQuantity and ReservedQuantity); a failed/invalid payment verification releases it (Release - clears ReservedQuantity only, AvailableQuantity untouched). The Razorpay order amount is computed entirely server-side from the authenticated user's cart (CreateRazorpayOrderCommand takes only a customer ID - there is no client-suppliable amount field anywhere in that request). Regression-tested in backend/tests/ECommerce.Tests/Application/.