Operations
Local development
Prerequisites
- .NET 10 SDK
- Docker
- Node.js (for CDK synth tests only)
Start all services
All services start with their own container. An nginx edge proxy on :8080 routes:
Developer tools
pgAdmin (optional, browser-based DB viewer):
Seed demo booking store:
Email/SMS in development
By default, Messaging uses logging transports (no real sends). To use real providers:
- Gmail: Create
.envwithEMAIL_PROVIDER=smtp,GMAIL_FROM,GMAIL_APP_PASSWORD - Twilio: Add
SMS_PROVIDER=twilio,TWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKEN,TWILIO_MESSAGING_SERVICE_SID
See docker-compose.yml for all environment variable placeholders.
Container images
All services use .NET 10 chiseled runtime images (mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled) — minimal, shell-less, non-root (USER $APP_UID).
All follow the same multi-stage pattern: restore → publish → chiseled runtime, EXPOSE 8080.
AWS infrastructure (CDK)
Infrastructure is defined in infra/SalonCloud.Infra/ as C# CDK stacks.
Stack architecture
Foundation stack details
Per-service stack pattern
Each service stack uses FargateWebService.Create() — a shared helper that provisions:
- ECS task definition (EC2/HOST networking in UAT, Fargate/AWSVPC in PROD)
- Target group + ALB listener rule (path-based routing)
- Auto-scaling (CPU tracking)
- Secrets Manager entries for connection strings and internal secrets
Environment differences (DeployEnv)
Service discovery
- Fargate (PROD): Services discover each other via Service Connect DNS (e.g.,
http://accounts:8080) - EC2 (UAT): Services use HOST networking with fixed port mapping, discover via
http://localhost:<port>— avoids Service Connect proxy overhead
CI/CD
CI (ci.yml)
- Trigger: push or PR to
main - Job:
build-testonubuntu-latest- Checkout
- Setup .NET 10
dotnet restoredotnet build --configuration Release --no-restoredotnet test --configuration Release --no-build(all tests including integration)
Deploy UAT (deploy-uat.yml)
- Triggers:
- Push a
uat/**tag (e.g.,uat/008) - Manual
workflow_dispatchwith service selection
- Push a
- Jobs:
- detect: Resolve/create tag, detect changed services via git diff, build JSON matrix
- Build & push: Build Docker images → push to ECR
- CDK deploy: Deploy Foundation + changed service stacks
Wake UAT (wake-uat.yml)
- Trigger: manual
workflow_dispatch - Purpose: UAT auto-scales to 0 during off-hours (Tue–Sun 03:00 UTC). This workflow restores
MinCapacity=1and desired count to 1 for all services
Configuration
Connection strings
All services read SALONCLOUD_DB environment variable for PostgreSQL connection. In AWS, connection strings are composed in Secrets Manager; password is sourced from the RDS secret.
Internal bridge secret
Inter-service HTTP calls use X-Internal-Secret header. The shared secret is in InternalBridgeSecret env var (local: dev-secret; cloud: Secrets Manager from FoundationStack).
Service-specific URLs
Each service is configured with URLs for its dependencies:
Email/SMS providers
Seed scripts
Run manually with psql — never against production.
UAT deploy helper
tools/uat-deploy/build-push.sh — convenience script for building and pushing images to ECR without full CDK deploy. See tools/uat-deploy/README.md.
More on testing in Testing.