7. Deployment View
7.1 Production Deployment (Azure)
7.2 Infrastructure Components
| Component | Azure Service | Purpose |
|---|---|---|
| Frontend hosting | Azure Static Web App | Serves React SPA. Global CDN distribution. |
| API hosting | Azure Container Apps | Serverless container hosting. Auto-scaling. Managed ingress. |
| Background workers | Azure Container Apps | Long-running jobs (BestEx batch, pooling, report generation). |
| Database | Azure SQL Managed Instance | Database-per-tenant. Enterprise SQL Server features. |
| Cache | Azure Cache for Redis | Session state, rate sheet cache, tenant config cache. |
| Message bus | Azure Service Bus | Async processing (batch BestEx, pool creation, notifications). |
| Monitoring | Application Insights + Log Analytics | OpenTelemetry-based. Traces, metrics, logs. |
| Container registry | Azure Container Registry | Docker image storage for API and worker containers. |
| Identity | TBD (Keycloak / Azure AD) | Authentication and authorization. See ADR-013. |
7.3 Local Development (Docker Compose)
All local services are accessed through a single Nginx reverse proxy at http://pssaas.powerseller.local/ (port 80), eliminating port sprawl. A hosts file entry maps pssaas.powerseller.local to 127.0.0.1. See ADR-016 for the rationale.
Docker Compose Profiles
Services are organized into profiles so only what's needed runs at any given time:
docker compose up # proxy + docs (default)
docker compose --profile dev up # + api, web, cache
docker compose --profile dev --profile full up # + identity, bus, mail
Docker Compose Services
| Service | Container | Image | Profile | Purpose |
|---|---|---|---|---|
proxy | pssaas-proxy | nginx:alpine | default | Reverse proxy — single entry point at http://pssaas.powerseller.local/ (port 80) |
docs | pssaas-docs | Custom Node.js | default | Docusaurus documentation site at /docs/ |
api | pssaas-api | mcr.microsoft.com/dotnet/sdk:8.0 | dev | PSSaaS API with hot reload at /api/ |
web | pssaas-web | node:20-alpine | dev | React dev server with HMR at / |
cache | pssaas-cache | redis:7-alpine | dev | Price cache (BestEx, rate sheets) |
identity | pssaas-identity | quay.io/keycloak/keycloak | full | Identity provider at /auth/ (TBD — ADR-013) |
bus | pssaas-bus | rabbitmq:3-management-alpine | full | Message bus for async operations |
mail | pssaas-mail | axllent/mailpit | full | Local email capture at /mail/ |
Key Design Decisions
- No local SQL Server. The API connects directly to Azure SQL MI. Only MWFI and test databases are there today. See ADR-005.
- No host port mappings on individual services. Only the proxy exposes port 8080. Services communicate internally via the Docker network.
- Unavailable routes return 502. When a profile's services aren't running, the proxy returns HTTP 502 gracefully — Nginx does not crash.
7.4 CI/CD Pipeline
Git Push → Azure DevOps Pipeline
├── Build: dotnet build + npm build
├── Test: dotnet test + npm test
├── Analyze: static analysis + security scan
├── Docker: build + push to ACR
└── Deploy:
├── Staging: deploy to staging Container App
├── Validate: smoke tests against staging
└── Production: promote to production Container App
7.5 Database Deployment
Each tenant database follows the same schema. Schema changes are applied via EF Core migrations, rolled out tenant-by-tenant with the ability to pause if issues are detected.
Schema Migration Pipeline:
1. EF Core migration generated
2. Migration reviewed and approved
3. Applied to internal test database
4. Applied to MWFI staging database
5. Applied to MWFI production database
6. Rolled out to remaining tenants