What is Client-Server Architecture?
The Client-Server Architecture is one of the most fundamental concepts in computing and the backbone of nearly every application you use today. Whether you're checking email, browsing social media, shopping online, or using a banking app, you're interacting with client-server systems.
At its core, this architecture divides computing tasks between two distinct roles: clients that request services, and servers that provide them. This separation of concerns has revolutionized how we build software, enabling the internet as we know it.
A Simple Analogy: The Restaurant
Understanding client-server architecture becomes intuitive when you think of it like dining at a restaurant:
- Client (Customer): You, the diner, make requests. "I'd like a margherita pizza with extra cheese." You don't need to know how to make pizza or where the ingredients come from—you just need to know how to order.
- Server (Kitchen): The kitchen receives your order, has all the ingredients, equipment, and expertise to prepare it, and sends back the finished dish. The kitchen can serve many customers simultaneously.
- Network (Waiter): The waiter carries messages between you and the kitchen. They speak a "protocol"—taking your order in a standard format, delivering it to the kitchen, and bringing back your food.
- Menu (API): The menu defines what you can order and how to order it. You can't ask for something that's not on the menu (unless the restaurant is very accommodating!).
Client-Server Architecture is a distributed computing model where:
- Clients are programs that request services or resources. They initiate communication and present information to users.
- Servers are programs that provide services or resources. They wait for requests, process them, and return results.
- Network is the communication medium that connects them, using standardized protocols (usually HTTP/HTTPS for web applications).
- Protocol is the agreed-upon "language" and rules for communication (like HTTP, WebSocket, or gRPC).
The Fundamental Communication Pattern
Every client-server interaction follows a simple pattern: request and response. The client asks for something, and the server provides it (or explains why it can't).
// The fundamental flow of client-server communication:
┌──────────┐ Request ┌──────────┐
│ │ ────────────────────▶ │ │
│ CLIENT │ "GET /users" │ SERVER │
│ (Browser)│ │ (Java) │
│ │ ◀──────────────────── │ │
└──────────┘ Response └──────────┘
[User Data]
// Key characteristics:
// 1. Client ALWAYS initiates - server never contacts client first (in HTTP)
// 2. Server can handle MANY clients simultaneously
// 3. Client and server can be on different machines, different continents
// 4. Communication happens over a network using agreed protocols
Why Client-Server? A Brief History
Before client-server architecture, computing was dominated by mainframe systems where a single powerful computer did all the processing, and users accessed it through "dumb terminals" that could only display text.
The evolution happened in stages:
1960s-70s: Mainframe Era
// All processing on one central computer
┌─────────────────────────────────────────────────┐
│ MAINFRAME │
│ (Does ALL the computing work) │
└─────────────────────────────────────────────────┘
↑ ↑ ↑
[Terminal] [Terminal] [Terminal]
(Display (Display (Display
only) only) only)
// Problems: Expensive, single point of failure, limited scalability
1980s-90s: Client-Server Revolution
// Processing distributed between client and server
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Client │ │ Client │
│ PC │ │ PC │ │ PC │
│(Has own │ │(Has own │ │(Has own │
│ CPU) │ │ CPU) │ │ CPU) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└───────────────┼───────────────┘
│
┌──────┴──────┐
│ SERVER │
│ (Shared │
│ resources) │
└─────────────┘
// Benefits: Cheaper, more scalable, distributed processing
2000s-Present: Web and Cloud Era
// Thin clients (browsers) with powerful server infrastructure
📱 💻 🖥️ 📺
Mobile Laptop Desktop Smart TV
│ │ │ │
└──────────┴────┬────┴─────────┘
│
┌─────┴─────┐
│ INTERNET │
└─────┬─────┘
│
┌───────────────┼───────────────┐
│ │ │
┌───┴───┐ ┌────┴────┐ ┌────┴────┐
│Server │ │ Server │ │ Server │
│Cluster│ │ Cluster │ │ Cluster │
│(AWS) │ │(Google) │ │(Azure) │
└───────┘ └─────────┘ └─────────┘
// Modern reality: Multiple clients, distributed servers, cloud infrastructure
This evolution happened because of several factors:
- Cost reduction: Personal computers became affordable
- Network improvements: Faster, more reliable networks
- Scalability needs: More users required distributed processing
- User experience: Rich interfaces needed client-side processing
Understanding Roles: Who is the Client? Who is the Server?
One common source of confusion is understanding what constitutes a "client" and what constitutes a "server." The answer depends on perspective and context—the same machine can be both!
Client and server are ROLES, not physical machines. The same computer can act as a client in one interaction and a server in another. What matters is who initiates the request and who responds.
// Example: Your Java backend is BOTH client and server
Acts as SERVER Acts as CLIENT
(for browsers) (for database)
│ │
┌──────────┐ ┌────┴─────────────────────────────┴────┐ ┌──────────┐
│ │ Request │ │ Request │ │
│ Browser │ ──────▶ │ JAVA APPLICATION │ ──────▶ │ Database │
│ (Client) │ │ │ │ (Server) │
│ │ ◀────── │ │ ◀────── │ │
└──────────┘ Response└───────────────────────────────────────┘ Response└──────────┘
// The Java app RECEIVES requests from browsers (it's the server)
// The Java app SENDS requests to database (it's the client)
// In microservices, this gets even more complex:
User Service ──────▶ Order Service ──────▶ Payment Service
(client) (server/client) (server)
This dual nature is essential in modern distributed systems. Your Spring Boot application:
- Acts as a SERVER when handling HTTP requests from browsers or mobile apps
- Acts as a CLIENT when querying databases, calling external APIs, or communicating with other microservices
Key Components in Detail
The Client
A client is any program or device that initiates requests to a server. Clients are responsible for:
- User interface: Presenting data in a human-readable form
- Input handling: Collecting user actions (clicks, typing, gestures)
- Request formation: Translating user actions into server requests
- Response processing: Receiving and displaying server responses
- Local processing: Performing operations that don't need server involvement
// Types of clients in the Java ecosystem:
// 1. WEB BROWSER (Chrome, Firefox, Safari, Edge)
// The most common client for web applications
// - Renders HTML, CSS for visual presentation
// - Executes JavaScript for interactivity
// - Makes HTTP/HTTPS requests via fetch() or XMLHttpRequest
// - Handles cookies, local storage, session management
// 2. MOBILE APPLICATION (Android, iOS)
// Native apps that communicate with backend servers
// - Android: Java/Kotlin with Retrofit, OkHttp
// - iOS: Swift/Objective-C with URLSession
// - Cross-platform: React Native, Flutter
// 3. DESKTOP APPLICATION
// Standalone programs that may need server connectivity
// - JavaFX applications
// - Electron apps (VS Code, Slack desktop)
// - Native Windows/Mac/Linux applications
// 4. ANOTHER SERVER (Server-to-Server / Backend-to-Backend)
// In microservices, servers call other servers
// - Using RestTemplate, WebClient, Feign in Spring
// - gRPC for high-performance communication
// 5. COMMAND LINE TOOLS (curl, wget, httpie)
// Useful for testing and automation
curl -X GET https://api.example.com/users \
-H "Authorization: Bearer token123"
// 6. IOT DEVICES (Internet of Things)
// Smart devices that report data to servers
// - Temperature sensors, smart home devices
// - Often use MQTT or lightweight HTTP
The Server
A server is a program (and often the machine running it) that waits for incoming requests and provides responses. Think of it as a tireless worker that's always "listening" for someone to ask for help.
Server responsibilities include:
- Listening: Constantly waiting for incoming connections on specified ports
- Authentication: Verifying who is making the request
- Authorization: Checking if they're allowed to do what they're asking
- Business logic: Processing requests according to application rules
- Data management: Reading from and writing to databases
- Response generation: Creating and sending appropriate responses
- Concurrent handling: Managing multiple client requests simultaneously
// What a server actually does - step by step:
// 1. SERVER STARTUP
// The server binds to a port and starts listening
// Port 8080 for HTTP development, 443 for HTTPS production
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
// Server is now listening on port 8080
// It will wait here indefinitely for requests
}
}
// 2. REQUEST ARRIVES
// A client connects and sends an HTTP request
// Spring automatically routes it to the right method
@RestController
public class UserController {
@GetMapping("/users")
public List<User> getUsers() {
// 3. PROCESSING
// - Authenticate (who is this?)
// - Authorize (can they do this?)
// - Execute business logic
// - Access database if needed
// 4. RESPONSE
// - Create response object
// - Serialize to JSON
// - Send back to client
return userService.findAll();
}
}
// Common Java server technologies:
// - Spring Boot (most popular, convention over configuration)
// - Jakarta EE (enterprise standard, formerly Java EE)
// - Quarkus (cloud-native, fast startup)
// - Micronaut (low memory footprint, compile-time DI)
The Network
The network is the communication medium that allows clients and servers to exchange messages. For most web development, you'll work with networks through the HTTP protocol, but understanding the underlying layers helps when debugging issues.
Network communication is organized in layers, each with a specific responsibility. This is called the OSI model (simplified to 4 layers in TCP/IP):
// The network stack - how data travels from your browser to a server
┌─────────────────────────────────────────────────────────────────────┐
│ APPLICATION LAYER (HTTP, HTTPS, WebSocket, gRPC) │
│ ───────────────────────────────────────────────────────────────── │
│ This is where YOU work as a developer │
│ - HTTP requests and responses │
│ - JSON/XML data formats │
│ - Headers, cookies, authentication tokens │
├─────────────────────────────────────────────────────────────────────┤
│ TRANSPORT LAYER (TCP, UDP) │
│ ───────────────────────────────────────────────────────────────── │
│ Ensures reliable delivery of data │
│ - TCP: Reliable, ordered delivery (used by HTTP) │
│ - UDP: Fast but unreliable (used by video streaming, gaming) │
│ - Ports: Identifies which application gets the data │
├─────────────────────────────────────────────────────────────────────┤
│ NETWORK LAYER (IP - Internet Protocol) │
│ ───────────────────────────────────────────────────────────────── │
│ Handles addressing and routing │
│ - IP addresses: 192.168.1.1 (IPv4) or 2001:db8::1 (IPv6) │
│ - Routers use this to forward packets │
├─────────────────────────────────────────────────────────────────────┤
│ PHYSICAL/LINK LAYER (Ethernet, WiFi, Fiber) │
│ ───────────────────────────────────────────────────────────────── │
│ Actual transmission of bits over physical medium │
│ - MAC addresses │
│ - Electrical signals, light pulses, radio waves │
└─────────────────────────────────────────────────────────────────────┘
// What happens when you visit https://myapp.com/users:
1. Browser creates HTTP request (Application Layer)
2. Request is wrapped in TCP packet (Transport Layer)
- Source port: 54321 (random high port)
- Destination port: 443 (HTTPS)
3. TCP packet wrapped in IP packet (Network Layer)
- Source IP: Your computer's IP
- Destination IP: myapp.com's IP (resolved via DNS)
4. IP packet sent over your network (Physical Layer)
- WiFi, Ethernet, etc.
5. Packets hop through routers across the internet
6. Arrives at server, unpacked layer by layer
7. Server processes request, sends response back the same way
Ports: The Apartment Numbers of the Internet
A port is like an apartment number. The IP address gets you to the building (the server), but the port tells you which apartment (application) to deliver to.
// Common ports you should know:
Port 80 → HTTP (unencrypted web traffic)
Port 443 → HTTPS (encrypted web traffic)
Port 8080 → Common development server port
Port 3000 → Common for Node.js, React dev servers
Port 5432 → PostgreSQL database
Port 3306 → MySQL database
Port 27017 → MongoDB
Port 6379 → Redis
// Your Spring Boot app typically runs on:
http://localhost:8080 // During development
https://myapp.com:443 // In production (443 is default for HTTPS)
// A single server can run multiple applications on different ports:
Server IP: 192.168.1.100
├── Port 8080 → User Service
├── Port 8081 → Order Service
├── Port 8082 → Payment Service
└── Port 5432 → PostgreSQL Database
How It Works in Practice
// Step-by-step: User clicks "View Profile"
// 1. CLIENT: Browser creates HTTP request
GET /api/users/123 HTTP/1.1
Host: myapp.com
Accept: application/json
Authorization: Bearer eyJhbGc...
// 2. NETWORK: Request travels over internet
// DNS resolves myapp.com → 203.0.113.50
// TCP connection established
// Request sent to server
// 3. SERVER: Java application receives request
@GetMapping("/api/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
// Validate authentication
// Query database
User user = userRepository.findById(id)
.orElseThrow(() -> new NotFoundException());
return ResponseEntity.ok(user);
}
// 4. SERVER: Creates HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
// 5. CLIENT: Browser receives and renders data
// JavaScript processes JSON
// Updates the UI
Architecture Patterns
Two-Tier Architecture
// Direct client-to-database (legacy, simple apps)
┌──────────┐ ┌──────────┐
│ CLIENT │ ──────────▶ │ DATABASE │
│ (Desktop │ │ (MySQL) │
│ App) │ ◀────────── │ │
└──────────┘ └──────────┘
// Problems:
// - Business logic in client (hard to maintain)
// - Security risks (direct DB access)
// - Tight coupling
Three-Tier Architecture (Most Common)
// Client → Server → Database
┌──────────┐ ┌──────────┐ ┌──────────┐
│ CLIENT │ │ SERVER │ │ DATABASE │
│ (Browser)│ ────▶ │ (Spring │ ────▶ │ (Postgre │
│ │ │ Boot) │ │ SQL) │
│ │ ◀──── │ │ ◀──── │ │
└──────────┘ └──────────┘ └──────────┘
│
┌─────────┴─────────┐
│ │
Presentation Business
Layer Logic
(Controllers) (Services)
N-Tier / Microservices
// Multiple specialized services
┌──────────┐ ┌─────────────────────────────────────┐
│ │ │ API Gateway │
│ CLIENT │ ────▶ │ (Authentication) │
│ │ └─────────────────────────────────────┘
└──────────┘ │
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ User │ │ Order │ │ Payment │
│ Service │ │ Service │ │ Service │
└──────────┘ └──────────┘ └──────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ User DB │ │ Order DB │ │Payment DB│
└──────────┘ └──────────┘ └──────────┘
Java Client-Server Example
Server Side (Spring Boot)
// ProductController.java
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
// GET /api/products - List all products
@GetMapping
public List<Product> getAllProducts() {
return productService.findAll();
}
// GET /api/products/123 - Get specific product
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
return productService.findById(id);
}
// POST /api/products - Create new product
@PostMapping
public Product createProduct(@RequestBody Product product) {
return productService.save(product);
}
// PUT /api/products/123 - Update product
@PutMapping("/{id}")
public Product updateProduct(
@PathVariable Long id,
@RequestBody Product product) {
return productService.update(id, product);
}
// DELETE /api/products/123 - Delete product
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.delete(id);
}
}
Client Side (Java HttpClient)
// Java 11+ HttpClient as a client
public class ProductClient {
private final HttpClient httpClient = HttpClient.newHttpClient();
private final String baseUrl = "http://localhost:8080/api/products";
private final ObjectMapper mapper = new ObjectMapper();
// GET request - Fetch all products
public List<Product> getAllProducts() throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl))
.GET()
.header("Accept", "application/json")
.build();
HttpResponse<String> response = httpClient.send(
request, HttpResponse.BodyHandlers.ofString()
);
return mapper.readValue(
response.body(),
new TypeReference<List<Product>>() {}
);
}
// POST request - Create a product
public Product createProduct(Product product) throws Exception {
String json = mapper.writeValueAsString(product);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl))
.POST(HttpRequest.BodyPublishers.ofString(json))
.header("Content-Type", "application/json")
.build();
HttpResponse<String> response = httpClient.send(
request, HttpResponse.BodyHandlers.ofString()
);
return mapper.readValue(response.body(), Product.class);
}
}
Stateless vs Stateful Servers
| Aspect | Stateless | Stateful |
|---|---|---|
| Memory | No client data stored | Remembers client state |
| Scalability | Easy to scale horizontally | Requires sticky sessions |
| Example | REST APIs with JWT | WebSocket connections |
| Request handling | Any server can handle | Same server required |
// STATELESS: Each request contains all needed info
@GetMapping("/api/profile")
public User getProfile(@RequestHeader("Authorization") String token) {
// Token contains user identity - no server-side session needed
Long userId = jwtService.extractUserId(token);
return userService.findById(userId);
}
// STATEFUL: Server remembers the session
@GetMapping("/api/profile")
public User getProfile(HttpSession session) {
// Server stores session data - must hit same server
Long userId = (Long) session.getAttribute("userId");
return userService.findById(userId);
}
Benefits and Challenges
Benefits:
- Separation of concerns: UI separate from business logic
- Scalability: Add more servers to handle load
- Maintainability: Update server without changing clients
- Security: Centralized data protection
- Multi-platform: One server, many client types
Challenges:
- Network dependency: Requires connectivity
- Latency: Network round-trips add delay
- Single point of failure: Server down = app down
- Complexity: More moving parts to manage
Summary
- Client: Initiates requests (browser, mobile app, other servers)
- Server: Processes requests and returns responses
- Network: Carries messages using protocols (HTTP)
- Three-tier: Client → Server → Database (most common)
- Stateless: Preferred for scalability (REST + JWT)
- Java servers: Spring Boot, Jakarta EE handle the heavy lifting