Skip to content
IRC-Coding IRC-Coding
Software Architecture Monolith Microservices API Gateway Service Mesh Container Orchestration

Monolith vs Microservices: Architecture Guide

Compare monolithic and microservices architectures. Learn API Gateway, Service Mesh, containers, and orchestration best practices.

S

schutzgeist

2 min read

Software Architecture: Monolith vs Microservices with API Gateway & Service Mesh

This article is a comprehensive introduction to Software Architecture – including monolith vs microservices with API Gateway, Service Mesh, containers and orchestration with practical examples.

In a Nutshell

Monolith architecture is a single, cohesive code block, while microservices are small, independent services. API Gateway coordinates communication, Service Mesh manages microservice interactions.

Compact Technical Description

Software Architecture is the fundamental structure of a software system, consisting of components, their relationships, and principles for design and evolution.

Architecture Patterns:

Monolith Architecture

  • Concept: Unified application in a single codebase
  • Deployment: Single deployment of the entire application
  • Communication: Direct method calls within the application
  • Advantages: Simple development, deployment, debugging
  • Disadvantages: Scaling challenges, technology constraints

Microservice Architecture

  • Concept: Small, autonomous services with their own responsibility
  • Deployment: Independent deployment capability
  • Communication: Network-based API calls
  • Advantages: Scalability, technology flexibility, resilience
  • Disadvantages: Complexity, network latency, distributed complexity

API Gateway

  • Concept: Central entry point for client requests
  • Functions: Routing, authentication, rate limiting, load balancing
  • Advantages: Centralized management, security, monitoring
  • Implementation: Kong, NGINX, AWS API Gateway

Service Mesh

  • Concept: Infrastructure layer for microservice communication
  • Functions: Service discovery, load balancing, circuit breaking
  • Implementation: Istio, Linkerd, Consul Connect
  • Advantages: Transparency, observability, security

Exam-Relevant Key Points

  • Software Architecture: Fundamental structure of software systems
  • Monolith: Unified application with shared codebase
  • Microservices: Small, independent services with their own lifecycle
  • API Gateway: Central entry point for client communication
  • Service Mesh: Infrastructure for microservice interactions
  • Containers: Virtualization at operating system level
  • Orchestration: Automated management of containers
  • IHK-relevant: Modern architecture decisions and patterns

Core Components

  1. Architecture Decision: Monolith vs Microservices
  2. API Management: Gateway, routing, security
  3. Service Discovery: Automatic service discovery
  4. Load Balancing: Distribution of requests
  5. Circuit Breaking: Protection against cascading failures
  6. Containerization: Docker, Podman
  7. Orchestration: Kubernetes, Docker Swarm
  8. Monitoring: Logging, metrics, tracing

Practical Examples

1. Monolith Architecture with Java Spring Boot

package com.example.monolith;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.*;
import java.util.List;
import java.util.Optional;

// Main application
@SpringBootApplication
public class MonolithApplication {
    public static void main(String[] args) {
        SpringApplication.run(MonolithApplication.class, args);
    }
}

// User Entity
@Entity
@Table(name = "users")
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true)
    private String username;
    
    @Column(nullable = false)
    private String email;
    
    @Column(nullable = false)
    private String password;
    
    // Getters and setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

// Order Entity
@Entity
@Table(name = "orders")
class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
    
    @Column(nullable = false)
    private String product;
    
    @Column(nullable = false)
    private Double amount;
    
    @Column(nullable = false)
    private String status;
    
    // Getters and setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public User getUser() { return user; }
    public void setUser(User user) { this.user = user; }
    
    public String getProduct() { return product; }
    public void setProduct(String product) { this.product = product; }
    
    public Double getAmount() { return amount; }
    public void setAmount(Double amount) { this.amount = amount; }
    
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
}

// Repository Interfaces
interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
    boolean existsByUsername(String username);
}

interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByUserId(Long userId);
    List<Order> findByStatus(String status);
}

// Business Logic Services
@Service
class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public User createUser(User user) {
        if (userRepository.existsByUsername(user.getUsername())) {
            throw new IllegalArgumentException("Username already exists");
        }
        return userRepository.save(user);
    }
    
    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }
    
    public Optional<User> getUserByUsername(String username) {
        return userRepository.findByUsername(username);
    }
    
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
    
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

@Service
class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private UserService userService;
    
    public Order createOrder(Long userId, String product, Double amount) {
        Optional<User> userOpt = userService.getUserById(userId);
        if (!userOpt.isPresent()) {
            throw new IllegalArgumentException("User not found");
        }
        
        Order order = new Order();
        order.setUser(userOpt.get());
        order.setProduct(product);
        order.setAmount(amount);
        order.setStatus("PENDING");
        
        return orderRepository.save(order);
    }
    
    public Optional<Order> getOrderById(Long id) {
        return orderRepository.findById(id);
    }
    
    public List<Order> getOrdersByUserId(Long userId) {
        return orderRepository.findByUserId(userId);
    }
    
    public Order updateOrderStatus(Long id, String status) {
        Optional<Order> orderOpt = orderRepository.findById(id);
        if (!orderOpt.isPresent()) {
            throw new IllegalArgumentException("Order not found");
        }
        
        Order order = orderOpt.get();
        order.setStatus(status);
        return orderRepository.save(order);
    }
    
    public void deleteOrder(Long id) {
        orderRepository.deleteById(id);
    }
}

// REST Controllers
@RestController
@RequestMapping("/api/users")
class UserController {
    @Autowired
    private UserService userService;
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id)
            .orElseThrow(() -> new IllegalArgumentException("User not found"));
    }
    
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
    
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

@RestController
@RequestMapping("/api/orders")
class OrderController {
    @Autowired
    private OrderService orderService;
    
    @PostMapping
    public Order createOrder(@RequestBody CreateOrderRequest request) {
        return orderService.createOrder(request.getUserId(), request.getProduct(), request.getAmount());
    }
    
    @GetMapping("/{id}")
    public Order getOrder(@PathVariable Long id) {
        return orderService.getOrderById(id)
            .orElseThrow(() -> new IllegalArgumentException("Order not found"));
    }
    
    @GetMapping("/user/{userId}")
    public List<Order> getOrdersByUser(@PathVariable Long userId) {
        return orderService.getOrdersByUserId(userId);
    }
    
    @PutMapping("/{id}/status")
    public Order updateOrderStatus(@PathVariable Long id, @RequestBody String status) {
        return orderService.updateOrderStatus(id, status);
    }
    
    @DeleteMapping("/{id}")
    public void deleteOrder(@PathVariable Long id) {
        orderService.deleteOrder(id);
    }
}

// DTOs
class CreateOrderRequest {
    private Long userId;
    private String product;
    private Double amount;
    
    // Getters and setters
    public Long getUserId() { return userId; }
    public void setUserId(Long userId) { this.userId = userId; }
    
    public String getProduct() { return product; }
    public void setProduct(String product) { this.product = product; }
    
    public Double getAmount() { return amount; }
    public void setAmount(Double amount) { this.amount = amount; }
}

2. Microservice-Architektur mit Spring Cloud

// User Microservice
package com.example.userservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.*;
import java.util.List;
import java.util.Optional;

@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

// User Entity (vereinfacht)
@Entity
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String email;
    
    // Getters und Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

@Service
class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public User createUser(User user) {
        return userRepository.save(user);
    }
    
    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }
    
    public Optional<User> getUserByUsername(String username) {
        return userRepository.findByUsername(username);
    }
    
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
}

@RestController
@RequestMapping("/api/users")
class UserController {
    @Autowired
    private UserService userService;
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id)
            .orElseThrow(() -> new RuntimeException("User not found"));
    }
    
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
}

// Order Microservice
package com.example.orderservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.*;
import java.util.List;
import java.util.Optional;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

// Feign Client für User Service
@FeignClient(name = "user-service")
interface UserServiceClient {
    @GetMapping("/api/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

// Order Entity
@Entity
class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private Long userId;
    private String product;
    private Double amount;
    private String status;
    
    // Getters und Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public Long getUserId() { return userId; }
    public void setUserId(Long userId) { this.userId = userId; }
    
    public String getProduct() { return product; }
    public void setProduct(String product) { this.product = product; }
    
    public Double getAmount() { return amount; }
    public void setAmount(Double amount) { this.amount = amount; }
    
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
}

interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByUserId(Long userId);
}

@Service
class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private UserServiceClient userServiceClient;
    
    public Order createOrder(Order order) {
        // User validation via microservice call
        try {
            User user = userServiceClient.getUser(order.getUserId());
            if (user == null) {
                throw new RuntimeException("User not found");
            }
        } catch (Exception e) {
            throw new RuntimeException("User service unavailable");
        }
        
        order.setStatus("PENDING");
        return orderRepository.save(order);
    }
    
    public Optional<Order> getOrderById(Long id) {
        return orderRepository.findById(id);
    }
    
    public List<Order> getOrdersByUserId(Long userId) {
        return orderRepository.findByUserId(userId);
    }
    
    public Order updateOrderStatus(Long id, String status) {
        Optional<Order> orderOpt = orderRepository.findById(id);
        if (!orderOpt.isPresent()) {
            throw new RuntimeException("Order not found");
        }
        
        Order order = orderOpt.get();
        order.setStatus(status);
        return orderRepository.save(order);
    }
}

@RestController
@RequestMapping("/api/orders")
class OrderController {
    @Autowired
    private OrderService orderService;
    
    @PostMapping
    public Order createOrder(@RequestBody Order order) {
        return orderService.createOrder(order);
    }
    
    @GetMapping("/{id}")
    public Order getOrder(@PathVariable Long id) {
        return orderService.getOrderById(id)
            .orElseThrow(() -> new RuntimeException("Order not found"));
    }
    
    @GetMapping("/user/{userId}")
    public List<Order> getOrdersByUser(@PathVariable Long userId) {
        return orderService.getOrdersByUserId(userId);
    }
    
    @PutMapping("/{id}/status")
    public Order updateOrderStatus(@PathVariable Long id, @RequestBody String status) {
        return orderService.updateOrderStatus(id, status);
    }
}

3. API Gateway mit Spring Cloud Gateway

package com.example.apigateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

@SpringBootApplication
public class ApiGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
    
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            // User Service Routes
            .route("user-service", r -> r.path("/api/users/**")
                .uri("lb://user-service"))
            
            // Order Service Routes
            .route("order-service", r -> r.path("/api/orders/**")
                .uri("lb://order-service"))
            
            // Product Service Routes
            .route("product-service", r -> r.path("/api/products/**")
                .uri("lb://product-service"))
            
            // Authentication Service Routes
            .route("auth-service", r -> r.path("/api/auth/**")
                .uri("lb://auth-service"))
            
            .build();
    }
}

// Authentication Filter
@Component
class AuthenticationFilter implements GlobalFilter, Ordered {
    
    @Override
    public Mono<Void> filter(ServerHttpRequest request, GatewayFilterChain chain) {
        String path = request.getURI().getPath();
        
        // Public Endpoints without Authentication
        if (path.startsWith("/api/auth/login") || 
            path.startsWith("/api/auth/register") ||
            path.startsWith("/actuator")) {
            return chain.filter(request);
        }
        
        // Extract token from header
        String authHeader = request.getHeaders().getFirst("Authorization");
        
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            return Mono.error(new RuntimeException("Missing or invalid token"));
        }
        
        String token = authHeader.substring(7);
        
        // Validate token (simplified)
        if (!isValidToken(token)) {
            return Mono.error(new RuntimeException("Invalid token"));
        }
        
        // Add user information to request
        ServerHttpRequest modifiedRequest = request.mutate()
            .header("X-User-ID", getUserIdFromToken(token))
            .header("X-User-Role", getUserRoleFromToken(token))
            .build();
        
        return chain.filter(modifiedRequest);
    }
    
    private boolean isValidToken(String token) {
        // Token validation (simplified)
        return token != null && !token.isEmpty();
    }
    
    private String getUserIdFromToken(String token) {
        // Extract user ID from token
        return "123"; // Example
    }
    
    private String getUserRoleFromToken(String token) {
        // Extract user role from token
        return "USER"; // Example
    }
    
    @Override
    public int getOrder() {
        return -100; // High priority
    }
}

// Rate Limiting Filter
@Component
class RateLimitingFilter implements GlobalFilter, Ordered {
    
    private final Map<String, Map<String, Integer>> rateLimitMap = new ConcurrentHashMap<>();
    private static final int RATE_LIMIT = 100; // 100 requests per minute
    private static final int TIME_WINDOW = 60; // seconds
    
    @Override
    public Mono<Void> filter(ServerHttpRequest request, GatewayFilterChain chain) {
        String clientId = getClientId(request);
        String currentTimeWindow = getCurrentTimeWindow();
        
        rateLimitMap.putIfAbsent(clientId, new ConcurrentHashMap<>());
        Map<String, Integer> clientRequests = rateLimitMap.get(clientId);
        
        // Remove old time windows
        clientRequests.entrySet().removeIf(entry -> 
            !entry.getKey().equals(currentTimeWindow));
        
        // Increment current counter
        int currentCount = clientRequests.getOrDefault(currentTimeWindow, 0);
        
        if (currentCount >= RATE_LIMIT) {
            return Mono.error(new RuntimeException("Rate limit exceeded"));
        }
        
        clientRequests.put(currentTimeWindow, currentCount + 1);
        return chain.filter(request);
    }
    
    private String getClientId(ServerHttpRequest request) {
        // Extract client ID from IP or token
        return request.getRemoteAddress() != null ? 
            request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
    }
    
    private String getCurrentTimeWindow() {
        return String.valueOf(System.currentTimeMillis() / (TIME_WINDOW * 1000));
    }
    
    @Override
    public int getOrder() {
        return -99; // After Authentication
    }
}

// Load Balancing Configuration
@Component
class LoadBalancerConfig {
    
    @Bean
    public ReactorLoadBalancer<ServiceInstance> userServiceLoadBalancer(
            Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RoundRobinLoadBalancer(
            loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
            name);
    }
}

// Circuit Breaker Configuration
@Component
class CircuitBreakerConfig {
    
    @Bean
    public CircuitBreaker circuitBreaker() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            .failureRateThreshold(50)
            .waitDurationInOpenState(Duration.ofMillis(1000))
            .slidingWindowSize(20)
            .minimumNumberOfCalls(10)
            .build();
        
        return CircuitBreaker.of("myCircuitBreaker", config);
    }
}

// API Gateway Controller für Health Checks
@RestController
@RequestMapping("/gateway")
class GatewayController {
    
    @GetMapping("/health")
    public Map<String, String> health() {
        Map<String, String> status = new HashMap<>();
        status.put("status", "UP");
        status.put("service", "api-gateway");
        status.put("timestamp", Instant.now().toString());
        return status;
    }
    
    @GetMapping("/routes")
    public Map<String, Object> getRoutes() {
        Map<String, Object> routes = new HashMap<>();
        routes.put("user-service", "/api/users/**");
        routes.put("order-service", "/api/orders/**");
        routes.put("product-service", "/api/products/**");
        routes.put("auth-service", "/api/auth/**");
        return routes;
    }
}

4. Docker Container for Microservices

# Dockerfile für User Service
FROM openjdk:11-jre-slim

# Working Directory
WORKDIR /app

# Application JAR kopieren
COPY target/user-service-0.0.1-SNAPSHOT.jar app.jar

# Expose Port
EXPOSE 8081

# Environment Variables
ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENV SPRING_PROFILES_ACTIVE=docker

# Health Check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8081/actuator/health || exit 1

# Application starten
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
# Dockerfile für Order Service
FROM openjdk:11-jre-slim

WORKDIR /app

COPY target/order-service-0.0.1-SNAPSHOT.jar app.jar

EXPOSE 8082

ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENV SPRING_PROFILES_ACTIVE=docker

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8082/actuator/health || exit 1

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
# Dockerfile für API Gateway
FROM openjdk:11-jre-slim

WORKDIR /app

COPY target/api-gateway-0.0.1-SNAPSHOT.jar app.jar

EXPOSE 8080

ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENV SPRING_PROFILES_ACTIVE=docker

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8080/gateway/health || exit 1

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

5. Kubernetes Orchestration

# Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: microservices

---
# User Service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: microservices
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: user-service:latest
        ports:
        - containerPort: 8081
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "kubernetes"
        - name: SPRING_DATASOURCE_URL
          value: "jdbc:postgresql://postgres-service:5432/userdb"
        - name: SPRING_DATASOURCE_USERNAME
          value: "postgres"
        - name: SPRING_DATASOURCE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8081
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8081
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

---
# User Service Service
apiVersion: v1
kind: Service
metadata:
  name: user-service
  namespace: microservices
spec:
  selector:
    app: user-service
  ports:
  - protocol: TCP
    port: 8081
    targetPort: 8081
  type: ClusterIP

---
# Order Service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: microservices
spec:
  replicas: 2
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-service
        image: order-service:latest
        ports:
        - containerPort: 8082
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "kubernetes"
        - name: USER_SERVICE_URL
          value: "http://user-service:8081"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8082
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8082
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

---
# Order Service Service
apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: microservices
spec:
  selector:
    app: order-service
  ports:
  - protocol: TCP
    port: 8082
    targetPort: 8082
  type: ClusterIP

---
# API Gateway Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-gateway
  namespace: microservices
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api-gateway
  template:
    metadata:
      labels:
        app: api-gateway
    spec:
      containers:
      - name: api-gateway
        image: api-gateway:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "kubernetes"
        livenessProbe:
          httpGet:
            path: /gateway/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /gateway/health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

---
# API Gateway Service
apiVersion: v1
kind: Service
metadata:
  name: api-gateway
  namespace: microservices
spec:
  selector:
    app: api-gateway
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 8080
  type: LoadBalancer

---
# Horizontal Pod Autoscaler for User Service
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: user-service-hpa
  namespace: microservices
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: user-service
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

---
# ConfigMap for Configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: microservices-config
  namespace: microservices
data:
  application.yml: |
    spring:
      cloud:
        kubernetes:
          discovery:
            enabled: true
          config:
            enabled: true
    management:
      endpoints:
        web:
          exposure:
            include: health,info,metrics,prometheus
      endpoint:
        health:
          show-details: always

---
# Secret for Sensitive Data
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
  namespace: microservices
type: Opaque
data:
  password: cGFzc3dvcmQxMjM=  # Base64 encoded "password123"
  username: cG9zdGdyZXM=      # Base64 encoded "postgres"

6. Service Mesh with Istio

# Istio Gateway
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: microservices-gateway
  namespace: microservices
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

---
# Virtual Service for API Gateway
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-gateway-vs
  namespace: microservices
spec:
  hosts:
  - "*"
  gateways:
  - microservices-gateway
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: api-gateway
        port:
          number: 8080

---
# Destination Rules for Load Balancing
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: user-service-dr
  namespace: microservices
spec:
  host: user-service
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 50
        maxRequestsPerConnection: 10
    circuitBreaker:
      consecutiveErrors: 3
      interval: 30s
      baseEjectionTime: 30s

---
# Service Entry for External Services
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-api
  namespace: microservices
spec:
  hosts:
  - external-api.example.com
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS

---
# Authorization Policy
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-api-access
  namespace: microservices
spec:
  selector:
    matchLabels:
      app: api-gateway
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"]
  - to:
    - operation:
        methods: ["GET", "POST", "PUT", "DELETE"]

---
# Request Authentication
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: jwt-auth
  namespace: microservices
spec:
  selector:
    matchLabels:
      app: api-gateway
  jwtRules:
  - issuer: "https://auth.example.com"
    jwksUri: "https://auth.example.com/.well-known/jwks.json"
    forwardOriginalToken: true

Architecture Comparison

Monolith vs Microservices

CriterionMonolithMicroservices
ComplexityLowHigh
DeploymentSimpleComplex
ScalingVerticalHorizontal
TechnologyUniformFlexible
Error IsolationPoorGood
Team StructureCentralizedDistributed
DevelopmentFastSlower

Deployment Strategies

StrategyDescriptionAdvantagesDisadvantages
Big BangEverything at onceSimpleHigh-risk
Blue-GreenParallel systemsZero-downtimeResources
CanaryGradualSafeComplex
RollingGradualEfficientSlow

Container Technologies

Docker vs Podman

FeatureDockerPodman
DaemonYesNo
RootlessLimitedFull
PodsNoYes
DockerfileYesYes
ComposeYesLimited

Container Registry

RegistryFeaturesPrice
Docker HubPublic/PrivateFreemium
GitHub PackagesIntegrationFree
AWS ECRCloud-nativePay-per-use
Google GCRCloud-nativePay-per-use

Orchestration Platforms

Kubernetes vs Docker Swarm

FeatureKubernetesDocker Swarm
ComplexityHighLow
ScalingAutoManual
Service MeshYesNo
Learning CurveSteepFlat
CommunityLargeMedium

Service Mesh Implementations

Istio vs Linkerd

FeatureIstioLinkerd
ComplexityHighLow
FeaturesComprehensiveFocused
PerformanceGoodVery good
ResourcesHighLow
IntegrationComprehensiveSimple

API Gateway Features

Core Functions

  • Routing: Direct requests to correct services
  • Authentication: JWT, OAuth2, API Keys
  • Authorization: Role-based access
  • Rate Limiting: Request limits per client
  • Load Balancing: Distribute load
  • Caching: Response caching
  • Logging: Request/Response logging

Advanced Features

  • Circuit Breaking: Protection from failures
  • Retry Logic: Automatic retries
  • Request/Response Transformation: Data transformation
  • API Versioning: Version management
  • Analytics: Usage statistics

Monitoring & Observability

Three Pillars of Observability

  1. Metrics: Quantitative data (Prometheus, Grafana)
  2. Logging: Event-based data (ELK Stack, Fluentd)
  3. Tracing: Request tracing (Jaeger, Zipkin)

Tools in Microservice Context

# Prometheus Monitoring
apiVersion: v1
kind: ServiceMonitor
metadata:
  name: user-service-monitor
  namespace: microservices
spec:
  selector:
    matchLabels:
      app: user-service
  endpoints:
  - port: http
    path: /actuator/prometheus
    interval: 30s

Best Practices

Microservice Design

  • Single Responsibility: One service, one task
  • Database per Service: Separate database per service
  • Async Communication: Event-based communication
  • Circuit Breaking: Protection from cascade failures
  • Health Checks: Monitor service health

Container Best Practices

# Best Practice Dockerfile
FROM alpine:latest

# Non-root User
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup

USER appuser

# Minimal Image
COPY --from=builder /app/target/app.jar /app/app.jar

WORKDIR /app

EXPOSE 8080

# Health Check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["java", "-jar", "app.jar"]

Advantages and Disadvantages

Advantages of Microservices

  • Scalability: Independent scaling per service
  • Flexibility: Different technologies possible
  • Resilience: Failure of one service does not affect others
  • Team Autonomy: Independent development teams
  • Faster Deployment: Small, frequent deployments

Disadvantages of Microservices

  • Complexity: Distributed systems are complex
  • Network Latency: Communication over network
  • Data Consistency: Distributed transactions
  • Monitoring: More complex monitoring
  • Debugging: Difficult troubleshooting

Common Exam Questions

  1. What are the main differences between monolith and microservices? Monolith is a single unit, microservices are small, independent services with their own responsibility.

  2. Explain the role of an API Gateway! API Gateway is the central entry point for client requests with routing, authentication, and rate limiting.

  3. When do you use containers and when orchestration? Containers for isolation and portability, orchestration for automated management of many containers.

  4. What is Service Mesh and why is it needed? Service Mesh is an infrastructure layer for microservice communication with service discovery and security.

Key Sources

  1. https://microservices.io/
  2. https://kubernetes.io/
  3. https://istio.io/
  4. https://spring.io/projects/spring-cloud
Back to Blog
Share:

Related Posts