A deployed, full-stack, cloud-native core-banking demo: a Blazor WebAssembly console fronted by an ASP.NET Core BFF, CQRS with idempotent money movement, real Postgres persistence + distributed Redis cache, end-to-end observability, a hardened edge, a local Kubernetes tier, and Terraform IaC — running serverless on GCP Cloud Run (scale-to-zero) and portable to on-prem Windows / IIS. One binary, three front doors.
Open accounts, move money between them, view balances — with the correctness properties a payments backend actually needs:
- Idempotent mutations — every transfer/create carries an
X-Idempotency-Key; a retry (even on a different instance) replays the cached result instead of double-charging. - Boundary validation — FluentValidation on the command → RFC 7807 problem responses, before any handler runs.
- Concurrency-safe — transfers run in a DB transaction; account-number allocation retries on unique-constraint races.
- Distributed by default — idempotency keys + read-cache live in Redis, so horizontal scale and scale-to-zero are safe.
Browser (Blazor WASM SPA)
│ HTTPS
▼
nginx edge ── TLS 1.2/1.3 · HSTS · CSP · rate-limit · gzip · upstream LB
▼
ASP.NET Core BFF (YARP) ── serves the SPA, reverse-proxies /api (no token/URL in the browser)
▼
PesaCore API (CQRS / MediatR) ──► Neon Postgres (durable state)
│ └──► Upstash Redis (idempotency + cache-aside)
└─ OTLP ─► OpenTelemetry Collector ─► Jaeger (traces) + Prometheus (metrics) ─► Grafana
Recurring principle: a stateless, portable core; state, infrastructure, and hardening at the configurable edge. The vendor (Neon/Upstash, nginx/GCLB) sits behind an abstraction, so it's a deployment choice — not a code dependency.
The same PesaCore.dll runs under:
- Kestrel in a Linux container (Cloud Run / local Docker),
- IIS + ANCM on Windows Server 2022 on-prem (
web.config,IISProfile.pubxml, .NET 10 Hosting Bundle), - Azure App Service / Container Apps.
make up # API :8080 + Blazor WASM/BFF :8090
make obs-up # OpenTelemetry → Jaeger :16686 + Prometheus :9090 → Grafana :3000
make edge-up # hardened nginx edge → https://localhost:8443
make cluster-up # local k8s (k3d preferred / Kind fallback) + ingress-nginx + HPA
make test # 70 tests (xUnit + WebApplicationFactory)The app runs standalone with SQLite + in-memory cache when no Postgres/Redis is configured; set ConnectionStrings:Postgres / :Redis to switch (both behind EF Core / IDistributedCache).
./scripts/deploy.sh # Cloud Build (amd64) → Artifact Registry → Cloud Run; secrets from Secret ManagerInfrastructure is also captured as Terraform under infra/terraform/ — authored and cost-gated (the managed load balancer + WAF are behind a default-off flag, so idle spend stays ~$0).
| Layer | Tech |
|---|---|
| Runtime / API | .NET 10 · ASP.NET Core · C# · CQRS (MediatR) · FluentValidation · Polly · EF Core (+ Dapper) · AutoMapper · Serilog |
| Front end | Blazor WebAssembly SPA · ASP.NET Core BFF (YARP reverse proxy) · ReDoc / OpenAPI |
| Data | Neon Postgres (durable) · Upstash Redis (idempotency + cache) — serverless, scale-to-zero |
| Observability | OpenTelemetry → Collector → Jaeger (traces/APM) + Prometheus (metrics) → Grafana |
| Edge | nginx reverse-proxy / API gateway — TLS 1.2/1.3, HSTS, CSP, rate-limiting, gzip, upstream load-balancing |
| Orchestration | Docker (Linux + Windows/IIS) · Kubernetes (k3d / Kind) + ingress-nginx + HPA |
| Cloud / IaC | GCP Cloud Run · Cloud Build · Artifact Registry · Secret Manager · Terraform |
| Quality gates | xUnit · WebApplicationFactory · Husky.Net (pre-commit format + secret scan, pre-push build + test) |
MIT — see LICENSE.