Unit 2 - Notes

INT363

Unit 2: Evolution of Cloud Microservices

1. Application Architectures: Monolithic & Distributed

To understand the evolution of cloud microservices, one must first understand the architectural paradigms that preceded them.

A. Monolithic Architecture

A monolithic application is built as a single, unified unit. All business logic, database access layers, and UI components are packaged into a single deployable artifact (e.g., a .war file in Java or a single directory in Node.js).

  • Characteristics:

    • Single Codebase: All developers work in the same repository.
    • Tightly Coupled: Components depend heavily on each other; changing one module often requires recompiling the whole application.
    • Centralized Database: Usually connects to a single, large relational database schema.
  • Advantages:

    • Simplicity: Easier to develop, test, and deploy in early stages.
    • Performance: Inter-module communication is in-memory (function calls), which is faster than network calls.
    • Cross-cutting Concerns: Logging, security, and caching are easily handled globally.
  • Disadvantages (The Cloud Problem):

    • Scalability Limits: You must scale the entire application, even if only one module (e.g., "Billing") is experiencing high load.
    • Technology Lock-in: Extremely difficult to change languages or frameworks once development has started.
    • CI/CD Friction: A small change requires a full redeployment of the massive artifact, increasing downtime risks.
    • Single Point of Failure: A memory leak in one module can crash the entire system.

B. Distributed Architecture

Distributed architecture disperses application components across multiple networked computers, which communicate and coordinate their actions by passing messages.

  • Service-Oriented Architecture (SOA): The precursor to microservices. It utilized an Enterprise Service Bus (ESB) for communication, emphasizing reusability via SOAP/XML interfaces.
  • The Shift: Modern distributed systems (Microservices) favor "dumb pipes and smart endpoints" over the heavy logic of ESBs found in SOA.

2. Microservice Fundamentals

Microservices are an architectural style that structures an application as a collection of services that are distinct, autonomous, and focused.

Core Principles

  1. Single Responsibility Principle (SRP): Each service should do one thing and do it well (e.g., an Order Service manages orders, nothing else).
  2. Autonomous: Each service can be developed, deployed, operated, and scaled without affecting other services.
  3. Decentralized Data Management: "Database-per-service." Services do not share database schemas to avoid tight coupling.
  4. Infrastructure Automation: Reliance on CI/CD pipelines, containerization (Docker), and orchestration (Kubernetes) is mandatory due to the complexity of managing many services.

Microservices vs. Monolith Comparison

Feature Monolithic Architecture Microservices Architecture
Coupling Tightly coupled Loosely coupled
Deployment All-or-nothing Independent per service
Scalability Vertical (bigger hardware) or Cloning Horizontal (more instances of specific services)
Technology Single stack (e.g., all Java) Polyglot (Java, Go, Python, Node.js mixed)
Complexity Low architecture complexity, High code complexity High architecture/operational complexity, Low code complexity

3. Microservices Architecture

The architecture focuses on breaking down a system into independent units that communicate over a network.

Architectural Components

  1. Services: The core logic units (e.g., User Service, Product Service).
  2. Communication Protocols:
    • Synchronous: HTTP/REST, gRPC (Request/Response). Best for real-time queries.
    • Asynchronous: AMQP, Kafka, RabbitMQ (Event-driven). Best for decoupling systems and handling high throughput.
  3. Service Discovery: A mechanism (like Netflix Eureka or Consul) allowing services to find each other dynamically without hardcoded IP addresses.
  4. Configuration Server: Centralized management of configuration files (e.g., Spring Cloud Config) so services don't need redeployment to change settings.

The "Share-Nothing" Philosophy

In a pure microservices architecture, services share nothing but APIs.

  • No Shared State: No global variables or shared in-memory caches.
  • No Shared Libraries (Logic): While utility libraries are okay, sharing business logic libraries creates "hidden monoliths."
  • No Shared Database: If Service A needs data from Service B, it must ask Service B via API; it cannot query Service B's database directly.

4. Domain-Driven Design (DDD) Principles

DDD is a software design approach that focuses on modeling software to match a domain (business logic). It is the primary method used to determine how to split a monolith into microservices.

Strategic Design (The "Big Picture")

The goal is to define boundaries between large chunks of the system.

  1. Bounded Context:

    • The central concept in mapping DDD to microservices.
    • A Bounded Context defines a linguistic boundary where a specific domain model applies.
    • Rule of Thumb: One Bounded Context One Microservice.
    • Example: In an e-commerce app, "Product" means something different to the Sales Context (price, image) than it does to the Inventory Context (weight, aisle location). These should be separate services.
  2. Ubiquitous Language:

    • A common language shared by developers and domain experts (business stakeholders).
    • The code (class names, variables) should match the spoken language of the business.
  3. Context Mapping:

    • Defining how different Bounded Contexts relate to each other (e.g., Shared Kernel, Customer/Supplier, Anti-Corruption Layer).

Tactical Design (The "Implementation")

Tools used within a specific microservice/bounded context.

  1. Entities: Objects defined by their identity (e.g., a User with a generic UUID). Even if attributes change, the identity remains.
  2. Value Objects: Objects defined by their attributes, not identity (e.g., an Address or Money). They are immutable.
  3. Aggregates & Aggregate Roots:
    • A cluster of domain objects that can be treated as a single unit.
    • Aggregate Root: The only entity that outside objects are allowed to hold references to.
    • Microservice Rule: Transactions should not span across Aggregates.

5. Service Boundaries and API Gateway

Once the domain is modeled, technical implementation requires defining strict boundaries and managing external access.

A. Service Boundaries

Defining where one service ends and another begins is the hardest part of microservices.

Strategies for defining boundaries:

  1. By Business Capability: Align services with what the business does (e.g., Shipping, Accounting, Marketing).
  2. By Subdomain (DDD): Align services with the complexity of the data models (Core domains vs. Supporting domains).

Coupling and Cohesion:

  • High Cohesion: Things that change together should stay together (inside the same service).
  • Low Coupling: Services should know as little about each other as possible.

B. API Gateway

An API Gateway is a server that acts as a single entry point into the system. It sits between the client apps (Mobile, Web) and the internal microservices.

Why is it needed?
Without a gateway, a client would have to make dozens of requests to different internal IP addresses to render a single page (the "Chatty Client" problem).

Core Functions:

  1. Routing: Forwards requests to the appropriate microservice (e.g., /api/users -> User Service).
  2. Aggregation: Gathers data from multiple services and returns a single response to the client (reduces round-trips).
  3. Cross-Cutting Concerns: Handles logic that every service needs, offloading it from the services themselves:
    • Authentication (OAuth2/JWT verification).
    • SSL Termination.
    • Rate Limiting and Throttling.
    • Logging and Analytics.

Backend for Frontend (BFF) Pattern:
A variation of the API Gateway where you create separate gateways for different client types (e.g., one Gateway for Mobile with a lightweight payload, one for Web with detailed payloads).

Example: API Gateway Routing Logic (Pseudo-code)

YAML
# Example configuration for an API Gateway (like Zuul or Spring Cloud Gateway)
routes:
  - id: product-service
    uri: lb://PRODUCT-SERVICE  # Load balanced URI
    predicates:
      - Path=/api/products/**
  
  - id: order-service
    uri: lb://ORDER-SERVICE
    predicates:
      - Path=/api/orders/**
    filters:
      - AddRequestHeader=X-Request-Foo, Bar
      - CircuitBreaker=name:myCircuitBreaker

Pros and Cons of API Gateway:

  • Pros: Hides internal architecture, simplifies client code, centralized security.
  • Cons: Potential bottleneck, single point of failure (if not clustered), adds latency.