Unit 2: Evolution of Cloud Microservices

INT363 — Cloud Microservices 9 min read

I. Orientation — From Unified Applications to Autonomous Services

Cloud microservices evolved from the need to change, deploy, and scale parts of an application independently. The governing principle is decomposition around business capabilities, supported by automated delivery, elastic cloud infrastructure, and communication through explicit service interfaces.

  • Core unit: A microservice is a small, independently deployable software component that owns a focused business capability, such as Payment, Inventory, or Shipping.
  • Independence: A service should be buildable, testable, deployable, and scalable without requiring coordinated release of the entire system.
  • Explicit communication: Services interact through contracts such as HTTP/REST APIs, gRPC operations, or asynchronous messages.
  • Data ownership: Each service normally controls its own data and exposes that data through APIs or events rather than shared database tables.
  • Cloud alignment: Containers, orchestration, managed databases, monitoring, and on-demand scaling make distributed services operationally practical.
  • Trade-off: Microservices reduce organizational and deployment coupling but introduce network failures, distributed data consistency, observability, and security challenges.
  • Evolutionary character: Microservices are not simply smaller programs; they combine architectural boundaries, decentralized ownership, and continuous delivery.

II. Application Architectures — Structural Models for Software Systems

Application architecture determines how responsibilities, code, data, and deployment units are organized. Monolithic and distributed architectures differ primarily in whether components execute and evolve as one unit or as multiple communicating processes.

A. Application Architectures—Monolithic and Distributed

The central distinction is that a monolith packages major capabilities together, whereas a distributed architecture separates them across networked components.

  1. Monolithic architecture

    • Structure: User interface, business logic, and data-access code are commonly packaged as one application, such as a single Java WAR file or .NET executable.
    • Deployment: Changing one module usually requires rebuilding and redeploying the complete application, even if only OrderService changed.
    • Advantages:
      • Local function calls are fast and reliable compared with network requests.
      • Transactions can use one database and ACID guarantees.
      • Testing, debugging, and deployment are initially straightforward.
    • Limitations:
      • Modules can become tightly coupled through shared classes and tables.
      • Scaling duplicates the whole application rather than only the overloaded capability.
      • A defective release may affect every function in the deployment.
    • Modular monolith: Strong internal module boundaries can preserve simplicity while avoiding an unstructured “big ball of mud.”
  2. Distributed architecture

    • Structure: Capabilities run in separate processes or machines and communicate through HTTP, RPC, queues, or event streams.
    • Advantages:
      • Catalog can scale to 20 instances while Billing remains at 2.
      • Services may be released independently and use technology suited to their workloads.
      • Failures can be isolated when timeouts, retries, and circuit breakers are applied.
    • Limitations:
      • A network call may fail, time out, duplicate a request, or return late.
      • Cross-service transactions require patterns such as sagas rather than one database transaction.
      • Logs, metrics, and traces must correlate activity across multiple processes.
    • Key contrast: A monolith has mainly in-process complexity; a distributed system exchanges some of that for coordination and operational complexity.

III. Microservice Fundamentals — Essential Properties and Trade-offs

A microservice is an independently deployable service organized around a cohesive business capability. Its size is determined by responsibility and autonomy, not by a fixed number of classes or lines of code.

A. Microservice Fundamentals

Microservice fundamentals define the conditions under which service decomposition produces genuine independence rather than a fragmented monolith.

  • Single business capability: A service should have a focused purpose, such as calculating prices rather than managing pricing, shipping, and accounts together.
  • Independent deployment: A new version of Pricing should not force simultaneous deployment of Catalog; versioned contracts support this independence.
  • Loose coupling: Consumers depend on an interface such as GET /products/{id}, not on the provider’s internal classes or tables.
  • High cohesion: Related behavior and data remain together; price rules and price history belong naturally within the pricing capability.
  • Decentralized data: Database-per-service prevents another service from bypassing business rules with direct SQL access.
  • Automation: Continuous integration, automated tests, container images, and deployment pipelines make frequent releases manageable.
  • Resilience: Timeouts, bounded retries, bulkheads, idempotency keys, and circuit breakers address partial failure.
  • Observability: A correlation identifier such as traceId=8f21 links gateway, order, and payment activity across logs and traces.
  • Cost: More services mean more deployments, endpoints, credentials, telemetry, and failure modes; microservices are therefore unsuitable when operational maturity is low.

IV. Microservices Architecture — Organization and Interaction of Services

Microservices architecture is a distributed style in which autonomous services collaborate to implement an application. The architecture includes not only services, but also communication, discovery, configuration, security, data management, and operations.

A. Microservices Architecture

A successful architecture controls dependencies while allowing each service to evolve and operate independently.

  • Service layer: Each service contains its business logic and exposes an API or consumes events; examples include Order, Payment, and Inventory.
  • Communication styles:
    • Synchronous: REST or gRPC provides an immediate response but couples availability and latency between caller and provider.
    • Asynchronous: A message such as OrderPlaced permits delayed processing and temporal decoupling.
  • Service discovery: A logical name such as inventory-service is resolved to a healthy instance by a registry, DNS, or orchestration platform.
  • Configuration: Environment-specific values—database addresses, queue names, or feature flags—remain outside the application image.
  • Data consistency: A saga coordinates local transactions. For example, an order may reserve stock, authorize payment, and confirm shipment; a payment failure triggers stock release.
  • Reliability controls: A request may use a 2 s timeout, limited retries with backoff, and a circuit breaker that stops calls to an unhealthy dependency.
  • Operational platform: Kubernetes can schedule containers, restart failed instances, expose services, and scale replicas from measured load.
  • Security: Transport encryption, service identities, token validation, authorization, and secret rotation must apply to every communication path.
  • Architectural risk: Excessive synchronous call chains—Gateway → Order → Customer → Credit → Database—increase latency and create cascading failure.

V. Domain-Driven Design — Modeling Software Around Business Meaning

Domain-driven design, associated with Eric Evans’s 2003 work, aligns software structure with the concepts and language of a business domain. It helps identify services by modeling meaningful capabilities rather than dividing systems by technical layers.

A. Domain-driven design (DDD) principles

DDD provides strategic and tactical concepts for creating models that remain valid within clearly defined business contexts.

  • Domain: The subject area addressed by the software, such as retail banking, healthcare scheduling, or logistics.
  • Ubiquitous language: Developers and domain experts use shared terms in conversation, models, and code; Shipment, for example, should have one agreed meaning within a context.
  • Bounded context: A model is valid inside an explicit boundary. Customer in Sales may mean a buyer, while in Support it may mean a person holding a service contract.
  • Context mapping: Relationships between contexts are documented, including upstream/downstream dependencies and translation requirements.
  • Entity: An object has continuity through identity; an Order remains the same order when its status changes because orderId is stable.
  • Value object: Meaning comes from attributes rather than identity; Money(100, "USD") can be immutable and compared by value.
  • Aggregate: A consistency boundary groups entities and value objects under an aggregate root. An Order root can enforce that an order cannot be confirmed without at least one line item.
  • Domain event: A past-tense fact such as PaymentAuthorized communicates a meaningful state change.
  • Repository: An abstraction retrieves and stores aggregates without exposing database details.
  • Microservice connection: A bounded context is a strong service candidate, but not an automatic one; deployment needs, team ownership, workload, and coupling must also be evaluated.

VI. Service Boundaries — Deciding Where One Service Ends

A service boundary defines the behavior, data, language, and change responsibility owned by a service. Good boundaries minimize coordinated change while preserving business consistency.

A. Service boundaries

Service boundaries should follow cohesive business responsibilities rather than arbitrary code size, database tables, or technical layers.

  • Business capability criterion: Inventory should own stock availability and reservation rules, while Payment should own authorization and refund rules.
  • Change coupling: Components that repeatedly change together may belong in the same service; constant coordinated releases indicate a poor boundary.
  • Data ownership: The service that enforces a business invariant owns the relevant data. Only Inventory should directly update available_quantity.
  • Transactional boundary: Rules requiring immediate atomic consistency are easier inside one service; cross-service invariants require sagas, compensation, or eventual consistency.
  • Team ownership: A team should be able to understand and operate its service end to end, including code, deployment, alerts, and production support.
  • Interface design: Contracts should expose business operations such as reserveStock() rather than internal CRUD details such as direct updates to inventory rows.
  • Granularity risks:
    • Too large: Independent scaling and deployment are lost.
    • Too small: Network traffic, duplicated logic, and operational overhead increase.
  • Boundary evolution: Boundaries may change as domain knowledge improves; a modular monolith can provide a safer starting point before extracting independently valuable services.
  • Practical indicator: If one user request requires numerous chatty calls between two services, their responsibilities may be incorrectly separated.

VII. API Gateway — Controlled Entry to Microservices

An API gateway is a reverse-proxy component that provides clients with a managed entry point to backend services. It hides internal topology and centralizes selected cross-cutting policies.

A. API gateway

The gateway routes external requests while handling concerns that should not be duplicated in every client or service.

  • Routing: A public path is mapped to an internal destination.
TEXT
/api/orders/*  -> order-service
/api/items/*   -> catalog-service
  • Authentication: The gateway may validate an OAuth 2.0 access token or JSON Web Token before forwarding a request.
  • Authorization: Route policies can require a scope such as orders:read, although services should still enforce domain-level permissions.
  • Rate limiting: A rule such as 100 requests/minute/client protects downstream services from abuse or accidental overload.
  • Aggregation: One client request can combine responses from Catalog and Inventory, reducing mobile-client round trips.
  • Protocol translation: The gateway may expose REST externally while calling an internal gRPC service.
  • Traffic management: Canary routing can direct 5% of requests to version v2 while the remainder continues to use v1.
  • Observability: Request counts, response status, latency, client identity, and trace identifiers can be recorded at the system boundary.
  • API gateway versus load balancer: A load balancer primarily distributes traffic among instances; a gateway performs application-level routing, authentication, transformation, and policy enforcement.
  • Limitations: The gateway can become a bottleneck or single point of failure, so it should be replicated, monitored, and kept free of core business logic.