0% found this document useful (0 votes)
33 views41 pages

Java Backend HLD Concepts Guide

The document outlines 20 high-level design concepts for Java backend developers with 4+ years of experience, categorized into basic, intermediate, and advanced levels. It covers essential topics such as client-server architecture, monolithic vs microservices, REST API design, database design fundamentals, and layered architecture, providing definitions, advantages, disadvantages, real-world examples, and implementation strategies. The guide serves as a comprehensive resource for developers looking to enhance their understanding of backend design principles and practices.

Uploaded by

Tushar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views41 pages

Java Backend HLD Concepts Guide

The document outlines 20 high-level design concepts for Java backend developers with 4+ years of experience, categorized into basic, intermediate, and advanced levels. It covers essential topics such as client-server architecture, monolithic vs microservices, REST API design, database design fundamentals, and layered architecture, providing definitions, advantages, disadvantages, real-world examples, and implementation strategies. The guide serves as a comprehensive resource for developers looking to enhance their understanding of backend design principles and practices.

Uploaded by

Tushar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Top 20 High-Level Design Concepts

For Java Backend Developers (4+ Years Experience)

From Basic to Advanced

A Comprehensive Guide with Use Cases and Real-World Scenarios


Table of Contents

Level Concepts Page

Basic (1-5) Client-Server, Monolithic vs Microservices, REST API, Database Design,


3 Layered Architecture
Intermediate (6-12) Load Balancing, Caching, Message Queues, API Gateway, DB Replication,
8 Auth, CAP Theorem
Advanced (13-20) Event-Driven, Circuit Breaker, Rate Limiting, Distributed Transactions,
15 Service Discovery, etc.
BASIC LEVEL CONCEPTS (1-5)

1. Client-Server Architecture

Definition:
Client-Server architecture is a distributed computing model where client applications request services
and resources from server applications. The server processes these requests and sends back
responses.

Key Components:
• Client: User-facing application (web browser, mobile app) that initiates requests
• Server: Backend system that processes requests and manages resources
• Communication Protocol: HTTP/HTTPS, TCP/IP for data exchange

Stateless vs Stateful:
Stateless: Server doesn't retain client information between requests. Each request is independent.
Example: RESTful APIs where every request contains all necessary information (JWT token,
parameters).

Stateful: Server maintains session information. Example: Traditional web applications with session
cookies stored on server.

Real-World Example:
E-commerce Application:
• Client: User's mobile app or web browser
• Request: GET /api/products?category=electronics
• Server: Spring Boot application running on Tomcat
• Response: JSON list of products with images, prices, descriptions
• The server doesn't remember previous searches (stateless) unless explicitly stored

Java Implementation Snippet:


@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping
public ResponseEntity<List<Product>> getProducts(@RequestParam String
category) {
// Stateless - no session data stored
return [Link]([Link](category));
}
}

When to Use:
Use stateless for scalability (easy to add more servers). Use stateful when you need to maintain
complex user sessions (banking applications, multiplayer games).
2. Monolithic vs Microservices Architecture

Monolithic Architecture:
A single, unified codebase where all application components (UI, business logic, database access) are
tightly coupled and deployed as one unit.

Advantages:
• Simple to develop and deploy initially
• Easy to test (everything in one place)
• Better performance (no network latency between modules)
• Easier debugging and monitoring

Disadvantages:
• Difficult to scale specific features independently
• Single point of failure - entire app goes down if one module fails
• Large codebase becomes hard to maintain
• Technology lock-in (entire app uses same framework/language)

Microservices Architecture:
Application is divided into small, independent services that communicate over network. Each service
handles a specific business capability.

Advantages:
• Independent deployment and scaling
• Technology diversity (each service can use different tech stack)
• Fault isolation (one service failure doesn't crash entire system)
• Better organized teams (each team owns specific services)

Disadvantages:
• Complex infrastructure (API Gateway, Service Discovery needed)
• Network latency between services
• Distributed system challenges (debugging, monitoring, transactions)
• Higher operational overhead

Real-World Example - E-commerce Platform:


Monolithic: Single Spring Boot application containing all modules - User Management, Product
Catalog, Order Processing, Payment, Inventory. All deployed together as one WAR/JAR file.

Microservices:
• User Service ([Link]): Handles registration, authentication, profile
• Product Service (Java Spring Boot): Manages product catalog, search
• Order Service (Java Spring Boot): Processes orders, order history
• Payment Service (Python): Integrates with payment gateways
• Inventory Service (Go): Real-time stock management
• Each service has its own database (Database-per-service pattern)

Migration Strategy:
Don't rewrite everything at once! Use Strangler Fig Pattern:
1. Identify independent business capability (e.g., Payment)
2. Extract it as microservice
3. Route new requests to microservice, old requests to monolith
4. Gradually migrate remaining modules
5. Retire monolith when all features migrated

Decision Framework:
Choose Monolithic when:
• Small team (< 10 developers)
• Simple application with limited scope
• Startup/MVP stage
• Limited scaling requirements

Choose Microservices when:


• Large team (multiple teams working independently)
• Need to scale specific features independently
• Complex domain that can be divided into bounded contexts
• Need technology flexibility for different components
3. REST API Design Principles

Definition:
REST (Representational State Transfer) is an architectural style for designing networked applications.
It uses HTTP protocol and treats server objects as resources that can be created, read, updated, or
deleted.

Core Principles:
1. Client-Server Separation: Clear separation between UI and data storage
2. Statelessness: Each request contains all information needed
3. Cacheability: Responses must define if they can be cached
4. Uniform Interface: Consistent resource identification and manipulation
5. Layered System: Client can't tell if connected directly to server or intermediary

HTTP Methods and Their Usage:


• GET: Retrieve resource(s) - Safe and Idempotent
• POST: Create new resource - Not Idempotent
• PUT: Update entire resource - Idempotent
• PATCH: Partial update - Not necessarily Idempotent
• DELETE: Remove resource - Idempotent

Resource Naming Best Practices:


■ Good Examples:
• GET /api/users - List all users
• GET /api/users/123 - Get specific user
• POST /api/users - Create new user
• PUT /api/users/123 - Update user
• DELETE /api/users/123 - Delete user
• GET /api/users/123/orders - Get orders for user 123

■ Bad Examples:
• GET /api/getAllUsers (verb in URL)
• POST /api/user/create (verb in URL)
• GET /api/users?action=delete (using GET for deletion)

HTTP Status Codes:


2xx Success:
• 200 OK - Request succeeded
• 201 Created - Resource created successfully
• 204 No Content - Success but no response body

4xx Client Errors:


• 400 Bad Request - Invalid syntax
• 401 Unauthorized - Authentication required
• 403 Forbidden - Authenticated but not authorized
• 404 Not Found - Resource doesn't exist
• 409 Conflict - Resource conflict (e.g., duplicate email)

5xx Server Errors:


• 500 Internal Server Error - Generic server error
• 503 Service Unavailable - Server temporarily down

Real-World Example - Online Banking API:


GET /api/accounts/12345/transactions?from=2024-01-01&to=2024-12-31&limit=50
Request Headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Response (200 OK):
{
"accountId": "12345",
"transactions": [
{ "id": "t1", "amount": -50.00, "type": "debit", "date": "2024-02-10" },
{ "id": "t2", "amount": 2000.00, "type": "credit", "date": "2024-02-01" }
],
"pagination": { "total": 150, "page": 1, "limit": 50 }
}

Versioning Strategies:
1. URI Versioning: /api/v1/users, /api/v2/users
2. Header Versioning: Accept: application/[Link].v1+json
3. Query Parameter: /api/users?version=1

Recommendation: Use URI versioning for simplicity and discoverability.

Pagination and Filtering:


GET /api/products?category=electronics&minPrice=100&maxPrice=500&page=1&limit
=20&sort=price:desc
4. Database Design Fundamentals

Normalization:
Process of organizing data to reduce redundancy and improve data integrity.

1NF (First Normal Form):


• Each column contains atomic (indivisible) values
• Each column contains values of single type
• Each column has unique name
• Order doesn't matter

Example: Instead of storing 'phone_numbers' as '123,456,789', create separate rows or table.

2NF (Second Normal Form):


• Must be in 1NF
• No partial dependencies (non-key attributes depend on entire primary key)

Example: In (StudentID, CourseID) → (StudentName, CourseName), StudentName depends only on


StudentID, not the full composite key. Split into separate tables.

3NF (Third Normal Form):


• Must be in 2NF
• No transitive dependencies (non-key attributes don't depend on other non-key attributes)

Example: If Student table has (StudentID, DepartmentID, DepartmentName), DepartmentName


depends on DepartmentID, not StudentID. Move DepartmentName to Department table.

Primary Keys and Foreign Keys:


Primary Key: Unique identifier for each record. Can be:
• Natural Key (email, SSN) - exists in real world
• Surrogate Key (auto-increment ID, UUID) - artificially created

Recommendation: Use surrogate keys (BIGINT AUTO_INCREMENT or UUID) for flexibility.

Foreign Key: References primary key in another table to establish relationships.


• Ensures referential integrity
• CASCADE options: ON DELETE CASCADE, ON UPDATE CASCADE

Indexing:
Indexes speed up data retrieval but slow down writes (INSERT, UPDATE, DELETE).

Types of Indexes:
1. Single-Column Index: CREATE INDEX idx_email ON users(email);
2. Composite Index: CREATE INDEX idx_name_age ON users(last_name, first_name, age);
3. Unique Index: CREATE UNIQUE INDEX idx_username ON users(username);
When to Create Indexes:
• Columns used in WHERE clauses
• Columns used in JOIN conditions
• Columns used in ORDER BY
• Foreign key columns

When NOT to Index:


• Small tables (< 1000 rows)
• Columns with high cardinality (many duplicates)
• Columns frequently updated

SQL vs NoSQL:
Aspect SQL (MySQL, PostgreSQL) NoSQL (MongoDB, Cassandra)

Data Model Structured tables with relations Flexible schema (JSON documents, key-value)

Scalability Vertical scaling (bigger server) Horizontal scaling (more servers)

ACID Full ACID compliance Eventually consistent (some offer ACID)

Query Language Standard SQL Varies by database

Use Cases Banking, ERP, traditional apps Real-time analytics, IoT, social media

Schema Fixed schema Dynamic schema

Real-World Example - Social Media Application:


SQL (PostgreSQL): User profiles, friendships, transactions
users table: id, username, email, password_hash, created_at
friendships table: user_id, friend_id, status, created_at

NoSQL (MongoDB): Posts, comments, activity feeds


posts collection: { userId, content, images[], likes[], comments[], timestamp }

Why this hybrid approach? User data needs ACID guarantees, but posts need flexible schema and
horizontal scaling.
5. Layered Architecture Pattern

Definition:
Layered Architecture organizes code into horizontal layers where each layer has a specific
responsibility and communicates only with adjacent layers. This enforces separation of concerns.

Common Layers:
1. Presentation Layer (Controllers/API):
• Handles HTTP requests and responses
• Input validation and formatting
• Maps DTOs (Data Transfer Objects) to domain models
• Example: @RestController classes in Spring Boot

2. Business Logic Layer (Service):


• Core application logic and business rules
• Orchestrates workflows
• Validation of business rules
• Example: @Service classes

3. Data Access Layer (DAO/Repository):


• Database interactions
• CRUD operations
• Query execution
• Example: @Repository interfaces using JPA

4. Domain/Model Layer:
• Business entities
• Domain logic (if using Domain-Driven Design)
• Example: @Entity classes

Benefits:
• Clear separation of concerns
• Easier testing (can mock layers)
• Better maintainability
• Team can work on different layers independently
• Easier to change database or UI without affecting business logic

Java Spring Boot Example - Order Processing:


// 1. PRESENTATION LAYER
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired private OrderService orderService;
@PostMapping
public ResponseEntity<OrderDTO> createOrder(@RequestBody CreateOrderRequest
request) {
// Validate input
Order order = [Link]([Link](),
[Link]());
return [Link](201).body(toDTO(order));
}
}
// 2. BUSINESS LOGIC LAYER
@Service
public class OrderService {
@Autowired private OrderRepository orderRepo;
@Autowired private InventoryService inventoryService;
@Autowired private PaymentService paymentService;
@Transactional
public Order createOrder(Long userId, List<OrderItem> items) {
// Business logic: Check inventory, calculate total, process payment
[Link](items);
Order order = new Order(userId, items, calculateTotal(items));
[Link](order);
return [Link](order);
}
}
// 3. DATA ACCESS LAYER
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(Long userId);
List<Order> findByStatus(OrderStatus status);
}

Anti-Patterns to Avoid:
■ Skipping layers: Controller directly accessing Repository (bypassing Service layer)
■ Business logic in Controllers: Controllers should be thin, only handling HTTP concerns
■ Data access in Service: Don't write SQL queries in Service layer, use Repository
■ Circular dependencies: Service A depends on Service B, which depends on Service A

When to Use:
Layered architecture is best for traditional enterprise applications with clear business logic separation.
For simpler CRUD apps, it might be overkill. For complex domains, consider Domain-Driven Design
with hexagonal architecture.
INTERMEDIATE LEVEL CONCEPTS (6-12)

6. Load Balancing

Definition:
Load balancing distributes incoming network traffic across multiple servers to ensure no single server
becomes overwhelmed. This improves application availability, scalability, and fault tolerance.

Load Balancing Algorithms:


1. Round Robin: Requests distributed sequentially. Simple but doesn't consider server load.
Request 1 → Server A, Request 2 → Server B, Request 3 → Server C, Request 4 → Server A...

2. Least Connections: Routes to server with fewest active connections. Good for long-lived
connections.

3. Least Response Time: Routes to server with lowest latency and fewest connections.

4. IP Hash: Client's IP determines which server handles request. Ensures same client always hits
same server (useful for session affinity).

5. Weighted Round Robin: Servers with higher capacity get more requests.
Server A (weight 3), Server B (weight 1) → A gets 75% traffic, B gets 25%

Types of Load Balancers:


Layer 4 (Transport Layer):
• Routes based on IP address and TCP/UDP port
• Fast, low latency
• Cannot inspect HTTP content
• Example: AWS Network Load Balancer

Layer 7 (Application Layer):


• Routes based on HTTP headers, URLs, cookies
• Can do content-based routing (/api/users → Service A, /api/products → Service B)
• SSL termination
• Example: Nginx, HAProxy, AWS Application Load Balancer

Hardware vs Software Load Balancers:


Hardware: Dedicated physical appliances (F5, Citrix NetScaler). Expensive but high performance.

Software: Nginx, HAProxy, Envoy. Cost-effective, flexible, easy to scale. Most modern applications
use software load balancers.
Health Checks:
Load balancers continuously monitor server health:
• TCP health check: Can server accept connections?
• HTTP health check: GET /health returns 200 OK?
• Custom health checks: Database connectivity, downstream service availability

If server fails health check, load balancer stops routing traffic to it until it recovers.

Real-World Example - E-commerce Application:


Setup: 3 Spring Boot application servers behind Nginx load balancer
User Request Flow:
1. User visits [Link]
2. DNS resolves to Nginx load balancer IP
3. Nginx uses Least Connections algorithm
4. Routes to Server 2 (has fewest active connections)
5. Server 2 processes request and returns response
6. If Server 2 fails health check, traffic automatically routed to Server 1
or 3
Nginx Configuration:
upstream backend {
least_conn;
server [Link]:8080;
server [Link]:8080;
server [Link]:8080;
}
server {
location / {
proxy_pass [Link]
}
}

Session Persistence (Sticky Sessions):


Problem: User's session data stored on Server A, but next request goes to Server B → user appears
logged out.

Solutions:
1. IP-based stickiness: Route same IP to same server (breaks with NAT)
2. Cookie-based stickiness: Load balancer sets cookie identifying server
3. Shared session storage: Store sessions in Redis/database (recommended)
7. Caching Strategies

Why Caching?
Caching stores frequently accessed data in fast-access memory (RAM) to reduce:
• Database load (fewer queries)
• API response time (microseconds vs milliseconds)
• External API costs (fewer calls to paid services)

Example: Product catalog rarely changes but is viewed millions of times. Cache it!

Cache-Aside (Lazy Loading):


Application is responsible for loading data into cache.

Flow:
1. Application checks cache for data
2. Cache hit → Return cached data
3. Cache miss → Query database
4. Store result in cache
5. Return data

Pros: Only caches requested data (no wasted memory)


Cons: First request is slow (cache miss), need to handle cache invalidation

// Java Spring Boot + Redis Example


@Service
public class ProductService {
@Autowired private RedisTemplate<String, Product> redis;
@Autowired private ProductRepository repo;
public Product getProduct(Long id) {
String key = "product:" + id;
// 1. Try cache
Product cached = [Link]().get(key);
if (cached != null) return cached; // Cache hit
// 2. Cache miss - query database
Product product = [Link](id).orElseThrow();
// 3. Store in cache (TTL 1 hour)
[Link]().set(key, product, 1, [Link]);
return product;
}
}

Write-Through Cache:
Data written to cache AND database simultaneously.

Flow:
1. Application writes data
2. Cache updates immediately
3. Database updates immediately

Pros: Cache always consistent with database, no cache misses for writes
Cons: Write latency (both cache and DB must complete), unused data in cache
Write-Back (Write-Behind) Cache:
Data written to cache first, then asynchronously to database.

Flow:
1. Application writes to cache
2. Cache acknowledges immediately
3. Cache writes to database in background (batched)

Pros: Very fast writes, reduced database load


Cons: Risk of data loss if cache crashes before DB write, complex to implement

Cache Invalidation Strategies:


Hardest problem in computer science: 'There are only two hard things in Computer Science: cache
invalidation and naming things.'

1. Time-to-Live (TTL): Cache expires after X seconds


• Simple but may serve stale data
• Good for data that changes predictably

2. Event-based Invalidation: Delete cache when data changes


• Accurate but complex
• Example: When product price updated, delete cache entry

3. Cache Stampede Prevention: When popular cache expires, thousands of requests hit database
• Solution: Use distributed locks, only one thread updates cache

Caching Levels:
1. Browser Cache: Static assets (CSS, JS, images)
2. CDN Cache: Content Delivery Network caches closer to users
3. API Gateway Cache: Cache API responses at gateway level
4. Application Cache: In-memory cache (Redis, Memcached)
5. Database Cache: Query result cache, buffer pool

Redis vs Memcached:
Feature Redis Memcached

Data Structures Strings, Lists, Sets, Hashes, Sorted Sets Only key-value strings

Persistence Can save to disk (RDB, AOF) In-memory only

Replication Master-slave replication No built-in replication

Use Cases Session store, pub/sub, leaderboards Simple caching, high throughput

Performance ~80k ops/sec ~200k ops/sec

Real-World Example - News Website:


Scenario: Homepage displays trending articles, updated every 5 minutes

Without Cache:
• 10,000 requests/minute → 10,000 database queries
• Average response time: 200ms

With Redis Cache (5-minute TTL):


• First request: Database query + cache storage (200ms)
• Next 9,999 requests: Cache hit (2ms)
• Database queries: 1 per 5 minutes instead of 10,000/minute
• 99% faster response time, 99.99% less database load
8. Message Queues and Asynchronous Communication

Definition:
Message queues enable asynchronous communication between services. Producer sends messages
to queue, consumer processes them later. This decouples services and improves scalability.

Why Use Message Queues?


1. Decoupling: Producer doesn't need to know about consumer
2. Scalability: Add more consumers to handle load
3. Reliability: Messages persisted even if consumer is down
4. Peak Load Handling: Queue absorbs traffic spikes
5. Asynchronous Processing: User doesn't wait for slow operations

Producer-Consumer Pattern:
Flow:
1. Producer creates message with payload
2. Sends message to queue/topic
3. Message stored in queue
4. Consumer polls queue for messages
5. Consumer processes message
6. Consumer acknowledges (ACK) completion
7. Message removed from queue

Popular Message Brokers:


1. RabbitMQ:
• Traditional message broker with AMQP protocol
• Supports complex routing (exchanges, bindings)
• Good for request/reply patterns
• Throughput: ~50k messages/second

2. Apache Kafka:
• Distributed streaming platform
• High throughput (millions of messages/second)
• Permanent log storage (messages retained)
• Perfect for event streaming, log aggregation

3. AWS SQS (Simple Queue Service):


• Fully managed, no server maintenance
• Auto-scaling
• Good for simple queuing needs in AWS

Queue vs Topic (Pub/Sub):


Queue (Point-to-Point):
• One producer → One consumer
• Message consumed by only one consumer
• Example: Order processing queue

Topic (Publish/Subscribe):
• One producer → Multiple consumers
• Same message delivered to all subscribers
• Example: User signup → Email service, Analytics service, CRM service all receive notification

Real-World Example - E-commerce Order Processing:


Synchronous (Without Queue) - BAD:
1. User clicks 'Place Order'
2. API processes payment (2 seconds)
3. API sends confirmation email (1 second)
4. API updates inventory (1 second)
5. API notifies warehouse (1 second)
6. Response to user (Total: 5 seconds) ■

Asynchronous (With RabbitMQ) - GOOD:


1. User clicks 'Place Order'
2. API validates and creates order (200ms)
3. API publishes OrderCreated event to queue
4. Response to user (Total: 200ms) ■
5. Background workers process queue:
• Email Worker: Sends confirmation email
• Inventory Worker: Updates stock
• Warehouse Worker: Notifies fulfillment
User sees instant response, background processing happens asynchronously!

Spring Boot + RabbitMQ Example:


// Producer
@Service
public class OrderService {
@Autowired private RabbitTemplate rabbitTemplate;
public void createOrder(Order order) {
// Save order to database
[Link](order);
// Publish event to queue
OrderEvent event = new OrderEvent([Link](), [Link]());
[Link]("[Link]", "[Link]", event);
}
}
// Consumer
@Component
public class EmailConsumer {
@RabbitListener(queues = "[Link]")
public void handleOrderCreated(OrderEvent event) {
// Send confirmation email
[Link]([Link](),
[Link]());
}
}

Message Acknowledgment and Retry:


Auto-Acknowledgment: Message removed as soon as delivered (risky - lost if consumer crashes)

Manual Acknowledgment: Consumer sends ACK after successful processing


• If consumer crashes before ACK, message redelivered
• Ensures at-least-once delivery

Dead Letter Queue (DLQ):


If message fails after X retries, move to DLQ for manual inspection

When to Use:
■ Use when: Long-running tasks, sending emails, processing uploads, microservices communication
■ Avoid when: Need immediate response, simple CRUD operations, real-time user interactions
9. API Gateway Pattern

Definition:
API Gateway is a server that acts as a single entry point for all client requests in a microservices
architecture. It routes requests to appropriate backend services and handles cross-cutting concerns.

Core Responsibilities:
1. Request Routing: Routes /users → User Service, /products → Product Service
2. Authentication & Authorization: Validates JWT tokens before forwarding requests
3. Rate Limiting: Prevents API abuse (max 100 requests/minute per user)
4. Load Balancing: Distributes requests across multiple service instances
5. Request/Response Transformation: Adapts client format to backend format
6. Protocol Translation: HTTP → gRPC, REST → GraphQL
7. Caching: Cache responses to reduce backend load
8. Logging & Monitoring: Centralized request logging
9. SSL Termination: Handles HTTPS, backend services use HTTP

Popular API Gateway Solutions:


• Spring Cloud Gateway: Java-based, integrates with Spring ecosystem
• Netflix Zuul: Earlier Spring solution (now in maintenance mode)
• Kong: Open-source, plugin-based, high performance
• AWS API Gateway: Managed service, auto-scaling
• Nginx: High-performance reverse proxy with API gateway capabilities
• Apigee: Enterprise-grade (Google Cloud)

Backend for Frontend (BFF) Pattern:


Instead of one generic API Gateway, create specialized gateways for each client type:

• Mobile BFF: Optimized responses (smaller payloads, aggregated data)


• Web BFF: Full data, supports complex queries
• IoT BFF: Lightweight, supports MQTT protocol

Why? Mobile needs different data format than web. Mobile has bandwidth constraints.

Request Flow with API Gateway:


1. Mobile app requests: GET [Link]
2. API Gateway receives request
3. Gateway validates JWT token (authentication)
4. Gateway checks rate limit for user
5. Gateway makes parallel calls to:
• User Service: GET /users/123 (profile data)
• Order Service: GET /orders?userId=123 (recent orders)
• Recommendation Service: GET /recommendations/123 (personalized)
6. Gateway aggregates responses into single JSON
7. Gateway returns combined response to mobile app
Without Gateway: Mobile app makes 3 separate API calls (slower, more complex)

Spring Cloud Gateway Example:


[Link]:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE # Load balanced
predicates:
- Path=/api/users/**
filters:
- RewritePath=/api/users/(?<segment>.*), /$\{segment}
- AddRequestHeader=X-Gateway-Name, SpringCloudGateway
- id: product-service
uri: lb://PRODUCT-SERVICE
predicates:
- Path=/api/products/**

Real-World Example - Food Delivery App:


Mobile App → API Gateway → Backend Services

Request: GET /api/homepage

Gateway aggregates data from:


1. Restaurant Service: Nearby restaurants
2. Promotion Service: Active discounts
3. User Service: User preferences, addresses
4. Order Service: Recent orders

Gateway also handles:


• Authentication (JWT validation)
• Rate limiting (prevent abuse)
• Caching (homepage data cached 30 seconds)
• Request transformation (mobile gets compact JSON)
• Circuit breaking (if Restaurant Service down, return cached restaurants)

API Gateway Anti-Patterns:


■ Smart Gateway: Don't put business logic in gateway. Keep it thin.
■ Single Point of Failure: Always run multiple gateway instances
■ Tight Coupling: Gateway shouldn't know internal details of services
10. Database Replication and Sharding

Database Replication:
Replication creates copies of database on multiple servers for redundancy and read scalability.

Master-Slave Replication:
• Master (Primary): Handles all writes (INSERT, UPDATE, DELETE)
• Slaves (Replicas): Handle reads (SELECT)
• Data automatically replicated from master to slaves

Benefits:
• Read scalability (add more slaves for more read capacity)
• High availability (if master fails, promote slave to master)
• Backup without impacting production

Drawbacks:
• Replication lag (slaves may be slightly behind master)
• Write bottleneck (all writes still go to single master)
• Read-after-write consistency issues

Master-Master Replication:
• Multiple masters, all handle reads and writes
• Changes bi-directionally replicated

Benefits: Write scalability, no single point of failure


Drawbacks: Conflict resolution complexity (two masters update same row)

Real-World Example - Social Media Platform:


Setup: 1 Master + 3 Slave databases

Write Operation (Create Post):


POST /api/posts → Write to Master DB

Read Operation (View Feed):


GET /api/feed → Read from Slave DB (round-robin across 3 slaves)

Result: Write capacity 1x, Read capacity 3x


If 90% of traffic is reads (typical social media), this 3x improvement matters!

Database Sharding (Horizontal Partitioning):


Sharding splits data across multiple databases. Each shard is independent database with subset of
data.

Sharding Strategies:
1. Hash-Based Sharding:
• Hash user_id → Determines which shard
• user_id 12345 → hash(12345) mod 4 → Shard 1
• Pros: Even distribution
• Cons: Hard to add new shards (requires rehashing all data)

2. Range-Based Sharding:
• Shard 1: user_id 1-1,000,000
• Shard 2: user_id 1,000,001-2,000,000
• Pros: Simple, easy to add new shards
• Cons: Uneven distribution (hotspots if certain ranges popular)

3. Geography-Based Sharding:
• US Shard, EU Shard, Asia Shard
• Pros: Low latency (data closer to users), compliance (GDPR)
• Cons: Uneven distribution, cross-region queries difficult

Sharding Challenges:
• Cross-Shard Queries: Joining data across shards is slow
• Distributed Transactions: ACID across multiple shards is complex
• Hotspot Shards: Popular data concentrated in one shard
• Resharding: Adding/removing shards requires data migration

Real-World Example - Instagram:


Problem: 2 billion users, single database can't handle load

Solution: Shard users by user_id using consistent hashing


• 1000 shards (each PostgreSQL instance)
• user_id 12345 → hash → Shard 789
• User's photos, followers, etc. stored on same shard (data locality)

Query Flow:
1. User views profile → Application computes shard_id from user_id
2. Routes query to correct shard
3. Single shard query (fast)

Challenge: Viewing follower feed requires data from multiple shards (slow)
Solution: Denormalize data, use caching (Redis)

When to Shard:
Don't shard prematurely! Shard when:
• Single database can't handle query load (even with replication)
• Database size exceeds single server capacity (multi-TB)
• Need geographic data distribution

Before sharding, try:


1. Optimize queries and indexes
2. Add read replicas
3. Use caching
4. Vertical scaling (bigger server)
11. Authentication and Authorization

Authentication vs Authorization:
Authentication: Who are you? (Login, verify identity)
• Username/password
• OAuth (Login with Google/Facebook)
• Biometric (fingerprint, face recognition)
• Multi-factor authentication (2FA)

Authorization: What can you do? (Permissions, access control)


• Admin can delete users, regular user cannot
• User can only view their own orders, not others'
• Premium subscriber can access premium content

JWT (JSON Web Tokens):


JWT is a compact, URL-safe token for stateless authentication.

Structure: [Link]

Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMzQ1LCJyb2xlIjoiYWRtaW4if
[Link]

Header: { "alg": "HS256", "typ": "JWT" }


Payload: { "userId": 12345, "role": "admin", "exp": 1708960000 }
Signature: HMACSHA256(base64(header) + "." + base64(payload), secret_key)

JWT Authentication Flow:


1. User submits username/password
2. Server validates credentials against database
3. Server generates JWT with user info (id, role, expiry)
4. Server returns JWT to client
5. Client stores JWT (localStorage/cookie)
6. Client includes JWT in every request: Authorization: Bearer <token>
7. Server validates JWT signature and expiry
8. Server extracts user info from JWT (no database lookup needed!)
9. Server processes request based on user permissions

Benefits of JWT:
• Stateless: No session storage needed on server
• Scalable: Any server can validate token (no shared session store)
• Cross-domain: Works across different domains/subdomains
• Mobile-friendly: Easy to use in mobile apps

JWT Security Considerations:


■ Never store sensitive data: JWT is base64-encoded, not encrypted (anyone can decode)
■ Never store passwords: Even hashed passwords shouldn't be in JWT
■ Use HTTPS: Prevent token interception
■ Short expiry: 15-60 minutes (use refresh tokens for longer sessions)
■ Strong secret key: At least 256 bits for HS256

OAuth 2.0:
OAuth is authorization framework for third-party access. Example: 'Login with Google'

Roles:
• Resource Owner: User
• Client: Your application
• Authorization Server: Google, Facebook, etc.
• Resource Server: Google's API servers

Flow (Authorization Code Grant):


1. User clicks 'Login with Google' on your app
2. Redirect to Google login page
3. User logs in and grants permissions
4. Google redirects back with authorization code
5. Your app exchanges code for access token (server-to-server)
6. Your app uses access token to call Google APIs (get profile, email)

Role-Based Access Control (RBAC):


Assign permissions to roles, then assign roles to users.

Example:
• Role: Admin → Permissions: CREATE_USER, DELETE_USER, VIEW_ALL_ORDERS
• Role: Customer → Permissions: VIEW_OWN_ORDERS, CREATE_ORDER
• User: Alice → Role: Admin
• User: Bob → Role: Customer

Authorization check:
if ([Link]("ADMIN") || [Link]() == [Link]()) {
// Allow viewing order
}

Spring Security Example:


@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.oauth2Login()
.sessionManagement(session ->
[Link]([Link])
);
return [Link]();
}
}

Real-World Example - Healthcare System:


Roles:
• Doctor: View all patient records, write prescriptions
• Nurse: View assigned patients, update vitals
• Patient: View own records only
• Admin: Manage users, access logs

Authorization logic:
GET /api/patients/12345/records
• If user is Patient #12345 → Allow
• If user is Doctor assigned to Patient #12345 → Allow
• If user is Admin → Allow
• Otherwise → 403 Forbidden
12. CAP Theorem

Definition:
CAP Theorem states that a distributed system can only guarantee TWO of the following three
properties:

C - Consistency: All nodes see same data at same time


A - Availability: System always responds to requests
P - Partition Tolerance: System continues despite network failures

Why Only Two?


Partition tolerance is mandatory in distributed systems (networks always fail sometimes). So the real
choice is between Consistency and Availability when partitions occur.

CP (Consistency + Partition Tolerance):


Sacrifice availability to maintain consistency. If nodes can't communicate, system refuses requests.
Examples: MongoDB, HBase, Redis

AP (Availability + Partition Tolerance):


Sacrifice consistency to maintain availability. System always responds, even with stale data.
Examples: Cassandra, CouchDB, DynamoDB

Real-World Scenarios:
Banking System (CP):
Scenario: Money transfer from Account A to Account B
• Database has 2 replicas in different data centers
• Network partition occurs (can't communicate)
• System MUST maintain consistency (can't have duplicate/lost money)
• Solution: Reject transfer requests until partition heals
• Trade-off: Availability sacrificed, but money is safe

Social Media Feed (AP):


Scenario: User posts a photo
• Database has 3 replicas globally
• Network partition occurs
• System MUST remain available (users expect to always post/view)
• Solution: Accept writes on all partitions, eventual consistency
• Trade-off: Some users see old feed temporarily, but system works

Eventual Consistency:
In AP systems, consistency is eventually achieved. Example:
• 10:00 AM: User posts photo on US server
• [Link] AM: Photo replicated to EU server
• [Link] AM: Photo replicated to Asia server
• Users in different regions see photo at slightly different times
• After replication completes, all regions consistent

This is acceptable for social media but NOT for banking.

Database Examples by CAP Category:


Category Database Use Case

CP MongoDB Financial transactions, inventory management

CP HBase Analytics, historical data

CP Redis Session store with strong consistency

AP Cassandra IoT data, time-series data

AP DynamoDB User profiles, shopping carts

AP Riak Content management, session store

Tunable Consistency (Cassandra Example):


Some databases let you choose consistency level per operation:

• Replication Factor: 3 (data copied to 3 nodes)


• Write Consistency Level:
■ ONE: Write to 1 node, return immediately (fast, less consistent)
■ QUORUM: Write to 2 nodes (majority), then return (balanced)
■ ALL: Write to all 3 nodes (slow, highly consistent)
• Read Consistency Level: Similar options

Example: User profile update (not critical) → ONE


Example: Payment processing (critical) → QUORUM or ALL

Practical Decision Framework:


Choose CP when:
• Data accuracy critical (finance, inventory, reservations)
• Temporary downtime acceptable
• Strong consistency required by law/regulation

Choose AP when:
• Availability more important than immediate consistency
• User experience degradation from downtime unacceptable
• Can tolerate eventual consistency (social media, caching, analytics)
ADVANCED LEVEL CONCEPTS (13-20)

13. Event-Driven Architecture (EDA)

Definition:
Event-Driven Architecture is a design pattern where components communicate through events. When
something happens (event), interested parties (subscribers) react to it.

Core Concepts:
Event: Immutable fact that something happened (OrderCreated, PaymentProcessed, UserRegistered)
Event Producer: Service that publishes events
Event Consumer: Service that subscribes to and processes events
Event Broker: Middleware that routes events (Kafka, RabbitMQ)

Event Sourcing:
Store all changes as sequence of events instead of current state.

Traditional Database Approach:


Account table: { id: 123, balance: 500 }
When money added/withdrawn, UPDATE balance directly. Lost history!

Event Sourcing Approach:


Event Stream:
1. AccountOpened { accountId: 123, initialBalance: 0 }
2. MoneyDeposited { accountId: 123, amount: 1000 }
3. MoneyWithdrawn { accountId: 123, amount: 500 }

Current state = replay all events → balance = 0 + 1000 - 500 = 500

Benefits:
• Complete audit trail (know exact history)
• Time travel (reconstruct state at any point in time)
• Event replay (reprocess events for new features)

Drawbacks:
• Complex querying (need to rebuild state)
• Storage overhead (all events stored forever)
• Eventual consistency

CQRS (Command Query Responsibility Segregation):


Separate models for writes (commands) and reads (queries).
Write Model: Handles commands, stores events
• Optimized for consistency and business rules
• Example: PostgreSQL for transactional data

Read Model: Handles queries, denormalized views


• Optimized for query performance
• Example: Elasticsearch for search, Redis for caching

Flow:
1. User submits CreateOrder command
2. Write model validates and creates OrderCreated event
3. Event published to Kafka
4. Read model consumer updates Elasticsearch index
5. User queries GET /orders → Read from Elasticsearch (fast)

Real-World Example - E-commerce Order Processing:


Event Flow:

1. OrderPlaced Event:
{ orderId: "order-123", userId: 456, items: [...], total: 299.99, timestamp: ... }

2. Event Consumers React:


• Inventory Service: Listens to OrderPlaced → Reserves items
• Payment Service: Listens to OrderPlaced → Processes payment
• Email Service: Listens to OrderPlaced → Sends confirmation
• Analytics Service: Listens to OrderPlaced → Updates dashboards
• Fraud Detection: Listens to OrderPlaced → Checks for fraud

3. Follow-up Events:
• PaymentProcessed → InventoryReserved → OrderShipped
• Each event triggers next set of actions

Benefits:
• Services loosely coupled (don't directly call each other)
• Easy to add new features (add new event consumers)
• Scalable (consumers process events independently)

Kafka as Event Backbone:


Apache Kafka is distributed event streaming platform.

Topics: Named event streams (orders-topic, payments-topic)


Partitions: Topics split for parallel processing
Consumer Groups: Multiple consumers share workload

Example:
• orders-topic has 3 partitions
• Email service has 3 consumer instances
• Each consumer processes events from one partition
• 3x parallelism!

Event-Driven Anti-Patterns:
■ Event Chains Too Long: Event A → Event B → Event C → Event D... (hard to debug)
■ Synchronous Event Processing: Defeats purpose, use async
■ Missing Idempotency: Events may be delivered twice, handle gracefully
14. Circuit Breaker Pattern

Problem:
In microservices, if Service A calls Service B and B is down, repeated failures cascade:
• Service A keeps trying B (wasting resources)
• Requests pile up (thread pool exhaustion)
• Service A also becomes slow/unresponsive
• Failure cascades to services depending on A

Solution: Circuit Breaker


Circuit Breaker pattern prevents cascading failures by stopping calls to failing service and providing fast
failure or fallback response.

Three States:
1. CLOSED (Normal Operation):
• All requests pass through to Service B
• Circuit monitors failure rate
• If failures exceed threshold (e.g., 50% in last 10 requests), trip to OPEN

2. OPEN (Failure State):


• All requests immediately fail (don't call Service B)
• Return fallback response or cached data
• After timeout period (e.g., 60 seconds), move to HALF_OPEN

3. HALF_OPEN (Recovery Testing):


• Allow limited requests through (e.g., 3 requests)
• If successful → Back to CLOSED
• If failed → Back to OPEN

Resilience4j Example (Java):


// Configuration
CircuitBreakerConfig config = [Link]()
.failureRateThreshold(50) // 50% failure rate triggers OPEN
.waitDurationInOpenState([Link](60)) // Stay OPEN for 60s
.slidingWindowSize(10) // Monitor last 10 requests
.build();
CircuitBreaker breaker = [Link]("paymentService", config);
// Usage
@Service
public class OrderService {
@CircuitBreaker(name = "paymentService", fallbackMethod =
"paymentFallback")
public Payment processPayment(Order order) {
return [Link]([Link]());
}
public Payment paymentFallback(Order order, Exception e) {
// Fallback: Queue payment for later processing
[Link](order);
return [Link]([Link]());
}
}
Real-World Example - Payment Gateway Integration:
Scenario: E-commerce app integrates with Stripe for payments

Without Circuit Breaker:


• Stripe API goes down
• Every checkout attempt waits 30 seconds for timeout
• 1000 concurrent users → 1000 threads blocked
• Entire application becomes unresponsive

With Circuit Breaker:


• First 10 payment attempts fail (exceed 50% threshold)
• Circuit breaker opens
• Next payment attempts immediately fail (no waiting)
• Fallback: Queue payments for later or show 'Payment temporarily unavailable'
• Application remains responsive
• After 60 seconds, circuit tries again (HALF_OPEN)
• If Stripe recovered, circuit closes and normal operation resumes

Fallback Strategies:
1. Return Cached Data: Show last known good data
2. Return Default Response: Generic/empty response
3. Queue for Later: Store request, process when service recovers
4. Fail Fast: Return error immediately (better than slow failure)

Monitoring:
Track circuit breaker metrics:
• Current state (CLOSED/OPEN/HALF_OPEN)
• Failure rate
• Number of successful/failed calls
• Time in each state

Alert when circuit opens (indicates service issues)


15. Rate Limiting and Throttling

Purpose:
Limit number of API requests to:
• Prevent abuse and DDoS attacks
• Ensure fair resource usage
• Protect backend from overload
• Enforce pricing tiers (free: 100 req/hour, paid: 10,000 req/hour)

Algorithms:
1. Token Bucket:
• Bucket holds N tokens (e.g., 100)
• Each request consumes 1 token
• Tokens refill at fixed rate (e.g., 10/minute)
• Allows bursts (up to bucket capacity)

2. Leaky Bucket:
• Requests added to queue
• Processed at constant rate (no bursts)
• Overflow requests rejected

3. Fixed Window:
• Count requests per time window (e.g., per minute)
• Reset counter at window boundary
• Problem: Burst at window edges (100 at 0:59, 100 at 1:00 = 200 in 1 second)

4. Sliding Window Log:


• Store timestamp of each request
• Count requests in last N seconds
• Accurate but memory-intensive

Implementation with Redis:


// Token Bucket with Redis
String key = "ratelimit:" + userId;
Long tokens = [Link]().get(key);
if (tokens == null) {
[Link]().set(key, 99, 60, [Link]); // 100 - 1 = 99
return true; // Allow request
}
if (tokens > 0) {
[Link]().decrement(key);
return true; // Allow request
}
return false; // Reject (429 Too Many Requests)

HTTP Response Headers:


X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 750
X-RateLimit-Reset: 1708963200
When limit exceeded: 429 Too Many Requests

Real-World Example - API Tiers:


Free Tier: 100 requests/hour
Basic Tier: 1,000 requests/hour
Premium Tier: 10,000 requests/hour

Different rate limits per user based on subscription level.


16. Distributed Transactions

Problem:
How to maintain ACID across multiple microservices/databases?

Example: Transfer money from Account A (Database 1) to Account B (Database 2)


• Debit from A must always match credit to B
• What if debit succeeds but credit fails?

Solution 1: Two-Phase Commit (2PC)


Phase 1 - Prepare:
1. Coordinator asks all participants: Can you commit?
2. Each participant locks resources and votes YES/NO

Phase 2 - Commit/Abort:
• If all voted YES → Coordinator sends COMMIT
• If any voted NO → Coordinator sends ABORT

Drawbacks:
• Blocking (locks held during coordination)
• Coordinator single point of failure
• Not used in modern microservices

Solution 2: Saga Pattern (Recommended)


Break transaction into sequence of local transactions. Each step has compensating transaction for
rollback.

Example: Order Processing Saga


1. Reserve Inventory → Compensating: Release Inventory
2. Process Payment → Compensating: Refund Payment
3. Ship Order → Compensating: Cancel Shipment

If any step fails, execute compensating transactions in reverse order.

Saga Orchestration vs Choreography:


Orchestration (Centralized):
• Saga orchestrator coordinates all steps
• Easier to understand and debug
• Single point of control

Choreography (Decentralized):
• Services react to events (no central coordinator)
• More resilient (no single point of failure)
• Harder to understand flow
Real-World Example - Flight Booking:
Saga Steps:
1. Reserve Flight → Success
2. Reserve Hotel → Success
3. Charge Credit Card → FAILED

Compensating Actions:
1. Release Hotel Reservation
2. Release Flight Reservation

Result: Consistent state, no partial booking


17. Service Discovery
In microservices, services need to find each other dynamically.

Service Registry: Database of all service instances (Eureka, Consul, Zookeeper)


• Services register on startup
• Services query registry to find dependencies
• Health checks remove dead instances

Example: Order Service needs Product Service IP:Port → Queries Eureka → Gets current instances

18. Idempotency
Operation produces same result when executed multiple times.

Why Important: Network failures cause retries, prevent duplicate operations

Example: Payment processing


• Client sends payment request with unique ID (idempotency key)
• Server checks if ID processed before
• If yes → Return cached result (don't charge twice)
• If no → Process payment, cache result

Implementation: Store processed request IDs in Redis/database

19. Data Consistency Models


Strong Consistency: Read always returns latest write (like single database)
• Example: Bank balance must be accurate immediately

Eventual Consistency: System becomes consistent after delay


• Example: Social media likes count (OK if slightly outdated)

Causal Consistency: Related operations seen in order


• Example: Comment appears after the post it replies to

20. Observability: Monitoring, Logging, Tracing


Three Pillars of Observability:

1. Metrics (Prometheus + Grafana):


• CPU, memory, request rate, error rate
• Time-series data for trend analysis

2. Logging (ELK Stack):


• Elasticsearch: Store logs
• Logstash: Process and transform logs
• Kibana: Visualize and search logs

3. Distributed Tracing (Jaeger/Zipkin):


• Track request flow across microservices
• Correlation ID links all related logs
• Identify bottlenecks and failures

Example Trace:
API Gateway (50ms) → Auth Service (20ms) → Order Service (100ms) → Payment Service (200ms)
Total latency: 370ms, Payment Service is bottleneck
Conclusion
This guide covered 20 essential High-Level Design concepts for Java backend developers. Mastering
these concepts will help you:

• Design scalable, resilient systems


• Make informed architectural decisions
• Troubleshoot distributed system issues
• Succeed in system design interviews

Remember: There's no one-size-fits-all solution. Choose patterns based on your specific requirements,
constraints, and trade-offs.

Next Steps:
1. Implement these patterns in small projects
2. Read case studies from companies like Netflix, Uber, Amazon
3. Practice system design interviews on platforms like LeetCode, Pramp
4. Stay updated with latest technologies and best practices

Happy Learning! ■

You might also like