System Architecture

Deep dive into microservices design and implementation

Architecture Overview

Galaxy E-Commerce Microservices Architecture Client Apps API Gateway :8080 Discovery Service Eureka :8082 Config Server :8888 Customer Service :8081 | MongoDB Product Service :8086 | PostgreSQL Order Service :8084 | PostgreSQL Payment Service :8085 | PostgreSQL Notification Service :8083 | MongoDB Apache Kafka Event Streaming Zipkin Distributed Tracing PostgreSQL Products, Orders, Payments MongoDB Customers, Notifications All services communicate through API Gateway with service discovery via Eureka

Design Patterns Implemented

Service Registry & Discovery

Pattern: Netflix Eureka for dynamic service discovery

Implementation: All microservices register with Eureka on startup. The API Gateway uses Eureka to dynamically route requests to available service instances.

Benefits:

  • No hardcoded service URLs
  • Automatic load balancing
  • Self-healing architecture
  • Easy horizontal scaling
# Services register themselves
@EnableEurekaClient
public class CustomerServiceApplication {
    // Service automatically registers with Eureka
}

API Gateway Pattern

Pattern: Spring Cloud Gateway as single entry point

Implementation: All client requests go through the gateway, which handles routing, load balancing, and cross-cutting concerns.

Benefits:

  • Single entry point for clients
  • Centralized authentication/authorization
  • Request/response logging
  • Rate limiting capabilities

Database per Service

Pattern: Each microservice owns its database

Implementation:

  • Customer, Notification → MongoDB
  • Product, Order, Payment → PostgreSQL
  • No direct database sharing

Benefits:

  • Loose coupling between services
  • Independent scaling
  • Technology diversity (polyglot persistence)
  • Easier to maintain and evolve

Event-Driven Architecture

Pattern: Apache Kafka for asynchronous messaging

Implementation:

  • Order Service publishes OrderConfirmation events
  • Payment Service publishes PaymentConfirmation events
  • Notification Service consumes both events

Benefits:

  • Loose coupling between services
  • Asynchronous processing
  • Event sourcing capabilities
  • Easy to add new consumers
# Event Flow
Order Created → Kafka Topic → Email Notification
Payment Processed → Kafka Topic → Email Notification

Saga Pattern (Orchestration)

Pattern: Order Service orchestrates distributed transaction

Order Flow:

  1. Validate customer exists (Customer Service)
  2. Purchase products, reduce inventory (Product Service)
  3. Create order record
  4. Process payment (Payment Service)
  5. Publish order confirmation event
  6. Send email notifications (async)

Compensation Logic: If any step fails, previous steps can be rolled back

Externalized Configuration

Pattern: Spring Cloud Config Server

Implementation: All configuration stored centrally, services fetch on startup

Benefits:

  • Configuration changes without redeployment
  • Environment-specific configs (dev, prod)
  • Secrets management
  • Version control for configs

Distributed Tracing

Pattern: Zipkin with Micrometer

Implementation: Every request gets a trace ID that propagates across all services

Benefits:

  • End-to-end request visibility
  • Performance bottleneck identification
  • Service dependency mapping
  • Troubleshooting distributed issues

Circuit Breaker (Ready)

Pattern: Infrastructure ready for Resilience4j

Purpose: Prevent cascading failures when services are down

Can be easily added:

  • Circuit breaker for external calls
  • Retry logic for transient failures
  • Bulkhead pattern for resource isolation
  • Timeout configuration

Health Check API

Pattern: Spring Boot Actuator

Implementation: All services expose /actuator/health

Checks:

  • Service availability
  • Database connectivity
  • Disk space
  • Custom health indicators

Inter-Service Communication

Synchronous Communication

Technologies: OpenFeign, RestTemplate

Use Cases:

  • Order → Customer (validate customer)
  • Order → Product (purchase products)
  • Order → Payment (process payment)

When to use: When immediate response is required

@FeignClient(name = "customer-service")
public interface CustomerClient {
    @GetMapping("/api/v1/customers/exists/{id}")
    Boolean existsById(@PathVariable String id);
}

Asynchronous Communication

Technology: Apache Kafka

Use Cases:

  • Order → Notification (order confirmation)
  • Payment → Notification (payment confirmation)

When to use: Fire-and-forget, event notifications, long-running processes

@KafkaListener(topics = "order-topic")
public void consumeOrderConfirmation(
    OrderConfirmation confirmation) {
    // Send email notification
}

Data Management Strategy

PostgreSQL Services

  • Product Service: Product catalog, categories, inventory
  • Order Service: Orders, order lines
  • Payment Service: Payment transactions

Why PostgreSQL? ACID transactions, relational data, complex queries

Migration Tool: Flyway for version-controlled schema changes

MongoDB Services

  • Customer Service: Customer profiles with embedded addresses
  • Notification Service: Notification history

Why MongoDB? Flexible schema, document-based, denormalized data

Advantages: Easy to evolve schema, no migrations needed

Deployment Architecture

Containerization Strategy

  • Every service has a multi-stage Dockerfile
  • Build stage: Maven builds JAR
  • Runtime stage: Lightweight JRE image
  • Optimized image sizes

Docker Compose Orchestration

  • Infrastructure services start first: PostgreSQL, MongoDB, Kafka, Zookeeper
  • Config Server starts next: Provides configuration to all services
  • Discovery Service starts: Service registry
  • Gateway starts: API entry point
  • Business services start: Customer, Product, Order, Payment, Notification
  • Monitoring services: Zipkin, PgAdmin, Mongo Express, MailDev

Service Dependencies

depends_on:
  - config-server
  - discovery
  - postgres
  - mongodb
  - kafka

Production Considerations

  • Kubernetes deployment (Helm charts can be added)
  • Load balancing with Ingress
  • Auto-scaling based on metrics
  • Health checks and readiness probes
  • Secrets management with Kubernetes Secrets
  • Monitoring with Prometheus & Grafana

Technology Decisions

Why Spring Boot 3?

  • Latest stable version with Java 17 support
  • Native compilation support (GraalVM)
  • Improved observability with Micrometer
  • Better security features
  • Performance improvements

Why Apache Kafka?

  • Industry-standard for event streaming
  • High throughput and low latency
  • Horizontal scalability
  • Guaranteed message delivery
  • Event sourcing capabilities

Why Polyglot Persistence?

  • Choose the right database for each use case
  • PostgreSQL for transactional data
  • MongoDB for flexible, document-based data
  • Demonstrates real-world architecture

Why Docker Compose?

  • Easy local development setup
  • Consistent environments
  • Single command to start entire platform
  • Perfect for demos and testing