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
- This page β why and when to split, Conway's Law, principles, data ownership, synchronous vs asynchronous communication, the store's services.
- Domain-Driven Design β the vocabulary and techniques to find good service boundaries.
- 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:
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.
-
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.
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
- Why the network makes this hard: Distributed Systems.
- The contracts between services: API.
- Build the platform: Hands-on 1 β CRUD, Hands-on 2 β Gateway, Hands-on 3 β Security.
- Inside one service: Software Architectures (clean and hexagonal).
- Asynchronous integration: Event-Driven Architecture and Hands-on 9 β Kafka.
-
XU, A. System Design 101Β β©
-
NEWMAN, S. Building Microservices, 2nd ed. O'Reilly, 2021.Β β©
-
EVANS, E. Domain-Driven Design. Addison-Wesley, 2003.Β β©
-
RICHARDSON, C. Microservices Patterns.Β β©β©
-
LEWIS, J.; FOWLER, M. Microservices β a definition of this new architectural term. martinfowler.com, 2014.Β β©
-
CONWAY, M. E. How Do Committees Invent? Datamation, 14(4), 1968, p. 28β31.Β β©