Architecture Overview
Understanding how ReliaPulse works under the hood.
System Overview
┌─────────────────────────────────────────────────────────────────┐
│ Load Balancer │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Next.js │ │ Next.js │ │ Next.js │
│ App (1) │ │ App (2) │ │ App (n) │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
└───────────────┼───────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ PostgreSQL│ │ Redis │ │ Workers │
└───────────┘ └───────────┘ └───────────┘Core Components
Next.js Application
The main application handles:
- Dashboard UI (React Server Components)
- API endpoints (Route Handlers)
- Public status pages (Server-rendered)
- Authentication (NextAuth.js)
PostgreSQL Database
Primary data store for:
- Organizations and users
- Components and status
- Incidents and updates
- Configuration and settings
Redis
Used for:
- Background job queues (BullMQ)
- Session storage
- Rate limiting counters
- Caching (optional)
Background Workers
Separate processes that handle:
- Uptime monitoring (health checks)
- Metrics polling (external integrations)
- Notification delivery
- Data cleanup
Data Flow
Incident Creation
User creates incident
│
▼
API Route Handler
│
├─► PostgreSQL (save incident)
│
└─► Redis Queue (notification jobs)
│
▼
Worker Process
│
├─► Email/SMS/Webhook
│
└─► On-call AlertsHealth Check Flow
Scheduler (Worker)
│
▼
HTTP Request to Target
│
▼
Evaluate Conditions
│
├─► Success: Update component status
│
└─► Failure: Check threshold
│
├─► Below threshold: Mark failed
│
└─► Above threshold: Create incident
│
└─► Send notificationsMetrics Polling Flow
Scheduler (Worker)
│
▼
Query External API (Datadog, Prometheus, etc.)
│
▼
Parse Response
│
├─► Store data points
│
├─► Check thresholds
│
└─► Update component statusMulti-Tenancy
ReliaPulse uses a single-database multi-tenancy model:
Organization Isolation
- Every data model includes
organizationId - All queries filter by organization
- API routes validate organization access
- No cross-organization data access
Request Context
// Middleware extracts organization from session
const session = await auth();
const organizationId = session.user.organizationId;
// All database queries include organization filter
const components = await prisma.component.findMany({
where: { organizationId }
});Authentication
Dual Authentication System
Request
│
├─► Check Authorization header
│ │
│ └─► API Key found? ─► Validate key ─► Synthetic session
│
└─► No API key ─► Check session cookie ─► Session authSession Flow
- User logs in via NextAuth.js
- Session stored in JWT (or database)
- Cookie sent with each request
- Middleware validates session
API Key Flow
- Request includes
Authorization: Bearer sk_live_xxx - Key is hashed and looked up
- Permissions checked against required
- Synthetic session created for request
Scalability
Horizontal Scaling
- App servers: Stateless, scale behind load balancer
- Workers: Scale by adding more processes
- Database: Use read replicas for queries
- Redis: Cluster mode for high availability
Performance Optimizations
- Server components (reduced client JS)
- Database query optimization (indexes, batching)
- Response caching (where appropriate)
- Connection pooling (Prisma)
Deployment Architecture
Docker Compose (Development/Small Scale)
docker-compose.yml
├── app (Next.js)
├── worker (BullMQ workers)
├── db (PostgreSQL)
├── redis (Redis)
└── adminer (DB admin)Kubernetes (Production)
├── Deployments
│ ├── app (replicas: 3)
│ └── worker (replicas: 2)
├── Services
│ ├── app-service
│ └── worker-service (internal)
├── Ingress
│ └── main ingress
└── External
├── RDS/Cloud SQL
└── ElastiCache/Cloud MemorystoreRelated Documentation
- Tech Stack - Technologies used
- Database - Schema design
- Workers - Background processing
- Security - Security measures