Microservices in System Designing

expert · System Designing

When shifting from a large, monolithic application to a Microservices Architecture , you are choosing to break up a single, unified codebase into a collection of small, loosely coupled, and independently deployable services. Each microservice is built around a specific business capability (e.g., Auth, Catalog, Billing) and operates as an isolated software component. 1. The Core Tenant: Database-per-Service The absolute foundational rule of microservices is that each service owns its own data and logic . Isolation: A service must be the sole owner of its database. The OrderService cannot directly query tables inside the CustomerService database. API Boundaries: If one service needs data owned by another, it must ask for it explicitly via a public network API interface. This encapsulation allows engineering teams to change a service's internal schema, rewrite its code in a different language, or switch its underlying database type (e.g., from Postgres to MongoDB) without breaking any other part of the system. 2. Inter-Service Communication Protocols Because services live on different machines across a network, they must use explicit communication channels to collaborate: REST (HTTP/JSON): The traditional, ubiquitous standard. It is text-based, highly readable, and excellent for client-to-server communication. However, it can be slow and computationally heavy for high-throughput service-to-service calls. gRPC (HTTP/2 / Protocol Buffers): A high-performance, low-latency framework designed by Google. It serializes data into a compact binary format and keeps persistent connections open, making it the ideal choice for synchronous internal backend communication. Asynchronous Events (Message Queues): Utilizing brokers like Kafka or RabbitMQ. Services emit events (e.g., OrderPlaced ), and other services react asynchronously. This removes runtime dependencies, ensuring that if one service goes down, the rest of the application remains functional. 3. The Key Benefit: Independent Deployability In a monolith, if a developer fixes a minor typo in the checkout flow, the entire application must be rebuilt, re-tested, and deployed as a single massive unit. This creates deployment bottlenecks and increases the blast radius of bugs. Microservices offer Independent Deployability : A team managing the RecommendationEngine can push code updates to production 50 times a day without coordinating with, restarting, or interrupting the teams managing the PaymentService . This isolation drastically accelerates feature delivery speed, unlocks true team autonomy, and allows individual components to scale horizontally using targeted auto-scaling groups based on their specific hardware needs. 4. The Hidden Tax: Architectural Challenges While microservices solve organizational scaling problems, they introduce immense infrastructure and operational complexity: A. Distributed Tracing When a user clicks "Checkout" and receives a 500 Internal Server Error , diagnosing the root cause is difficult because the request may have traveled through an API Gateway, an Auth service, an Order service, and a Payment gateway. To solve this, architectures use Distributed Tracing tools (like Jaeger, Zipkin, or OpenTelemetry). The API Gateway injects a unique Correlation ID (Trace ID) into the HTTP header of the initial request. As that request passes from service to service, every component logs its execution time and status using that exact same ID, allowing engineers to visualize the entire request lifecycle in a single unified timeline. B. Service Mesh As the number of microservices grows from 5 to 500, managing service-to-service security (mutual TLS), retries, timeouts, circuit breaking, and metrics inside the application code becomes unmanageable. A Service Mesh (like Istio or Linkerd) injects a lightweight network proxy infrastructure component (often an Envoy Proxy ) right next to every single microservice container (called a Sidecar Pattern ). All network traffic traveling between services goes through these proxies first, allowing platform engineers to configure traffic routing, encryption, and logging centrally without changing a single line of the developers' application code. C. Data Consistency (The Saga Pattern) Because every service has its own database, you can no longer use standard SQL ACID transactions across your system. If a user places an order, you cannot lock the Orders table and the Inventory table simultaneously across two different physical databases. Systems manage this using the Saga Pattern : Instead of an all-or-nothing database lock, a Saga executes a chain of distinct local transactions across multiple services. If the InventoryService fails to reserve stock halfway through the workflow, it emits a failure event that triggers Compensating Transactions backward down the chain (e.g., the OrderService sets the order status to "Cancelled" and the BillingService issues a refund), manually restoring consistency to the ecosystem.

Back to System Designing

Browse all study material on Careeroza