Microservices is an architectural style that structures an application as a collection of loosely coupled, independently deployable services organized around business capabilities.

Monolith vs Microservices

  Monolith:                     Microservices:
┌─────────────────────┐       ┌──────┐ ┌──────┐ ┌──────┐
│  User │ Order │ Pay  │       │ User │ │Order │ │ Pay  │
│  Module│Module│Module│       │ Svc  │ │ Svc  │ │ Svc  │
└─────────────────────┘       └──┬───┘ └──┬───┘ └──┬───┘
     Single deployment              Independent deployments
  
Aspect Monolith Microservices
Deployment All-or-nothing Independent
Scaling Scale entire app Scale individual services
Technology Single stack Polyglot possible
Complexity Lower (initially) Higher (distributed)
Team structure Single team Team per service
Data Shared database Database per service

Key Principles

  1. Single responsibility — each service owns one business domain
  2. Decentralized data — each service has its own database
  3. Independent deployment — deploy without affecting others
  4. Failure isolation — one service failure doesn’t cascade
  5. Design for failure — circuit breakers, retries, fallbacks

When to Use Microservices

Good fit:

  • Large team (Conway’s Law — architecture mirrors org structure)
  • Different scaling requirements per domain
  • Need for independent deployment cycles
  • Polyglot requirements (different languages per service)

Poor fit:

  • Small team (< 10 developers)
  • Simple domain with low traffic
  • Early-stage startup (premature decomposition)
  • Strong transactional requirements across domains

Service Boundaries

Define by business capability, not technical layer:

  Good:                          Bad:
User Service                   Controller Service
Order Service                  Service Layer Service
Payment Service                Repository Service
Inventory Service              Database Service
  

Use Domain-Driven Design (DDD) bounded contexts to identify boundaries.

Infrastructure Components

                      ┌─────────────┐
                    │ API Gateway │
                    └──────┬──────┘
           ┌───────────────┼───────────────┐
           ▼               ▼               ▼
    ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
    │ User Svc    │ │ Order Svc   │ │ Product Svc │
    └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
           │               │               │
    ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
    │ User DB     │ │ Order DB    │ │ Product DB  │
    └─────────────┘ └─────────────┘ └─────────────┘

    Supporting: Service Discovery, Config Server, Message Broker,
                Distributed Tracing, Centralized Logging
  

Challenges

Challenge Solution
Distributed transactions Saga pattern, eventual consistency
Service discovery Eureka, Consul, Kubernetes DNS
Configuration Spring Cloud Config, etcd
Monitoring Prometheus, Grafana, Zipkin
Data consistency Event sourcing, CQRS
Network latency Caching, async messaging
Testing Contract testing (Pact), test containers

Migration Strategy

Don’t rewrite — evolve gradually:

  1. Strangler Fig — new features as microservices, old code remains
  2. Extract service — pull one module out of monolith
  3. Branch by abstraction — interface in monolith, implementation in new service
  4. Database first — separate schemas before separating services

Best Practices

  • Start with a modular monolith — extract services when boundaries are clear
  • Invest in observability from day one (logging, metrics, tracing)
  • Automate everything — CI/CD, testing, deployment
  • Design for failure — assume network calls will fail
  • Keep services small but not too small (avoid nanoservices)