Start here: open
index.htmlin any browser. That single page is the animated architecture guide, the demo-credentials sheet, the operations runbook and the interview tour, all in one self-contained document.
The backend platform for FreshCart: twelve bounded contexts, each in a deliberately different architectural style, behind a YARP gateway, hosted on Azure Kubernetes Service through Azure DevOps or GitHub Actions multi-stage pipelines. The Angular 20 signal-based storefront lives in a companion repository — amasen02/freshcart-web.
The platform ships with three pre-seeded accounts so you can sign in and walk through the
customer and back-office flows in under a minute. The
IdentityDataSeeder
creates them automatically on first boot when ASPNETCORE_ENVIRONMENT=Development. It refuses
to run in any other environment.
# 0. Clone
git clone https://github.com/amasen02/freshcart-backend.git && cd freshcart-backend
# 1. Backing services (SQL Server, Postgres, MySQL, MongoDB, Redis, RabbitMQ, Seq, Grafana, Prometheus)
docker compose -f deploy/docker/docker-compose.yaml up -d
# 2. Whole .NET stack via Aspire — boots every microservice + the gateway + seeds demo accounts
dotnet run --project src/AspireAppHost/FreshCart.AppHost
# 3. Customer storefront (separate repo) — ng serve proxies /api and /hubs to the gateway on 7100
git clone https://github.com/amasen02/freshcart-web.git
cd freshcart-web && npm install && npm start| Surface | URL | What it shows |
|---|---|---|
| Customer storefront | http://localhost:4200 | Catalog, basket, checkout, orders, support chat, real-time notifications |
| Aspire dashboard | http://localhost:15888 | Every service + live traces, metrics, logs |
| YARP API Gateway | https://localhost:7100 | Single public edge; cookie-to-JWT BFF exchange |
| Identity API (OpenAPI) | https://localhost:7101 | Sign-up, sign-in, refresh, MFA enrollment |
| Reporting API (OpenAPI) | https://localhost:7110 | Dashboards, invoices, Excel exports |
| Seq (logs) | http://localhost:5341 | Structured logs across every service |
| Grafana (metrics) | http://localhost:3000 | RED + USE dashboards (admin / freshcart_local_dev) |
| Prometheus | http://localhost:9090 | Raw metric store |
| RabbitMQ management | http://localhost:15672 | Queues + exchanges (freshcart / freshcart_local_dev) |
| Role | Password | What this account can do | |
|---|---|---|---|
| Customer | demo@freshcart.test |
Demo-P@ssw0rd-2026 |
Browse catalog, add to basket, check out, view orders, chat to support |
| SupportAgent | support@freshcart.test |
Support-P@ssw0rd-2026 |
Answer support chats; query the Reporting API for order context |
| Administrator | admin@freshcart.test |
Admin-P@ssw0rd-2026 |
Reporting API: KPI dashboards, invoices, inventory health, exports |
Production safety. The seeder is guarded by
IHostEnvironment.IsDevelopment()and the sign-up validator rejects any*.testemail — these accounts cannot exist in Staging or Production.
The admin SPA is a planned phase (see
CHANGELOG.md). Until it ships, theSupportAgentandAdministratorsurfaces are reached through the Reporting API and the support hub directly.
- Open http://localhost:4200 → sign in as
demo@freshcart.test. - Browse the catalog (30 seeded products across 8 categories), add three items to the basket.
- Check out — accept the default address. The Ordering saga drives stock reservation, payment capture, and delivery booking.
- A toast notification appears within ~1 second — that is the
NotificationHubSignalR push through the Redis backplane. - Open the support panel and start a chat; a
SupportAgentsession picks it up over the WebSocket hub. - Hit the Reporting API at https://localhost:7110 with the
admin@freshcart.testtoken → Sales overview reflects the test order in the GMV and order-count tiles. POST /invoicesfor the order → a QuestPDF-rendered PDF opens via a 15-minute Azure Blob SAS URL.GET /exports/sales-transactions.xlsx— ClosedXML Excel with frozen header and auto-width columns.
The candidate's CV lists eleven years of work across .NET, Angular, Azure, AWS, IdentityServer4, SignalR, RabbitMQ, microservices, DDD, CQRS, Event Sourcing, Saga, polyglot persistence, EF Core + Dapper hybrid, SonarQube, Prometheus + Grafana, AKS, EKS, Azure DevOps CI/CD.
A long list of skills is unfalsifiable. This repository is the falsifiable evidence for the backend — every claim anchored to running code, an ADR, and a walkthrough card. The Angular front-end evidence lives in the companion repo freshcart-web.
| # | Context | Architecture | Persistence | Status |
|---|---|---|---|---|
| 1 | Identity | Clean Architecture + ASP.NET Identity | Azure SQL | Built |
| 2 | Catalog | Vertical Slice + Carter + MediatR | PostgreSQL + Marten | Built |
| 3 | Pricing | gRPC service | SQLite (in-container) | Built |
| 4 | Basket | Vertical Slice + Outbox + Decorator | PostgreSQL + Redis HybridCache | Built |
| 5 | Ordering | Clean / Onion + DDD + Saga | SQL Server (EF Core writes, Dapper reads) | Built |
| 6 | Inventory | Layered (3-tier) + Dapper | SQL Server | Built |
| 7 | Payment | Clean + Event Sourcing | MongoDB events + SQL Server projection | Built |
| 8 | Delivery | Hexagonal (Ports & Adapters) | MongoDB (geo indexes) | Built |
| 9 | Notification | Pipes-and-filters + SignalR | MongoDB + Redis backplane | Built |
| 10 | CustomerSupport | Stateful WebSocket | MongoDB transcripts + Redis presence | Built |
| 11 | Reviews | Vertical Slice + Document DB | MongoDB | Built |
| 12 | Reporting | CQRS read model + QuestPDF | MySQL warehouse + Blob Storage | Built |
Edge and clients:
| Surface | Technology | Status |
|---|---|---|
| YARP API Gateway | .NET 10 + YARP; cookie-to-JWT BFF exchange | Built |
| AdminBackoffice | Modular monolith (IModule pattern) |
Planned |
| Customer SPA | Angular 20 standalone + signals + Bootstrap 5 | Built — separate repo: freshcart-web |
| Admin SPA | Angular 20 standalone + signals + Bootstrap 5 | Planned — will live in freshcart-web |
The browser clients live in the companion frontend repository amasen02/freshcart-web; this repository is the backend platform they reach through the gateway.
BuildingBlocks carries the cross-cutting libraries: BuildingBlocks (CQRS, behaviors,
exception handling, pagination, security), BuildingBlocks.Messaging (integration event
contracts, outbox, MassTransit wiring), BuildingBlocks.Observability, and ServiceDefaults.
The Reporting service is a first-class member of the platform:
- Executive KPI dashboards — GMV, net revenue, AOV, refund rate, customer LTV, delivery success rate, inventory health.
- PDF invoice generation via QuestPDF with gap-free per-year per-kind invoice numbering
(
INV-2026-000123,CR-2026-000018,PF-2026-000009). - Excel exports via ClosedXML (managed-only, no Office install).
- Daily scheduled reports dropped into Azure Blob Storage by a
BackgroundService. - Event projection pipeline —
OrderConfirmed,OrderRefundedintegration events consumed by MassTransit, UPSERT-ed into denormalised MySQL tables. Idempotent at every hop via the projection inbox.
See index.html → Reporting & Invoices tab for the full surface.
- Authentication. ASP.NET Identity + HttpOnly + Secure + SameSite=Strict cookies for the browser; JWT bearer for service-to-service. Anti-forgery double-submit on every state-changing endpoint.
- Authorisation. Per-endpoint policies (
Customer,SupportAgent,Administrator,BackOfficeUser); resource-based authorization handlers defeat BOLA. - Observability. OpenTelemetry → OTLP → Azure App Insights + Log Analytics; Azure Managed Prometheus + Managed Grafana. W3C TraceContext propagated through HTTP, gRPC, MassTransit, SignalR.
- Resilience.
AddStandardResilienceHandler()on every typed HttpClient (retry + circuit breaker + timeout + bulkhead). - Validation. FluentValidation dispatched by the MediatR
ValidationBehavior. - Errors. Single
CustomExceptionHandlermapping domain exceptions to RFC 7807ProblemDetailswithtraceId. - Outbox. Transactional outbox in Basket and Ordering;
OutboxPublisherbackground worker; consumers idempotent via inbox. - Security headers.
UseFreshCartSecurityHeaders()middleware applies CSP strict,X-Frame-Options=DENY,Referrer-Policy,Permissions-Policy, COOP/COEP/CORP. - SSRF defence.
OutboundUrlAllowListHandleron every typed HttpClient + AKS egress NetworkPolicy blocks169.254.169.254. - Crypto. Argon2id password hashing (replaces PBKDF2). Refresh-token reuse detection.
Full OWASP Top-10 2025 mapping in
docs/adr/ADR-0004-owasp-top-10-control-mapping.md.
The hard parts of a distributed system are the races. Each guarantee below is backed by a real-database concurrency test (Testcontainers spins up the actual engine and fires N parallel callers) rather than asserted in prose:
- Exactly-once read projections (Reporting). The idempotency record commits in the same transaction as the projection, so an at-least-once redelivery can never double-count an additive aggregate (refund totals, customer lifetime value). Proven by a redelivery test and a 12-way concurrent MySQL test. (REP-001)
- No oversubscribed delivery slots. Booking is a single conditional increment guarded by
BookedCount < Capacity; the loser of a race is redelivered and re-scheduled against the remaining slots. Proven by a 20-way concurrent MongoDB test. (DLV-001) - Gap-free, collision-free invoice numbers. Allocation is one atomic upsert
(
INSERT … ON DUPLICATE KEY UPDATE … LAST_INSERT_ID), never a read-then-write. Proven by a 25-way concurrent MySQL test yielding exactly{1..25}. (REP-002) - Single-use refresh tokens. Rotation is claimed with one conditional
UPDATE; a concurrent reuse is treated as a stolen-token replay and revokes the whole token family. Proven by an 8-way concurrent SQL Server test. (ID-COR-01) - Idempotent payment refunds. The refund carries an idempotency key recorded on the
PaymentRefundedevent, so a retried refund replays the recorded outcome instead of charging the customer twice. (ORD-002) - Durable event contracts. Outboxed events resolve by their version-independent type name, so a deployment that bumps an assembly version never dead-letters an in-flight event. (BB-002)
- No duplicate outbox publishes across replicas. Each drain claims its batch with an atomic
conditional update (EF Core
ExecuteUpdateon SQL Server, MartenPatchon PostgreSQL) and a crash-recovering lease, so two publisher replicas always claim disjoint sets. Proven by a concurrent two-drainer test per store. (BSK-01) - Delivery events never lost on a broker outage. The delivery document and its
DeliveryScheduled/DeliveryCompletedevent commit in one MongoDB transaction (a single-node replica set), then a claim-by-update outbox publisher delivers the event; a failed business write stages no event and a failed event write rolls the delivery back. Proven by replica-set atomicity, claim-disjointness and publish-lifecycle tests. (DLV-002/003) - No double charge from a payment dual write. The event append and a projection marker commit in one
MongoDB transaction; a background projector replays each stream to SQL idempotently, and the capture
idempotency / one-payment-per-order invariant lives in the event store (partial unique index on the
initiating event's
OrderId), not the asynchronously-projected read model. Proven by replica-set atomicity, source-of-truth-uniqueness and projector convergence tests. (PAY-003) - Product creation never duplicates a SKU or 500s on a race. The check-then-write is backed by a unique
index on
Sku; the writer that loses a concurrent race has its violation mapped to a 409 instead of a raw database error. Proven by a 15-way concurrent PostgreSQL/Marten test yielding one product and the rest conflicts. (CAT-001)
Every item traces to an audit finding; the fixes and their verification are recorded in
CHANGELOG.md, and the originating review is
docs/google-standards-audit-2026-06-23.md.
- Local. Docker Compose + .NET Aspire AppHost.
- Azure. Bicep modules under
infra/provision AKS, ACR, Azure SQL, Postgres Flexible Server, MySQL Flexible Server, Cosmos DB, Cache for Redis, Service Bus, Key Vault, App Configuration, Log Analytics, App Insights, Front Door + WAF, Storage, VNet + private endpoints. - CI/CD.
.github/workflows/runs per-service build + test on every push tomaster(green badges below), plus CodeQL security analysis, Dependabot, and Playwright E2E. The full multi-stage build → scan → sign → blue/green AKS deploy pipeline (OIDC federated identity, no long-lived secrets) ships inazure-pipelines/(Azure DevOps). - Helm. One chart per service in
deploy/helm/withvalues-dev.yaml,values-staging.yaml,values-prod.yaml. PodSecurity restricted, non-root, read-only rootfs, NetworkPolicy default-deny. - GitOps. Optional overlay under
gitops/for Flux / ArgoCD.
freshcart-backend/
├── index.html ← LANDING PAGE — open this first
├── README.md ← this file
├── ARCHITECTURE.md ← C4 narrative (C1 + C2 + C3)
├── CHANGELOG.md ← gradual version history (Keep-a-Changelog format)
├── docs/ ← ADRs · threat models · interview-tour cards · conventions
├── src/
│ ├── BuildingBlocks/ ← CQRS, Messaging, Observability, ServiceDefaults
│ ├── Services/ ← twelve bounded contexts (Identity … Reporting)
│ ├── ApiGateways/ ← YARP gateway + gateway tests
│ ├── ModularMonolith/ ← AdminBackoffice (planned)
│ └── AspireAppHost/ ← local orchestration
│ (frontend storefront lives in a separate repo → github.com/amasen02/freshcart-web)
├── deploy/
│ ├── docker/ ← docker-compose stack
│ ├── helm/ ← Helm charts
│ └── k8s/ ← raw manifests (teaching reference)
├── infra/ ← Bicep modules + env params
├── azure-pipelines/ ← Azure DevOps multi-stage YAML
├── .github/workflows/ ← GitHub Actions
├── gitops/ ← Flux / ArgoCD manifests
└── tests/
├── integration/ ← Testcontainers
├── contract/ ← Pact
├── load/ ← k6
└── e2e/ ← Playwright (3-browser + mobile matrix)
- Unit + integration. xUnit + FluentAssertions + NSubstitute + Testcontainers (real SQL Server / Postgres / MongoDB / Redis / RabbitMQ in Docker).
- End-to-end. Playwright — three browser projects (Chromium / Firefox / WebKit) +
mobile viewport. Tests under
tests/e2e/specs/drive the storefront from freshcart-web and cover the cookie sign-in flow and the full customer happy path; the admin reporting dashboard spec is skipped until the admin SPA ships. - Contract / load. Pact (provider verification) and k6 are planned phases
(see
CHANGELOG.md). - Static. CodeQL (security-extended) on every push to
master, plus adotnet list package --vulnerablescan in each service CI. SonarCloud and Trivy image scanning run in the Azure DevOps pipeline (azure-pipelines/).
Every service builds and runs its full test suite (unit + Testcontainers integration tests) on
each push to master, and CodeQL scans the C# code for security issues:
Open the index.html landing page for the animated architecture guide. The
deeper references are:
ARCHITECTURE.md— C4 narrative + cross-cutting map.docs/CONVENTIONS.md— naming, async, LINQ discipline.docs/adr/— architecture decision records.docs/interview-tour/— 90-second pitch per service.docs/threat-models/— STRIDE per bounded context.docs/REQUIREMENTS.md— per-service functional + non-functional requirements with a traceability matrix.docs/CLASS_DIAGRAMS.md— Mermaid class + sequence diagrams.docs/INTERNAL_ARCHITECTURE.md— per-service deep dive.CHANGELOG.md— gradual version history (Keep a Changelog format).SECURITY.md— security policy + control table.CONTRIBUTING.md— coding standards summary.
This is open source under the MIT licence — fork it, build on it, take it in your own
direction. To run it locally, clone and follow the quickstart above (dotnet run the Aspire
AppHost, or deploy/docker/docker-compose.yaml); the seeded demo accounts are in the table near the
top of this README.
CONTRIBUTING.md— build/test/PR workflow and the coding bar.docs/CONVENTIONS.md— the full coding standards.CODE_OF_CONDUCT.md— Contributor Covenant.SECURITY.md— report vulnerabilities privately, never as a public issue.- Use the issue templates for bug reports and feature/pattern proposals; green CI (build + tests) is required on every pull request.
This project is, and will remain, free and open source. As maintainer I commit to:
- A permissive licence, kept stable. MIT — use it commercially, fork it, build on it. No relicensing of accepted contributions.
- No CLA. Contributions are accepted under the MIT licence; you keep the copyright to your work.
- An honest history. Real, walkable commits tied to shippable increments — no fabricated activity and no rewritten releases.
- Best-effort, transparent triage. Issues and pull requests are read and answered; security
reports are acknowledged within 72 hours (see
SECURITY.md). - A welcoming community governed by the Code of Conduct.
- Reproducible builds. Green CI — build, tests, SonarCloud quality gate, and Trivy image scan — on every change.
MIT — see LICENSE. You are free to use, modify, and distribute this software,
including for commercial purposes, provided the copyright notice is retained.
Ama Senevirathne — Senior Software Engineer & Tech Lead. Eleven years on C# / ASP.NET Core / Angular / Azure / AWS.
- GitHub
- Email: amabandarasp@gmail.com