Skip to content

Overview

Picture the store as a single Spring Boot application: login, catalogue, orders and payments in one codebase, one database, one deployable .jar. On Black Friday only checkout is overloaded, but the only way to scale it is to run more copies of everything. A bug in the catalogue page takes login down with it. Three teams now share the codebase, and every release needs all of them to agree on the same day. Nothing is wrong with the code β€” the shape of the system no longer fits the organisation.

The microservices style is a response to this specific problem: as teams and codebases grow, a single monolith becomes increasingly expensive to change β€” a modification to one module requires understanding and testing the entire system, and a deployment requires coordinating across the whole organisation.

Microservices is an architectural style that structures an application as a collection of small, autonomous services, each modelled around a specific business domain52. Rather than building one large deployable unit (a monolith), the system is composed of independently deployable pieces that collaborate through well-defined APIs.

Learning objectives

By the end of this class you should be able to:

  • Compare a monolith, a modular monolith and microservices, and justify when splitting is worth its cost.
  • Explain Conway's Law and the core principles of microservices (single capability, independence, data isolation).
  • Choose between synchronous and asynchronous communication for an interaction, and estimate the availability cost of a synchronous chain.
  • Apply Domain-Driven Design (bounded contexts, aggregates, context maps) to find service boundaries.
  • Describe the infrastructure components of a microservice platform and follow one request through the course's architecture.

Roadmap

  1. This page β€” why and when to split, Conway's Law, principles, data ownership, synchronous vs asynchronous communication, the store's services.
  2. Domain-Driven Design β€” the vocabulary and techniques to find good service boundaries.
  3. Architecture & Components β€” load balancer, gateway, identity provider, discovery, configuration, broker, observability, and the course's reference architecture.

Slides

Click the slides and use ← β†’ to move through them; F goes full screen and P opens the presenter view, with the notes. Open the slides in a new tab


Monolith vs. Microservices

flowchart LR
    subgraph mono [Monolith]
        direction TB
        ui_m[UI]
        biz_m[Business Logic\nOrders Β· Auth Β· Payments Β· Inventory]
        data_m[Single Database]
        ui_m --> biz_m --> data_m
    end
    subgraph micro [Microservices]
        direction TB
        gw[API Gateway]
        s1[Orders\nService]
        s2[Auth\nService]
        s3[Inventory\nService]
        db1[(Orders DB)]
        db2[(Auth DB)]
        db3[(Inventory DB)]
        gw --> s1 & s2 & s3
        s1 --> db1
        s2 --> db2
        s3 --> db3
    end

Neither style is universally better. The right choice depends on team size, domain complexity, and operational maturity:

Dimension Monolith Microservices
Deployment One unit β€” simple to ship Independent per service β€” complex to coordinate
Scaling Scale the whole app Scale only the bottleneck service
Development speed Fast initially Slower setup; faster at scale
Operational complexity Low High β€” needs orchestration, tracing, service discovery
Data consistency ACID transactions Eventual consistency across services
Team fit Small, co-located team Multiple teams, each owning a service

Two terms in the table deserve a definition. An ACID transaction (Atomicity, Consistency, Isolation, Durability) lets the monolith change orders and stock in one database operation that either fully happens or not at all. Across services there is no shared transaction: each service commits its own data, and the system becomes consistent a moment later β€” eventual consistency (see CAP Theorem).

Start with a monolith

Most successful microservice systems began as monoliths. Identify stable domain boundaries (using Domain-Driven Design) before splitting. Premature decomposition creates a distributed monolith β€” all the complexity of microservices with none of the independence benefits.

The middle ground: a modular monolith. One deployable unit, but internally split into modules with explicit interfaces and no shared tables β€” each module could become a service later. It gives the team practice with boundaries before paying for the network, and it is often the right answer for a small team.

Symptoms of a distributed monolith

  • Services must be deployed together ("release train") because a change in one breaks the others.
  • Two services read and write the same tables.
  • One user request triggers a long chain of synchronous calls; if any link is down, everything is down.
  • A shared library with domain classes forces every service to upgrade at once.

Conway's Law

"Any organisation that designs a system will produce a design whose structure is a copy of the organisation's communication structure." β€” Melvin Conway, 19676

Service boundaries and team boundaries converge over time. The simulation below shows how an initially unified codebase fragments into services that mirror the communication patterns of the teams that built it:

2026-10-12T22:08:25.030594 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/

The law also works in reverse, which is how organisations use it on purpose: the inverse Conway maneuver shapes the teams first (one team per business capability) so that the architecture follows.


Key principles

Principle Meaning
Single Responsibility Each service implements exactly one business capability
Independence Services can be developed, deployed, and scaled without coordinating with others
Data isolation Each service owns its own database β€” no shared schema, no shared tables
Decentralised governance Teams choose the technology that best fits their service
Failure isolation A failing service does not cascade and bring down the entire system
API-first communication Services interact through explicit, versioned contracts
Observability Comprehensive monitoring, distributed tracing, and structured logging are prerequisites

How small is "micro"? Not a number of lines. A service is the right size when one team can own it end to end, it can be deployed without coordinating with others, and it maps to one business capability. Services that are too small ("nanoservices", e.g. one service per table) spend more time talking to each other than doing work.


The store, decomposed

The course builds one platform step by step. Each service owns one capability and its own data4:

Service Capability Owns Talks to Built in
gateway-service Single entry point, routing, authentication check β€” every service Hands-on 2, 3
account-service User accounts accounts schema β€” Hands-on 1
auth-service Register, login, issue and resolve JWTs β€” (uses account) account Hands-on 3
product-service Product catalogue products data β€” Product API
order-service Orders of the current user orders data product, exchange, Kafka Order API
exchange-service (Python) Currency conversion β€” third-party rates API Exchange API
notification-service (Python) Order confirmation e-mails β€” Kafka Hands-on 9

Note decentralised governance in action: the exchange and notification services are written in Python, the others in Java β€” the contract is HTTP/JSON or a Kafka event, so the language does not leak.

Owning the data

Database-per-service means that no service reads or writes another service's tables. It does not require one database server per service. In the hands-on, a single Postgres container hosts several schemas, and each service is the only one that touches its own (the account service sets hibernate.default_schema: accounts and runs its own Flyway migrations):

flowchart LR
    subgraph pg ["one Postgres server"]
        s1[("schema accounts")]
        s2[("schema products")]
        s3[("schema orders")]
    end
    account["account-service"] --> s1
    product["product-service"] --> s2
    order["order-service"] --> s3
    order -->|"GET /products/{id}"| product
    order -.-x|"no direct SQL"| s2

When the order service needs a product's price, it asks the product API, never the product table. That keeps the product team free to change its schema. Two consequences follow:

  • Queries across services become API calls (or a local copy of the data, kept up to date by events β€” see CQRS).
  • Business transactions across services cannot use one ACID transaction. They become a sequence of local transactions coordinated by events, with compensating actions when a step fails β€” the saga pattern4.

Inter-service communication

Services in a microservice system communicate in two fundamentally different ways, each with distinct trade-offs:

The caller sends a request and waits for a response. Simple to reason about, but creates temporal coupling β€” the caller cannot complete its operation if the callee is down.

sequenceDiagram
    participant Client
    participant Gateway
    participant Order Service
    participant Inventory Service

    Client->>+Gateway: POST /orders
    Gateway->>+Order Service: create order
    Order Service->>+Inventory Service: reserve items
    Inventory Service-->>-Order Service: reserved βœ“
    Order Service-->>-Gateway: 201 Created
    Gateway-->>-Client: 201 Created

Use when: the caller needs the result immediately (e.g., checkout response, auth validation).

The producer publishes an event and continues. Consumers process the event independently, in their own time. This eliminates temporal coupling β€” services can be down and process the message when they recover.

sequenceDiagram
    participant Order Service
    participant Message Broker
    participant Inventory Service
    participant Notification Service

    Order Service->>Message Broker: OrderPlaced event
    Note over Order Service: continues immediately
    Message Broker->>Inventory Service: OrderPlaced
    Message Broker->>Notification Service: OrderPlaced
    Inventory Service->>Message Broker: InventoryReserved

Use when: strict consistency is not needed immediately (e.g., sending an email, updating analytics, inventory reservation).

In the store, the order service calls the product service synchronously (it needs the price to compute the total before answering), and publishes an OrderCreated event to Kafka asynchronously so that the notification service can send the e-mail without slowing the customer down (Hands-on 9).

Synchronous chains multiply failure

If a request must pass through three services, and each one is available 99.9% of the time, the chain is available only 0.999 Γ— 0.999 Γ— 0.999 β‰ˆ 99.7% of the time β€” from about 8.8 hours of downtime a year to about 26 hours. Every extra synchronous hop lowers availability and adds latency. Keep chains short, use timeouts and circuit breakers (Resilience Patterns), and move work that does not need an immediate answer to events (Event-Driven Architecture).


Topics covered

  • Domain-Driven Design


    The vocabulary and techniques for identifying service boundaries: ubiquitous language, bounded contexts, entities, value objects, aggregates, domain events, repositories and context maps.

    Domain-Driven Design

  • Architecture & Components


    The building blocks of a production microservices system: load balancers, API gateway, identity provider, service discovery, configuration service, message broker, observability, and the course's reference architecture1.

    Architecture & Components


Key takeaways

  • Microservices trade operational complexity for independent deployment, scaling and team autonomy β€” worth it only when those are real problems.
  • Start with a (modular) monolith; split along stable domain boundaries found with DDD3, or you get a distributed monolith.
  • Conway's Law: service boundaries mirror team communication β€” design both together.
  • Each service owns one capability and its data; others use its API, never its tables.
  • Synchronous calls are simple but couple availability; events decouple producers from consumers.

Connections



  1. XU, A. System Design 101 ↩

  2. NEWMAN, S. Building Microservices, 2nd ed. O'Reilly, 2021. ↩

  3. EVANS, E. Domain-Driven Design. Addison-Wesley, 2003. ↩

  4. RICHARDSON, C. Microservices Patterns. ↩↩

  5. LEWIS, J.; FOWLER, M. Microservices β€” a definition of this new architectural term. martinfowler.com, 2014. ↩

  6. CONWAY, M. E. How Do Committees Invent? Datamation, 14(4), 1968, p. 28–31. ↩