Skip to content

Architecture Overview

The portfolio website is a cloud-native application deployed on Microsoft Azure. It consists of a React frontend, a FastAPI backend, and a MkDocs documentation site — all managed through Infrastructure as Code (Bicep) and deployed via GitHub Actions CI/CD.

Full Architecture Diagram

The diagram below shows the complete system: GitHub CI/CD pipelines, Azure resource groups (prod + dev), Entra ID authentication, external services, and all data flows.

Diagram source

The source is at docs/assets/architecture.drawio. Rendered interactively in-browser via the mkdocs-drawio plugin — no CI export step required.

For focused views at different abstraction levels, see the Architecture index: C4 Context, C4 Container, Deployment, and Azure Cloud diagrams.

High-Level Overview (Mermaid)

graph TB subgraph "User Layer" Browser[Web Browser] end subgraph "Azure Cloud" subgraph "Container Apps Environment" Frontend[Frontend Container
React 19 + Vite] Backend[Backend Container
FastAPI] UIGen[UIGen Container
admin.sven-relijveld.com] CMS[CMS Container
Strapi v5] end subgraph "Static Web App" Docs[MkDocs Documentation] end subgraph "Shared Services" ACR[Container Registry] KeyVault[Key Vault] AppInsights[Application Insights] LogAnalytics[Log Analytics] PostgreSQL[PostgreSQL Flexible Server] end subgraph "Cost Management" CostMgmt[Azure Cost Management] end end subgraph "External Services" Gmail[Gmail SMTP] GitHub[GitHub Actions] end Browser -->|HTTPS| Frontend Browser -->|HTTPS| Docs Browser -->|HTTPS admin.*| UIGen UIGen -->|proxy /api/*| Backend Frontend -->|API Calls| Backend Frontend -->|CMS content /cms/api/*| CMS CMS -->|MI auth| PostgreSQL Backend -->|Send Email| Gmail Backend -->|Query data| PostgreSQL Frontend -.->|Logs| AppInsights Backend -.->|Logs| AppInsights Docs -.->|Logs| AppInsights ACR -->|Pull Images| Frontend ACR -->|Pull Images| Backend Backend -->|Get Secrets| KeyVault GitHub -->|cost-report weekly| CostMgmt

Components

Component Technology Hosting Purpose
Frontend React 19, TypeScript, Vite, Tailwind CSS, shadcn/ui Azure Container Apps Portfolio UI: home, about, projects, contact, CV download; VITE_DOCS_URL
Backend FastAPI, Python 3.11, Uvicorn, slowapi Azure Container Apps REST API: contact, CV download, projects/experience/skills, admin write API
nginx nginx 1.25 (sidecar in frontend container) Azure Container Apps Serves static assets, proxies /api/* to backend on port 8000
Documentation MkDocs Material Azure Static Web App Architecture docs, ADRs, API reference, planning, cost dashboard
PostgreSQL Azure Flexible Server (AAD-only auth) Azure Managed Service Stores projects, experience, skills data; MI-based connection via asyncpg
UIGen Node 20, @uigen-dev/cli + @uigen-dev/react Azure Container Apps Auto-generated API Explorer UI; admin.sven-relijveld.com; guest read-only + admin writes
CMS Strapi v5, Node 22 Azure Container Apps Headless CMS: project-page-v2 content, future blog/speaking/announcements. REST API at /cms/api/*. Admin at cms.sven-relijveld.com
Infrastructure Bicep (subscription-level) Azure Resource Manager All Azure resources declared as code
CI/CD GitHub Actions (11 workflows) GitHub Build, test, deploy via OIDC; weekly cost report; on-demand CV generation

Data Flow

sequenceDiagram participant User as Visitor participant DNS as Azure DNS participant FE as Frontend (nginx) participant BE as Backend (FastAPI) participant CMS as CMS (Strapi v5) participant KV as Key Vault participant Gmail as Gmail SMTP participant DB as PostgreSQL User->>DNS: www.sven-relijveld.com DNS->>FE: A record → Container App User->>FE: HTTPS request FE->>User: React SPA (static assets) User->>FE: /api/contact (POST) FE->>BE: nginx proxy → port 8000 BE->>KV: Read SMTP credentials (Managed Identity) KV-->>BE: email-address, email-password BE->>Gmail: SMTP:587/TLS — notification email BE->>Gmail: SMTP:587/TLS — confirmation email BE-->>FE: {success: true} FE-->>User: Success toast User->>FE: /api/projects (GET) FE->>BE: nginx proxy → port 8000 BE->>DB: SELECT projects (asyncpg / MI token) DB-->>BE: rows BE-->>FE: JSON array FE-->>User: Projects page Note over FE,DB: project-page-v2 content fetched from CMS User->>FE: /cms/api/project-pages (GET) FE->>+CMS: proxy → CMS Container (Strapi v5) CMS->>DB: SELECT content (MI token) DB-->>CMS: rows CMS-->>-FE: JSON content FE-->>User: Project page (v2)

Deployment Pipeline

flowchart LR PR["Pull Request"] --> Preview["deploy-pr-preview.yml\n(Build portfolio-frontend,\nportfolio-backend, portfolio-cms\n→ Dev ACR → Dev deploy)"] Preview --> E2E["Playwright E2E\n+ API tests"] PR --> Validate["validate-infrastructure.yml\n(Bicep lint + what-if)"] PR --> Tests["test.yml\n(api / unit / typecheck / links)"] Main["git push main"] --> InfraDev["deploy-infrastructure.yml\n(Bicep → dev)"] Main --> AppDev["deploy-application.yml\n(Docker build portfolio-frontend,\nportfolio-backend, portfolio-cms\n→ dev ACR → dev)"] Main --> DocsDev["deploy-docs.yml\n(MkDocs → dev SWA)"] Tag["git tag v*"] --> InfraProd["deploy-infrastructure.yml\n(Bicep → prod)"] Tag --> AppProd["deploy-application.yml\n(Docker build portfolio-frontend,\nportfolio-backend, portfolio-cms\n→ prod ACR → prod)"] InfraProd --> AppProd AppProd --> DocsProd["deploy-docs.yml\n(MkDocs → prod SWA)"] AppProd --> Smoke["smoke-test-prod.yml\n(Playwright E2E)"] Schedule["Weekly schedule"] --> Cost["cost-report.yml\n(Azure Cost Management → docs)"] Dispatch["workflow_dispatch"] --> CV["generate-cv.yml\n(Bun + pagedjs → PDF)"]

All deployments use OIDC federation — GitHub Actions exchanges a token with Microsoft Entra ID, so no long-lived credentials are stored as secrets.


Environments

Aspect Production Dev / Preview
Resource Group dna-prod-portfolio-westeurope-rg dna-dev-portfolio-westeurope-rg
Frontend URL dev.sven-relijveld.com
Docs URL docs.sven-relijveld.com dev-docs.sven-relijveld.com
Admin (UIGen) admin.sven-relijveld.com (public) admin.dev.sven-relijveld.com (EasyAuth)
CMS URL cms.sven-relijveld.com cms.dev.sven-relijveld.com
Min Replicas 1 (always on) 0 (scale-to-zero)
Max Replicas 5 3
Authentication Public (UIGen: guest/admin token) EasyAuth (Entra SSO) on all services
Image Tags <sha> from v* tag pr-<N>-<sha> (PR) or <sha> (main)
Deploy Trigger v* release tag only Pull request or push to main

Security

Layer Mechanism
Transport HTTPS everywhere — managed TLS certificates via Azure
Secrets Azure Key Vault — email credentials, Entra client secret, SWA token
Identity User-assigned Managed Identities (RBAC: Key Vault Secrets User, ACR Pull, PostgreSQL AAD admin)
CI/CD Auth OIDC federation — no stored passwords or service principal keys
Dev Access EasyAuth with Entra SSO — blocks unauthenticated access to all dev services (incl. UIGen)
UIGen Auth Scoped guest/admin tokens issued by /api/uigen-token; admin token gates write access
CMS Auth Strapi admin credentials (no EasyAuth); Managed Identity for Postgres auth
Rate Limiting slowapi on all API endpoints (5/min contact, 10/min CV, 60/min read)
CORS Configured per environment on FastAPI middleware

Scalability

  • Auto-scaling: Container Apps scale based on concurrent HTTP requests
  • Scaling rule: 100 concurrent requests per replica
  • Production: 1–5 replicas (always available)
  • Dev: 0–3 replicas (scale-to-zero for cost savings)
  • Consumption plan: Pay only for active usage (no reserved capacity)

Monitoring

  • Application Insights: Request metrics, dependency tracking, exception logging
  • Log Analytics: Centralized log aggregation (30-day retention)
  • Container probes: Backend liveness check on /health every 30 seconds
  • Smoke tests: Playwright E2E runs 3x/day on weekdays against production
  • Cost dashboard: Weekly cost-report.yml queries Azure Cost Management and renders cost-dashboard.md
  • Coverage reports: test.yml generates HTML coverage for both Python (pytest-cov) and TypeScript (Vitest v8)

See Also

Page history

Field Value
Last updated 2026-06-02

Changelog

Date PRs Summary
2026-03-10 #66 Initial architecture overview page created
2026-03-22 #90 Add draw.io architecture diagram; rename nav entry to Overview
2026-03-22 #93 Expand overview with C4 references, detailed component table, deployment pipeline flow, and environment comparison
2026-04-17 React 18 → React 19; add PostgreSQL to Mermaid diagram and components table; add cost-report.yml and generate-cv.yml to pipeline; add DB query to data flow; add MI for DB to security table; add cost dashboard and coverage report to monitoring
2026-04-18 #205 Add UIGen API Explorer to components table and local stack diagram; add to C4 container diagram
2026-04-18 #207 UIGen promoted to Azure Container App; admin.sven-relijveld.com; update Mermaid, components table, environments table, security table
2026-04-19 Update deployment pipeline diagram and environments table to reflect promotion model: main → dev, v* tag → prod
2026-06-02 #484, #485, #486, #497, #512, #516 Add Strapi CMS to all architecture sections: Mermaid diagram, components table, environments table, security table, data flow