High-Level Design (HLD) Patterns: Load Balancing, Sharding & CQRS
Master production High-Level Design (HLD): L4/L7 load balancing, consistent hashing with virtual nodes, horizontal database sharding, and CQRS.
Architectural Summary (AEO)
Production High-Level Design (HLD) scales systems horizontally by eliminating single points of failure and decoupling read/write bottlenecks. Core building blocks include L4/L7 load balancing, consistent hashing with virtual nodes for stateful caches, range/hash database sharding, and CQRS to scale complex analytics independently from transactional writes.
Reference System Flow
Clients ──► Anycast DNS / CDN ──► L7 ALB (TLS + WAF + Rate Limit)
│
┌───────────────────┴───────────────────┐
▼ ▼
Command API (Writes) Query API (Reads)
│ ▲
▼ │
Primary DB (Sharded) ──► Kafka CDC ──► Read Replica / Redis1. L4 vs L7 Load Balancing & Health Checks
Layer-4 (TCP/UDP) balancers route packets at wire speed without TLS termination, ideal for raw throughput. Layer-7 (HTTP/gRPC) balancers inspect paths, headers, and JWTs to route `/api/ai/*` to GPU pools and `/images/*` to CDN origins.
2. Consistent Hashing with Virtual Nodes
Modulo hashing (`hash(key) % N`) remaps nearly 100% of keys when a cache node is added or removed. Consistent hashing maps nodes and keys onto a `0..2^32-1` ring with 100–200 virtual nodes per physical host, limiting key movement to `1/N` during scaling.
3. Database Sharding & CQRS
When vertical scaling and read replicas saturate on writes, shard horizontally by `tenant_id` or `user_id`—never by monotonically increasing timestamps (which create write hotspots). Pair with CQRS (Command Query Responsibility Segregation) when complex reporting queries would otherwise lock OLTP tables.
Consistent Hashing Ring Lookup (TypeScript)
function getTargetNode(ringKeys, ringMap, keyHash) {
let left = 0;
let right = ringKeys.length - 1;
if (keyHash > ringKeys[right]) return ringMap.get(ringKeys[0]);
while (left < right) {
const mid = (left + right) >> 1;
if (ringKeys[mid] >= keyHash) right = mid;
else left = mid + 1;
}
return ringMap.get(ringKeys[left]);
}Architectural Trade-Offs
| Pattern | Solves | Added Complexity | When to Adopt |
|---|---|---|---|
| Read Replicas + Redis | Read-heavy DB CPU saturation | Low (eventual lag) | First step past single DB |
| Consistent Hashing | Cache-node churn storms | Low | Multi-node in-memory caches |
| Horizontal Sharding | Single-primary write ceiling | High (cross-shard joins) | Only after indexing & partitioning |
| CQRS + CDC | OLTP vs analytics contention | Moderate | Heavy dashboards + high writes |
Production Benchmarks & Field Observations
- Consistent hashing with 150 vnodes keeps key redistribution within ±4% of ideal `1/N` when adding a 5th cache node.
Related Commercial Services
Engineering FAQs
Should we shard our database from day one?
No. Start with a well-indexed single PostgreSQL primary, add connection pooling (PgBouncer), Redis caching, and read replicas first. Shard only when write IOPS or storage genuinely demands it.
What is the biggest risk with CQRS?
Eventual consistency lag between the write model and read projection—always design the UI to handle optimistic updates or show 'syncing' states gracefully.
