Microservices Architecture
Complete Tutorial with Visual Diagrams, Implementation Guides, and Best Practices for Modern Distributed Systems
What Are Microservices?
The Microservices Revolution
Microservices architecture is an approach where a single application is composed of many loosely coupled, independently deployable services. Each service implements specific business capabilities and communicates via lightweight protocols.
Monolithic Architecture
Single Application
Microservices Architecture
API Gateway
Auth Service
Order Service
Payment Service
Inventory Service
Monolithic vs Microservices
| Aspect | Monolithic | Microservices |
|---|---|---|
| Architecture | Single unified codebase | Multiple independent services |
| Deployment | Single deployment unit | Independent deployments |
| Scaling | Scale entire application | Scale individual services |
| Technology | Single tech stack | Polyglot (multiple stacks) |
| Database | Single shared database | Database per service |
| Failure Impact | Single point of failure | Isolated failures |
| Team Structure | Large teams per layer | Small cross-functional teams |
When to Choose Microservices
β Large Development Teams
Multiple teams working on different features simultaneously
β Need for Independent Scaling
Different services have different scaling requirements
β Technology Diversity
Different parts need different technology stacks
β High Availability
Need 99.99% uptime with fault isolation
Microservices Architecture Patterns
Core Architecture Components
Client Applications
Web, Mobile, Desktop
API Gateway
Service Discovery
Eureka, Consul
Load Balancer
Ribbon, Nginx
Circuit Breaker
Hystrix, Resilience4j
Business Services
User Service
Product Service
Order Service
Payment Service
Notification Service
Data Layer
Service Communication Patterns
Synchronous Communication
Direct request-response using REST or gRPC
// REST API Call
GET /api/users/123
// gRPC Call
userService.GetUser({id: 123})
// Response (blocking)
{
"id": 123,
"name": "John Doe",
"email": "[email protected]"
}Asynchronous Communication
Event-driven using message queues
// Event Publishing
orderService.publish(
"order.created",
{ orderId: 456, amount: 99.99 }
)
// Event Consumption
paymentService.subscribe(
"order.created",
processPayment
)Data Management Patterns
Database per Service Pattern
User Service
Users DB
Order Service
Orders DB
Product Service
Products DB
Saga Pattern for Distributed Transactions
Orchestration
Choreography
Essential Microservices Patterns
API Gateway Pattern
The API Gateway acts as a single entry point for all client requests, routing them to appropriate services while handling cross-cutting concerns.
Client Applications
API Gateway
Routing
Authentication
Rate Limiting
Caching
Order Service
User Service
Payment Service
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: userService
fallbackUri: forward:/fallback/user
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-Id, ${spring.cloud.sleuth.traceId}Service Discovery Pattern
Client-Side Discovery
Client queries service registry and selects instance
Server-Side Discovery
Router/Load balancer queries registry
# Eureka Server Configuration
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# Client Configuration
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
lease-renewal-interval-in-seconds: 30
lease-expiration-duration-in-seconds: 90Circuit Breaker Pattern
CLOSED
Requests flow normally
OPEN
Requests fail immediately
HALF-OPEN
Limited test requests
Circuit Breaker Flow
@Configuration
public class ResilienceConfig {
@Bean
public CircuitBreakerConfig circuitBreakerConfig() {
return CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofMillis(1000))
.slidingWindowSize(10)
.permittedNumberOfCallsInHalfOpenState(3)
.recordExceptions(IOException.class,
TimeoutException.class)
.build();
}
@Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
return CircuitBreakerRegistry.of(circuitBreakerConfig());
}
}
@Service
public class ExternalService {
@CircuitBreaker(name = "externalService",
fallbackMethod = "fallback")
public String callExternalService() {
// Call external service
return restTemplate.getForObject(url, String.class);
}
public String fallback(Exception ex) {
return "Fallback response due to: " + ex.getMessage();
}
}Practical Implementation
E-commerce Microservices Example
E-commerce Platform Architecture
Web & Mobile Apps
API Gateway (Spring Cloud Gateway)
User Service
Authentication & Profiles
Product Service
Catalog Management
Order Service
Order Processing
Payment Service
Payment Processing
Shipping Service
Delivery Management
Notification Service
Email & SMS Notifications
Infrastructure Layer
PostgreSQL
MongoDB
Redis Cache
RabbitMQ
Elasticsearch
Complete Service Implementation
// Order Service Main Application
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// Order Entity
@Entity
@Table(name = "orders")
@Data
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
private String userId;
private List<OrderItem> items;
private BigDecimal totalAmount;
private OrderStatus status;
private LocalDateTime createdAt;
}
// Order Service Implementation
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
private final OrderRepository orderRepository;
private final ProductServiceClient productService;
private final PaymentServiceClient paymentService;
private final CircuitBreakerFactory circuitBreakerFactory;
@Override
@Transactional
public Order createOrder(CreateOrderRequest request) {
// Validate products
List<Product> products = circuitBreakerFactory.create("productService")
.run(() -> productService.validateProducts(request.getItems()),
throwable -> {
log.error("Product service unavailable", throwable);
throw new ServiceUnavailableException("Product service unavailable");
});
// Calculate total
BigDecimal total = calculateTotal(products, request.getItems());
// Create order
Order order = Order.builder()
.userId(request.getUserId())
.items(request.getItems())
.totalAmount(total)
.status(OrderStatus.PENDING)
.createdAt(LocalDateTime.now())
.build();
order = orderRepository.save(order);
// Process payment asynchronously
processPaymentAsync(order);
return order;
}
@Async
public void processPaymentAsync(Order order) {
try {
PaymentResponse payment = paymentService.processPayment(
new PaymentRequest(order.getId(), order.getTotalAmount())
);
if (payment.isSuccess()) {
order.setStatus(OrderStatus.CONFIRMED);
// Publish order confirmed event
eventPublisher.publishEvent(new OrderConfirmedEvent(order));
} else {
order.setStatus(OrderStatus.FAILED);
}
orderRepository.save(order);
} catch (Exception e) {
log.error("Payment processing failed", e);
order.setStatus(OrderStatus.FAILED);
orderRepository.save(order);
}
}
}
// Feign Client for Product Service
@FeignClient(name = "product-service",
configuration = FeignConfig.class,
fallback = ProductServiceFallback.class)
public interface ProductServiceClient {
@PostMapping("/api/products/validate")
List<Product> validateProducts(@RequestBody List<OrderItem> items);
@GetMapping("/api/products/{id}")
Product getProduct(@PathVariable String id);
}
// Circuit Breaker Configuration
@Configuration
public class CircuitBreakerConfig {
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {
return factory -> factory.configureDefault(id ->
Resilience4JConfigBuilder.of(id)
.circuitBreakerConfig(CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.slowCallRateThreshold(100)
.waitDurationInOpenState(Duration.ofSeconds(5))
.slidingWindowSize(10)
.minimumNumberOfCalls(5)
.build())
.timeLimiterConfig(TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(3))
.build())
.build()
);
}
}
// API Gateway Routes Configuration
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: orderService
fallbackUri: forward:/fallback/order
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
- AddRequestHeader=X-User-Id, ${user.id}Docker & Kubernetes Deployment
Docker Compose Setup
version: '3.8'
services:
postgres:
image: postgres:14
environment:
POSTGRES_DB: orderdb
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"
eureka-server:
build: ./eureka-server
ports:
- "8761:8761"
environment:
SPRING_PROFILES_ACTIVE: docker
api-gateway:
build: ./api-gateway
ports:
- "8080:8080"
depends_on:
- eureka-server
order-service:
build: ./order-service
ports:
- "8081:8081"
environment:
SPRING_PROFILES_ACTIVE: docker
EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka-server:8761/eureka/
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/orderdb
depends_on:
- postgres
- eureka-server
deploy:
replicas: 2Kubernetes Deployment
# order-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: microservices
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: order-service:latest
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "kubernetes"
- name: EUREKA_CLIENT_SERVICEURL_DEFAULTZONE
value: "http://eureka-server:8761/eureka/"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5Tools & Technology Stack
Complete Microservices Stack
Development
- Spring Boot
- Node.js
- Python/FastAPI
- Go
Service Mesh
- Istio
- Linkerd
- Consul
- Kong
Data Stores
- PostgreSQL
- MongoDB
- Redis
- Cassandra
Message Brokers
- Kafka
- RabbitMQ
- AWS SQS
- NATS
Monitoring
- Prometheus
- Grafana
- ELK Stack
- Jaeger
Deployment
- Docker
- Kubernetes
- Helm
- Terraform
Framework Comparison
| Framework | Best For | Language | Learning Curve | Ecosystem |
|---|---|---|---|---|
| Spring Boot | Enterprise Java applications | Java/Kotlin | Medium | Excellent |
| Node.js | Real-time, I/O intensive apps | JavaScript/TypeScript | Easy | Excellent |
| .NET Core | Enterprise Windows/Linux apps | C# | Medium | Good |
| Go | High-performance, concurrent apps | Go | Easy | Growing |
| Python FastAPI | Data-intensive, ML applications | Python | Easy | Excellent |
Development Tools
Docker
Containerization
Kubernetes
Orchestration
Prometheus
Monitoring
Jaeger
Distributed Tracing
GitLab CI/CD
Continuous Integration
Best Practices & Anti-Patterns
Do’s and Don’ts
Best Practices
- Design around business capabilities
- Implement circuit breakers
- Use API Gateway for routing
- Implement centralized logging
- Use database per service
- Implement health checks
- Use containerization
- Implement retry mechanisms
Anti-Patterns
- Distributed monolith
- Shared database
- Chatty communication
- No API versioning
- Hard-coded endpoints
- No circuit breakers
- Inconsistent logging
- No health monitoring
Security Best Practices
Authentication
- OAuth 2.0 / OpenID Connect
- JWT tokens
- API keys for services
- Multi-factor authentication
Authorization
- Role-based access control
- Attribute-based access
- Policy-based authorization
- Service-to-service auth
Network Security
- Mutual TLS (mTLS)
- Service mesh security
- Network policies
- API rate limiting
Monitoring & Audit
- Centralized logging
- Distributed tracing
- Security event monitoring
- Compliance auditing
Performance Optimization
Performance Optimization Strategies
Caching
Redis, Memcached
Async Processing
Message queues
Database Optimization
Indexing, Sharding
Load Balancing
Round-robin, Least conn
Connection Pooling
Database, HTTP
Compression
GZIP, Brotli
Real-World Case Studies
Netflix – Microservices at Scale
Netflix Architecture
API Gateway
Zuul
Service Discovery
Eureka
Load Balancer
Ribbon
Key Learnings from Netflix
Chaos Engineering
Chaos Monkey for resilience testing
Circuit Breakers
Hystrix for fault tolerance
Deployment
Spinnaker for CD
Monitoring
Atlas for metrics
Uber – Event-Driven Architecture
Before Microservices
- Monolithic codebase
- Difficult to scale
- Long deployment cycles
- Team coordination issues
After Microservices
- 1300+ microservices
- Independent scaling
- Faster deployments
- Team autonomy
// Trip Service publishes event
tripService.completeTrip(tripId) {
// Update trip status
trip.status = COMPLETED;
tripRepository.save(trip);
// Publish event
eventBus.publish("trip.completed", {
tripId: trip.id,
driverId: trip.driverId,
riderId: trip.riderId,
amount: trip.amount,
completedAt: new Date()
});
}
// Billing Service subscribes
billingService.subscribe("trip.completed", (event) => {
// Create invoice
const invoice = createInvoice(event);
billingRepository.save(invoice);
// Send to payment service
paymentService.processPayment(invoice);
});
// Notification Service subscribes
notificationService.subscribe("trip.completed", (event) => {
// Send receipt to rider
emailService.sendReceipt(event.riderId, event);
// Notify driver
pushService.notifyDriver(event.driverId, "Trip completed");
});Amazon – Two-Pizza Teams
Amazon’s Organizational Structure
Small, autonomous teams
Full ownership
Well-defined interfaces
You build it, you run it
Ready to Build Microservices?
Download our complete microservices template with Docker, Kubernetes, and monitoring setup.
