Scalability Guide
Enterprise scalability patterns for global deployment
Current Stack (February 2026)
| Layer | Technology | Version |
|---|---|---|
| Frontend | Next.js (Azure Static Web Apps) | 15.5.12 |
| Backend | NestJS (Azure Container Apps) | 11.x |
| Database | MongoDB Atlas | 7.x |
| Storage | Azure Blob Storage | - |
| CDN | Azure Front Door (optional) | - |
| CI/CD | GitHub Actions, Node.js 22 | - |
Current Architecture
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Frontend │────▶│ Azure Container │────▶│ MongoDB │
│ (Static) │ │ Apps (Backend) │ │ Atlas │
└──────────────┘ └──────────────────┘ └──────────────┘
│
┌────────┴────────┐
▼ ▼
┌────────────┐ ┌────────────────┐
│ Azure Blob │ │ Azure CDN │
│ Storage │ │ (Optional) │
└────────────┘ └────────────────┘Horizontal Scaling
Container Apps Auto-scaling
Azure Container Apps supports automatic scaling:
bash
az containerapp update \
--name ctrl-audio-backend \
--resource-group Sonnance-WebApp \
--min-replicas 1 \
--max-replicas 10 \
--scale-rule-name http-rule \
--scale-rule-type http \
--scale-rule-http-concurrency 100Scaling Triggers
| Metric | Recommendation |
|---|---|
| HTTP Requests | Scale at 100 concurrent connections |
| Memory | Scale at 70% memory utilization |
| Queue Depth | For background jobs (future) |
Database Optimization
MongoDB Indexes
Ensure indexes exist for common queries:
javascript
// Artist Space queries
db.artistspaces.createIndex({ owner: 1 });
db.artistspaces.createIndex({ "collaborators.user": 1 });
// Track queries
db.tracks.createIndex({ project: 1, isDeleted: 1 });
db.tracks.createIndex({ owner: 1, createdAt: -1 });
// Search optimization
db.artistspaces.createIndex({ name: "text" });
db.projects.createIndex({ name: "text" });
db.tracks.createIndex({ name: "text" });Aggregation Pipeline Optimization
typescript
// Use $project early to reduce document size
const pipeline = [
{ $match: { owner: userId, isDeleted: false } },
{ $project: { name: 1, image: 1, updatedAt: 1 } }, // Early projection
{ $sort: { updatedAt: -1 } },
{ $limit: 50 }
];CDN for Global Distribution
Azure CDN Setup
Run the setup script:
bash
cd infrastructure
chmod +x setup-cdn.sh
./setup-cdn.shCDN Benefits
| Metric | Without CDN | With CDN |
|---|---|---|
| Latency (EU) | 50-100ms | 10-20ms |
| Latency (US) | 150-200ms | 20-30ms |
| Bandwidth costs | $0.087/GB | $0.081/GB |
| Cache hit ratio | 0% | 85-95% |
CDN URL Integration
typescript
// Environment variable
NEXT_PUBLIC_CDN_URL=https://sonnance-images.azureedge.net
// Replace storage URLs with CDN
const cdnUrl = imageUrl.replace(
'sonnancewebappstorage00.blob.core.windows.net',
process.env.CDN_HOSTNAME
);Mobile App API Considerations
Pagination
Always use cursor-based pagination for mobile:
typescript
// Cursor-based (recommended)
GET /tracks?cursor=507f1f77bcf86cd799439011&limit=20
// Response
{
items: [...],
nextCursor: "507f1f77bcf86cd799439012",
hasMore: true
}Response Compression
Enable gzip compression:
typescript
import * as compression from 'compression';
app.use(compression());Efficient Endpoints for Mobile
typescript
// ✅ Good - Single request with embedded data
GET /artist-space/:id/full
// Returns: space + projects + recent tracks
// ❌ Avoid - Multiple round trips
GET /artist-space/:id
GET /projects?spaceId=xxx
GET /tracks?projectId=xxxImage Optimization for Mobile
Return appropriate image variants:
typescript
// Mobile clients request smaller images
GET /artist/:id?imageSize=thumbnail
// Responsive image URLs in responses
{
image: {
thumbnail: "https://cdn/.../thumb.webp", // 150x150
medium: "https://cdn/.../medium.webp", // 600px
original: "https://cdn/.../original.webp"
}
}Caching Strategy
API Response Caching
typescript
// Redis caching (recommended for future)
import { CacheModule } from '@nestjs/cache-manager';
import * as redisStore from 'cache-manager-redis-store';
CacheModule.register({
store: redisStore,
host: 'localhost',
ttl: 300, // 5 minutes
});Cache Invalidation
Invalidate on mutations:
typescript
@Injectable()
export class ArtistService {
async update(id: string, dto: UpdateDto) {
await this.cacheManager.del(`artist:${id}`);
return this.repository.update(id, dto);
}
}Background Jobs
Current: @nestjs/schedule
typescript
// Trash cleanup runs periodically
@Cron('0 0 * * *') // Daily at midnight
async deleteOldItems() {
// Delete items older than 30 days
}Future: Dedicated Queue (BullMQ)
For heavy processing (audio transcoding, batch operations):
typescript
import { BullModule } from '@nestjs/bull';
@Module({
imports: [
BullModule.registerQueue({
name: 'audio-processing',
}),
],
})Monitoring & Observability
Azure Application Insights
typescript
import * as appInsights from 'applicationinsights';
appInsights.setup(process.env.APPINSIGHTS_INSTRUMENTATIONKEY)
.setAutoCollectRequests(true)
.setAutoCollectDependencies(true)
.start();Health Checks
typescript
import { HealthModule, HealthCheckService } from '@nestjs/terminus';
@Controller('health')
export class HealthController {
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.db.pingCheck('mongodb'),
() => this.storage.pingCheck('azure-blob'),
]);
}
}Scaling Roadmap
| Phase | Feature | Effort | Impact |
|---|---|---|---|
| 1 | Azure CDN | 1 hour | High |
| 2 | MongoDB indexes | 2 hours | High |
| 3 | Response compression | 30 min | Medium |
| 4 | Application Insights | 2 hours | Medium |
| 5 | Redis caching | 1 day | High |
| 6 | BullMQ for jobs | 2 days | Medium |
Last Updated: February 2026