microservices-tutorial

Microservices Architecture: Complete Tutorial with Diagrams | TKTips.org

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
UI
Auth
Orders
Payment
Single Codebase & Deployment

Microservices Architecture

API Gateway
Auth Service
Order Service
Payment Service
Inventory Service
Independent Services & Deployments

Monolithic vs Microservices

AspectMonolithicMicroservices
ArchitectureSingle unified codebaseMultiple independent services
DeploymentSingle deployment unitIndependent deployments
ScalingScale entire applicationScale individual services
TechnologySingle tech stackPolyglot (multiple stacks)
DatabaseSingle shared databaseDatabase per service
Failure ImpactSingle point of failureIsolated failures
Team StructureLarge teams per layerSmall 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

Routing Auth Rate Limit
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

PostgreSQL MongoDB Redis

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
1. Order Service creates order
2. Saga Orchestrator coordinates
3. Payment Service processes
4. Inventory Service updates
Choreography
1. Order Service publishes event
2. Payment Service listens & processes
3. Inventory Service listens & updates
4. Notification Service sends updates

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 Configuration
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

Service A
Registry
Service B

Server-Side Discovery

Router/Load balancer queries registry

Client
Router
Service B
Eureka Service Registry Configuration
# 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: 90

Circuit Breaker Pattern

CLOSED

Requests flow normally

OPEN

Requests fail immediately

HALF-OPEN

Limited test requests

Circuit Breaker Flow
Request
Service A β†’ Service B
Success
Circuit CLOSED
Failure > 50%
Circuit OPEN
Timeout
Test β†’ HALF-OPEN
Resilience4j Circuit Breaker Implementation
@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 – Complete 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: 2

Kubernetes 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: 5

Tools & 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

FrameworkBest ForLanguageLearning CurveEcosystem
Spring BootEnterprise Java applicationsJava/KotlinMediumExcellent
Node.jsReal-time, I/O intensive appsJavaScript/TypeScriptEasyExcellent
.NET CoreEnterprise Windows/Linux appsC#MediumGood
GoHigh-performance, concurrent appsGoEasyGrowing
Python FastAPIData-intensive, ML applicationsPythonEasyExcellent

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
Uber’s Event-Driven Architecture
// 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

2 Pizza Team

Small, autonomous teams

Owned Service

Full ownership

API First

Well-defined interfaces

DevOps

You build it, you run it

Ready to Build Microservices?

Download our complete microservices template with Docker, Kubernetes, and monitoring setup.