Microservices Architecture

Microservices decompose a large application into small, independent services that communicate over a network. Each service owns its data, runs its own process, and can be deployed independently. This chapter examines when microservices make sense, how to design service boundaries, and the real operational costs.

Monolith vs. Microservices

Monolith

  • Single codebase, single deployment unit.
  • All modules share the same process and database.
  • Simple to develop, test, and deploy initially.
  • Harder to scale individual components.
  • A bug in one module can crash the entire application.
  • Long deployment cycles as the codebase grows.

Microservices

  • Multiple codebases, independently deployable services.
  • Each service has its own database (ideally).
  • Complex to develop, test, and operate.
  • Scale individual services based on their specific load.
  • Failure in one service can be isolated from others.
  • Small, frequent deployments per service.
Monolith vs. Microservices Architecture
Monolith User Module Order Module Payment Module Shared DB Microservices User Service Users DB Order Service Orders DB Payment Svc Payments DB API Gateway

When to Use Microservices

Microservices are not always the right choice. They introduce significant operational complexity. Use microservices when:

  • Your team is large enough that a monolith causes merge conflicts and release bottlenecks.
  • Different parts of the system have vastly different scaling requirements.
  • You need independent deployment cycles for different features.
  • You want to use different technology stacks for different services.
  • You can invest in the operational infrastructure (CI/CD, monitoring, service mesh).
Start Monolith, Extract Later
Martin Fowler's advice: "Don't even consider microservices unless you have a system that's too complex to manage as a monolith." Start with a well-structured monolith. Extract services along natural boundaries when the complexity justifies it.

Service Boundaries

The hardest part of microservices is deciding where to draw the lines. Use Domain-Driven Design (DDD) to find natural boundaries:

  • Bounded Contexts: Each service should correspond to a bounded context: a distinct area of the business with its own models and language.
  • Single Responsibility: Each service should do one thing well.
  • Data Ownership: Each service owns its data exclusively. No shared databases between services.
  • Team Alignment: Service boundaries should align with team boundaries (Conway's Law).

Communication Patterns

Synchronous (Request-Response)

Services call each other via HTTP/REST or gRPC. Simple and intuitive, but creates temporal coupling: the caller is blocked until the callee responds.

Asynchronous (Event-Driven)

Services communicate through message queues or event streams. Decoupled in time: the producer does not wait for the consumer. Preferred for operations that do not need an immediate response.

Service Mesh

A dedicated infrastructure layer (e.g., Istio, Linkerd) that handles service-to-service communication, including discovery, load balancing, encryption, observability, and retry logic. The application code does not need to implement these concerns.

Service Discovery

In a dynamic environment where services scale up and down, how does Service A know where Service B is running?

  • Client-side discovery: The client queries a service registry (e.g., Consul, Eureka) to find available instances and load balances requests itself.
  • Server-side discovery: The client sends requests to a load balancer or API gateway, which queries the registry and routes the request.
  • DNS-based: Services register DNS entries. The calling service resolves the DNS name. Kubernetes services work this way.

API Gateway

An API gateway is a single entry point for all client requests. It routes requests to the appropriate microservice and provides cross-cutting concerns:

  • Request routing and composition
  • Authentication and authorization
  • Rate limiting and throttling
  • SSL termination
  • Response caching
  • Request/response transformation

Data Management

Each microservice should own its database. This means:

  • No direct database sharing between services. Other services access data through the owning service's API.
  • Data consistency across services is eventual, not immediate.
  • Distributed transactions are replaced by Sagas: a sequence of local transactions coordinated through events.

Saga Pattern

A saga is a sequence of transactions where each step has a compensating transaction in case of failure:

  1. Order Service creates an order (pending).
  2. Payment Service charges the customer.
  3. Inventory Service reserves the items.
  4. If any step fails, previous steps execute their compensating actions (refund payment, cancel order).

Challenges of Microservices

  • Distributed system complexity: Network calls can fail, be slow, or return partial results.
  • Data consistency: Maintaining consistency across services is harder than within a single database.
  • Testing: Integration testing across services is complex. Contract testing helps.
  • Debugging: A single user request may traverse many services. Distributed tracing is essential.
  • Operational overhead: Each service needs its own CI/CD pipeline, monitoring, logging, and alerting.
  • Deployment coordination: Breaking API changes require careful versioning and rollout.

Key Takeaways

  • Start with a well-structured monolith. Extract microservices when complexity demands it.
  • Define service boundaries using domain-driven design and bounded contexts.
  • Each service owns its data. No shared databases.
  • Prefer asynchronous communication for loose coupling; use synchronous only when immediate responses are required.
  • Invest heavily in observability: distributed tracing, centralized logging, and service-level metrics.
  • Use an API gateway to centralize cross-cutting concerns.
  • Microservices trade development simplicity for operational complexity. Make sure the trade is worth it.

Chapter Check-Up

Quick quiz to reinforce what you just learned.

Practice What You Learned

Build a microservices architecture with service mesh and API gateway in our guided lab.

Start Guided Lab โ†’