Microservices Architecture
Microservices is one of the most asked topics in PRA and Sprints 3-4. The questions are mostly conceptual — understanding the architecture, components, and when to use what. You won't be asked to build a full microservices system, but you need to know Spring Cloud annotations and the role of each component.
Sprint 3-4: You may build simple Spring Boot microservices with Eureka discovery. Know annotations cold.
1. What are Microservices?
Microservices is an architectural style where an application is built as a collection of small, independent services, each running in its own process and communicating via lightweight mechanisms (usually HTTP/REST).
Each service does ONE thing well — it has a single responsibility, its own database, and can be deployed independently.
Microservices = a food court with specialized stalls. The pizza stall, the burger stall, the juice stall — each operates independently. If the pizza stall has a problem, everyone else keeps running.
2. Monolithic vs Microservices (ALWAYS ASKED)
This is the most frequently asked comparison in TCS ILP exams. Memorize this table — at least 1-2 questions will come from here.
Monolithic Architecture
The entire application is built as a single deployable unit. All features — UI, business logic, data access — are in one codebase, one project, one WAR/JAR file.
Microservices Architecture
Each feature is its own independently deployable service. User management is one service, payment is another, notifications is another. Each has its own codebase, database, and deployment pipeline.
| Aspect | Monolithic | Microservices |
|---|---|---|
| Codebase | Single codebase for everything | Separate codebase per service |
| Deployment | Deploy entire app for any change | Deploy only the changed service |
| Scaling | Scale the entire app (vertical) | Scale individual services (horizontal) |
| Technology | One tech stack for everything | Each service can use different tech |
| Team Structure | One large team | Small teams per service |
| Failure Impact | One bug can crash the whole app | Failure is isolated to one service |
| Database | Shared database | Database per service |
| Communication | In-process method calls | Network calls (REST, messaging) |
| Complexity | Simple to develop initially | Complex (distributed systems) |
| Startup Time | Slow (loads entire app) | Fast (small services) |
| Testing | Easier (single app) | Harder (integration across services) |
When to use Monolith?
- Small application with limited functionality
- Small development team
- Quick prototype / MVP
- No need for independent scaling
When to use Microservices?
- Large, complex application
- Multiple teams working in parallel
- Need to scale specific features independently
- Different parts need different technologies
- High availability is critical
3. Key Characteristics of Microservices
These are the defining features of microservices architecture. Exam asks "which of the following is a characteristic of microservices?"
| Characteristic | What It Means |
|---|---|
| Single Responsibility | Each service handles ONE business capability (e.g., only user management, only payments) |
| Independent Deployment | Deploy one service without affecting others. No need to redeploy the whole app. |
| Decentralized Data | Each service owns its own database. No shared database across services. |
| Lightweight Communication | Services talk via REST APIs (HTTP) or message queues. Not heavy protocols like SOAP. |
| Technology Agnostic | User Service can be in Java, Payment Service in Python, Notification Service in Node.js. |
| Fault Isolation | If Payment Service crashes, User Service and Product Service keep working. |
| Independently Scalable | If Order Service gets heavy traffic, scale only that service — not the entire app. |
| Organized Around Business | Teams are organized by business domain (Order team, Payment team), not by technology layers. |
4. Microservices Architecture Components
A microservices system isn't just services — it needs supporting infrastructure. Know each component's role.
┌──────────────┐
│ Client │ (Browser / Mobile App)
└──────┬───────┘
│
┌──────▼───────┐
│ API Gateway │ (Single entry point)
└──────┬───────┘
│
┌───────────┼───────────┐
│ │ │
┌───▼───┐ ┌────▼────┐ ┌────▼────┐
│ User │ │ Order │ │ Payment │ (Microservices)
│Service│ │ Service │ │ Service │
└───┬───┘ └────┬────┘ └────┬────┘
│ │ │
┌───▼───┐ ┌────▼────┐ ┌────▼────┐
│User DB│ │Order DB │ │Pay DB │ (Each owns its DB)
└───────┘ └─────────┘ └─────────┘
All services register with:
┌────────────────────┐
│ Service Registry │ (Eureka Server)
│ (Eureka) │
└────────────────────┘
Config loaded from:
┌────────────────────┐
│ Config Server │ (Centralized config)
└────────────────────┘
| Component | Role | Spring Cloud Tool |
|---|---|---|
| API Gateway | Single entry point — routes requests, handles auth, rate limiting | Spring Cloud Gateway / Zuul |
| Service Registry | Services register themselves; other services discover them by name | Netflix Eureka |
| Config Server | Centralized configuration for all services | Spring Cloud Config |
| Load Balancer | Distributes incoming requests across service instances | Ribbon (client-side) |
| Circuit Breaker | Prevents cascading failures when a service is down | Resilience4j / Hystrix |
| Distributed Tracing | Track a request across multiple services | Sleuth + Zipkin |
5. Communication Between Services
Services need to talk to each other. There are two main approaches:
Synchronous Communication
Service A calls Service B and waits for the response before continuing.
- REST APIs (HTTP) — most common, uses JSON over HTTP. Service A makes a GET/POST request to Service B.
- gRPC — faster than REST, uses Protocol Buffers (binary format). Used for high-performance internal communication.
- Feign Client — Spring Cloud tool that makes REST calls between services look like simple method calls.
Asynchronous Communication
Service A sends a message and doesn't wait. Service B processes it later.
- Message Queues — RabbitMQ, ActiveMQ. Producer sends message to queue, consumer picks it up.
- Event Streaming — Apache Kafka. Services publish events to topics, other services subscribe.
| Aspect | Synchronous (REST) | Asynchronous (Messaging) |
|---|---|---|
| Waiting | Caller waits for response | Caller doesn't wait (fire-and-forget) |
| Coupling | Tighter — both services must be up | Looser — services are decoupled |
| Speed | Immediate response | Eventually processed |
| Use Case | Get user details, check inventory | Send email notification, process order |
| Failure Handling | Fails if target is down | Message waits in queue until target is up |
Asynchronous: When you DON'T need an immediate response (e.g., "Send a confirmation email" after order is placed).
6. API Gateway
The API Gateway is the single entry point for all client requests. Instead of clients calling individual services directly, they call the gateway, and it routes the request to the right service.
Without API Gateway:
Client → User Service (port 8081)
Client → Order Service (port 8082)
Client → Payment Service (port 8083)
(Client needs to know every service's address!)
With API Gateway:
Client → API Gateway (port 8080) → routes to correct service
Client only knows ONE address!
What API Gateway Does:
- Request Routing — forwards
/api/users/**to User Service,/api/orders/**to Order Service - Authentication & Authorization — validates JWT tokens before forwarding
- Rate Limiting — prevents too many requests from one client
- Load Balancing — distributes across multiple instances of a service
- Request/Response Transformation — modifies headers, aggregates responses
- SSL Termination — handles HTTPS, forwards HTTP internally
Spring Cloud Gateway Config (Know the Format)
# application.yml for API Gateway
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/api/orders/**
7. Service Discovery (Eureka)
The Problem
In microservices, services run on dynamic IPs and ports. If Order Service needs to call User Service, how does it know where User Service is running? Hardcoding IPs breaks everything when services scale or restart.
The Solution: Service Registry
A Service Registry (like Netflix Eureka) acts as a phone book for services:
- Register — When a service starts, it registers itself with Eureka ("I'm User Service, running at 192.168.1.5:8081")
- Discover — When Order Service needs User Service, it asks Eureka ("Where is User Service?")
- Call — Order Service gets the address and makes the call
Client-Side vs Server-Side Discovery
| Type | How It Works | Example |
|---|---|---|
| Client-Side | Client queries registry and picks an instance itself | Netflix Eureka + Ribbon |
| Server-Side | Client calls a load balancer, which queries registry and routes | AWS ELB, Kubernetes |
Eureka Setup (Know These Annotations)
// Eureka SERVER — the registry itself
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
// Eureka CLIENT — any microservice that registers
@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
# Eureka Server — application.properties
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
# Eureka Client — application.properties
spring.application.name=user-service
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
@EnableEurekaClient — goes on EACH microservice that wants to register.
The server has
register-with-eureka=false because the registry doesn't register itself!
8. Spring Cloud for Microservices
Spring Cloud provides tools to implement microservices patterns on top of Spring Boot. Know the mapping:
| Pattern | Spring Cloud Tool | Annotation |
|---|---|---|
| Service Discovery | Netflix Eureka | @EnableEurekaServer, @EnableEurekaClient |
| API Gateway | Spring Cloud Gateway / Zuul | @EnableZuulProxy (Zuul) |
| Service-to-Service Calls | OpenFeign | @EnableFeignClients, @FeignClient |
| Client-Side Load Balancing | Ribbon | Auto-configured with Eureka |
| Circuit Breaker | Resilience4j / Hystrix | @CircuitBreaker |
| Centralized Config | Spring Cloud Config | @EnableConfigServer |
| Distributed Tracing | Sleuth + Zipkin | Auto-configured |
Feign Client — Service-to-Service Calls
Instead of writing RestTemplate boilerplate, Feign lets you call other services like calling a local method:
// Enable Feign in main class
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication { ... }
// Define a Feign Client interface
@FeignClient(name = "USER-SERVICE")
public interface UserClient {
@GetMapping("/api/users/{id}")
User getUserById(@PathVariable Long id);
}
// Use it in Order Service — just inject and call!
@RestController
public class OrderController {
@Autowired
private UserClient userClient;
@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable Long id) {
User user = userClient.getUserById(id); // Calls User Service via Eureka!
// ...
}
}
@FeignClient(name = "USER-SERVICE") — the name must match the spring.application.name of the target service registered in Eureka. Feign uses Eureka to resolve the actual address.
9. Circuit Breaker Pattern
The Problem
If Order Service calls Payment Service, and Payment Service is down, Order Service keeps trying and waiting. Meanwhile, requests pile up, Order Service runs out of resources, and crashes too. Now User Service calls Order Service which is also down — cascading failure. One service going down takes everything down.
The Solution: Circuit Breaker
Like an electrical circuit breaker — when it detects too many failures, it "opens" the circuit and stops calling the failing service. Instead, it returns a fallback response immediately.
Three States
| State | What Happens | When It Transitions |
|---|---|---|
| CLOSED (Normal) | All requests pass through normally | Moves to OPEN when failure threshold is reached |
| OPEN (Failing) | All requests immediately return fallback response. No calls to failing service. | After a timeout period, moves to HALF-OPEN |
| HALF-OPEN (Testing) | Allows a few test requests through. If they succeed, back to CLOSED. If they fail, back to OPEN. | Success → CLOSED, Failure → OPEN |
CLOSED (all good)
│
│ failures exceed threshold (e.g., 5 failures in a row)
▼
OPEN (stop calling, return fallback)
│
│ wait timeout (e.g., 30 seconds)
▼
HALF-OPEN (try a few requests)
│
├── success → back to CLOSED ✓
└── failure → back to OPEN ✗
Resilience4j Example (Know the Concept)
@RestController
public class OrderController {
@GetMapping("/orders/{id}")
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
public String getOrder(@PathVariable Long id) {
// calls Payment Service — may fail
return restTemplate.getForObject("http://PAYMENT-SERVICE/pay/" + id, String.class);
}
// Fallback — runs when circuit is OPEN
public String paymentFallback(Long id, Throwable t) {
return "Payment service is currently unavailable. Please try later.";
}
}
10. Docker & Containers (Brief)
Docker packages your microservice + all its dependencies into a container — a lightweight, portable unit that runs the same everywhere.
Key Concepts
| Concept | What It Is |
|---|---|
| Docker Image | A blueprint/template — like a class in Java. Read-only. |
| Docker Container | A running instance of an image — like an object. You can have multiple containers from one image. |
| Dockerfile | A text file with instructions to build an image. |
| Docker Hub | Public registry for images (like GitHub for Docker images). |
| Docker Compose | Tool to run multiple containers together (e.g., all microservices + databases). |
Dockerfile Basics
# Dockerfile for a Spring Boot microservice
FROM openjdk:17-jdk-slim
COPY target/user-service.0.0.1.jar app.jar
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "app.jar"]
Common Docker Commands
# Build an image from Dockerfile
docker build -t user-service .
# Run a container from the image
docker run -p 8081:8081 user-service
# List running containers
docker ps
# Stop a container
docker stop <container-id>
# List images
docker images
Container = running instance (like a Java object). Has its own state, can be started/stopped.
11. Microservices Design Patterns
Database Per Service
Each microservice has its own database. Order Service uses its own Order DB, User Service uses User DB. They never access each other's database directly — only through APIs.
Why: Loose coupling, independent deployment, each service can choose its own database type (SQL, NoSQL).
Saga Pattern (Distributed Transactions)
In monolith, you have one database transaction. In microservices, an "order" might involve Order Service, Payment Service, and Inventory Service — each with its own DB. How do you keep them consistent?
Saga = a sequence of local transactions. If one step fails, compensating transactions undo the previous steps.
- Order Service → Create order (PENDING)
- Payment Service → Charge customer
- Inventory Service → Reserve stock
- Order Service → Update order (CONFIRMED)
Two types of Saga:
- Choreography — each service publishes events, next service listens and reacts. No central controller.
- Orchestration — a central orchestrator tells each service what to do and when.
CQRS (Command Query Responsibility Segregation)
Separate the write model (commands: create, update, delete) from the read model (queries: get, search). Different databases or models for reads vs writes. Useful when read and write patterns are very different.
Event-Driven Architecture
Services communicate by publishing and subscribing to events. When Order Service creates an order, it publishes an "OrderCreated" event. Payment Service subscribes to this event and processes payment. Decouples services completely.
12. Advantages vs Disadvantages
Advantages
| Advantage | Explanation |
|---|---|
| Independent Deployment | Deploy one service without touching others |
| Independent Scaling | Scale only the services that need it |
| Technology Freedom | Each service can use different languages, frameworks, databases |
| Fault Isolation | One service crashing doesn't take down the whole system |
| Faster Development | Small teams work on small services — faster iterations |
| Easy to Understand | Each service is small and focused — easier to comprehend |
| Reusability | Services can be reused across different applications |
Disadvantages
| Disadvantage | Explanation |
|---|---|
| Complexity | Distributed system = harder to develop, deploy, and debug |
| Network Latency | Services communicate over network — slower than in-process calls |
| Data Consistency | No shared database — ensuring consistency across services is hard |
| Testing Difficulty | Integration testing across services is complex |
| Operational Overhead | Need monitoring, logging, deployment pipelines for EACH service |
| Debugging | A request goes through multiple services — tracing issues is harder |
13. Real-World Example: E-Commerce App
Let's see how a real e-commerce application would be split into microservices:
┌─────────────────────────────────────────────┐
│ API Gateway (:8080) │
└──────┬──────┬──────┬──────┬──────┬──────────┘
│ │ │ │ │
┌────▼──┐ ┌─▼────┐ ┌▼─────┐ ┌───▼───┐ ┌────▼────────┐
│ User │ │Produc│ │Order │ │Payment│ │Notification │
│Service│ │tServ.│ │Serv. │ │Service│ │ Service │
│ :8081 │ │:8082 │ │:8083 │ │ :8084 │ │ :8085 │
└───┬───┘ └──┬───┘ └──┬───┘ └───┬───┘ └─────────────┘
│ │ │ │
┌───▼───┐ ┌──▼───┐ ┌──▼───┐ ┌──▼────┐
│UserDB │ │Prod │ │Order │ │PayDB │
│(MySQL)│ │DB │ │DB │ │ │
└───────┘ │(Mongo│ └──────┘ └───────┘
│ DB) │
└──────┘
| Service | Responsibility | Communicates With |
|---|---|---|
| User Service | Registration, login, profile management | Auth only — mostly independent |
| Product Service | Product catalog, search, inventory | Order Service (stock check) |
| Order Service | Create/manage orders | User Service, Product Service, Payment Service |
| Payment Service | Process payments, refunds | Order Service |
| Notification Service | Send emails, SMS, push notifications | Listens to events from all services |
How a Customer Places an Order:
- Client sends request to API Gateway
- Gateway routes to Order Service
- Order Service calls User Service (via Feign) to validate user
- Order Service calls Product Service to check stock
- Order Service calls Payment Service to process payment
- Payment Service publishes "PaymentCompleted" event
- Notification Service listens to the event and sends confirmation email
- Order status updated to CONFIRMED
14. Practice MCQs
register-with-eureka=false in its configuration?