Why monolithic applications become painful at scale, how the microservice architecture helps, and the pattern language that guides adoption.
Many successful applications start as a single, unified codebase. Over time, this codebase grows large and hard to manage. This section explains how that happens.
FTGO is an online food-delivery platform. It began as a monolithic application — one deployable unit containing all the business logic. The server-side code handles REST API requests, web pages, and integration with external services such as payment and messaging.
At its core, the FTGO monolith has a hexagonal architecture. Business logic sits in the center, surrounded by adapters that connect it to the outside world (databases, message brokers, REST clients).
When an application is small, the monolithic architecture has clear advantages:
Key point: Startups should begin with a monolithic architecture. It lets you move fast when the codebase is still small and the team is still learning the domain.
As the application grows over months and years, the codebase becomes very large. Development slows down. Teams step on each other's code. Deployments become risky. The application becomes difficult to understand, and fixing a bug in one area can break something in another. This state is called monolithic hell.
Problem — Monolithic Hell: The codebase is too large for any one person to understand. Building, testing, and deploying takes too long. Scaling is limited because all modules share the same resources, even if only one module needs more power.
Analogy: Think of a small restaurant where one chef cooks everything. It works when there are few orders. But if the restaurant gets hundreds of orders per hour, one chef cannot scale — you need a team of specialized cooks, each handling a different part of the menu.
Whether you are a developer, architect, CTO, or VP of Engineering, monolithic hell is a problem you will likely face. This book provides a structured approach — a pattern language — to adopt the microservice architecture successfully.
Even if you are already working with microservices, the patterns help you solve recurring design problems in communication, data management, and deployment.
The book covers a broad set of topics needed to design, build, and operate microservices:
The microservice architecture structures an application as a set of small, loosely coupled services. Each service is independently deployable and focuses on one business capability.
Core decision: Monolithic Architecture and Microservice Architecture are alternatives. Monolithic hell is the problem that motivates the move to microservices.
The Scale Cube is a three-dimensional model for scaling applications. Each axis represents a different strategy.
| Axis | Strategy | What it does |
|---|---|---|
| X-axis | Horizontal cloning | Run multiple identical copies of the application behind a load balancer. Improves capacity but does not reduce complexity. |
| Y-axis | Functional decomposition | Split the application into separate services by function. This is what defines the microservice architecture. |
| Z-axis | Data partitioning | Each instance handles a subset of data (e.g., by customer ID). Complements X and Y axes. |
Key point: Y-axis scaling (functional decomposition) is the axis that defines microservices. X-axis scaling helps with load but does not reduce code complexity. Z-axis scaling helps partition data across instances.
In a monolith, modularity is enforced by code conventions and packages — but nothing stops a developer from breaking module boundaries. In a microservice architecture, each service is a separate process with a well-defined API. You cannot accidentally call another service's internal code. This hard boundary enforces modularity much better than code conventions alone.
In a microservice architecture, each service owns its private database. No other service can access it directly. This is the Database per Service pattern.
Because services cannot share a database, you need new patterns for consistency and querying:
Analogy: Imagine each department in a company keeps its own filing cabinet. No department can open another's cabinet. If you need information from two departments, you must ask each one separately and combine the answers yourself.
In the microservice version of FTGO, the monolith is split into services such as Order Service, Restaurant Service, Delivery Service, Kitchen Service, and Accounting Service. Each service owns its data and exposes a REST or messaging API.
An API Gateway sits in front of all services. It routes requests from external clients (web, mobile) to the correct internal service. A related pattern, Backend for Frontend (BFF), creates a separate gateway for each client type.
Service-Oriented Architecture (SOA) and the microservice architecture both organize applications into services. However, they differ in important ways.
| Aspect | SOA | Microservices |
|---|---|---|
| Communication | Heavy protocols (ESB, WS-*, SOAP) | Lightweight protocols (REST, gRPC, messaging) |
| Data | Shared global database is common | Database per service |
| Service size | Larger, enterprise-wide services | Smaller services focused on one capability |
Warning — Distributed Monolith: If you decompose incorrectly, services stay tightly coupled. Every change still requires coordinating multiple teams and deploying multiple services together. This is called a distributed monolith, and it is worse than a regular monolith because you also pay the cost of network communication.
| Aspect | Monolithic | Microservices |
|---|---|---|
| Simplicity | Simple to develop, test, deploy early on | More moving parts from the start |
| Scalability | Scale entire app (wasteful) | Scale individual services |
| Team autonomy | Teams share the same codebase | Teams own independent services |
| Data consistency | Single database transaction (ACID) | Requires Sagas for cross-service consistency |
| Deployment | One unit to deploy | Many units; needs automation |
| Fault isolation | One bug can crash everything | Failures contained per service |
A pattern language is an organized collection of patterns that solve related problems. The microservice architecture pattern language groups patterns into several categories that cover the full lifecycle of building microservices.
Microservices solve many problems, but they also introduce new ones. There is no single correct answer — every decision involves tradeoffs. The pattern language helps you navigate these tradeoffs by showing which patterns solve which problems, and what new issues each pattern may create.
Adoption timing dilemma: Startups should begin with a monolith because the overhead of microservices is too high for a small team. As the application and team grow, you migrate to microservices when the pain of the monolith outweighs the cost of migration.
A pattern is a reusable solution to a problem that occurs in a specific context. Each pattern describes the problem, the forces at play, the solution, and the resulting consequences (both positive and negative). Patterns often have relationships: one pattern's consequence may be a problem solved by another pattern.
A pattern language groups these patterns into a structured collection. It shows alternatives, dependencies, and chains of patterns that work together.
The pattern language is organized into the following groups. Each group addresses a different concern.
These patterns decide how to split an application into services.
Services need to talk to each other. Two main styles exist.
Reliable communication also requires handling failures:
Services need to find each other on the network. A Service Registry is a database of service instance locations.
| Pattern | How it works |
|---|---|
| Client-side Discovery | The client queries the Service Registry and picks an instance to call. |
| Server-side Discovery | The client sends a request to a router/load balancer, which queries the registry. |
| Self Registration | Each service registers itself in the registry on startup. |
| 3rd-party Registration | An external component (e.g., deployment platform) registers services in the registry. |
The Database per Service pattern creates challenges for consistency and querying.
To reliably publish domain events from a service's database, you can use:
| Pattern | Description |
|---|---|
| Single Service per Host | Each service instance runs on its own host. Variants: Service-per-Container, Service-per-VM. |
| Multiple Services per Host | Several service instances share one host. Simpler but less isolated. |
| Serverless Deployment | Deploy service code as functions. The platform handles scaling and infrastructure. |
| Service Deployment Platform | An automated platform (e.g., Kubernetes) that manages all deployment patterns. |
With many services running, you need visibility into the system. Observability patterns work together to give you that visibility.
Testing microservices in isolation requires new approaches.
Technology alone is not enough. You also need the right team structure and development process to succeed with microservices.
The microservice architecture enables small, autonomous teams — often called two-pizza teams (small enough to be fed by two pizzas). Each team owns one or more services end-to-end: development, testing, deployment, and operations.
Conway's Law: Organizations tend to produce systems whose structure mirrors their communication structure. If your teams are organized by technology layer (frontend team, backend team, DBA team), your services will likely be coupled in the same way.
Reverse Conway Maneuver: Instead of letting your organization shape the architecture, reorganize your teams to mirror the service architecture you want. Structure teams around business capabilities so that each team can own its service independently.
Autonomous two-pizza teams enable Continuous Delivery and Deployment (DevOps). Each team can push changes to production frequently and safely because they only deploy their own service, not the entire application.
Key chain: Microservice Architecture enables autonomous Two-Pizza Teams, which in turn enable Continuous Delivery/Deployment.
Moving from a monolith to microservices is not just a technical change — it is also an emotional transition for people. Roles change, responsibilities shift, and familiar workflows disappear.
The concepts in this chapter connect to each other in chains. Understanding these relationships helps you see how patterns depend on one another.
Core chain: Monolithic Hell (problem) → motivates Microservice Architecture (solution) → requires Database per Service → creates need for Saga (consistency), API Composition / CQRS (querying).
Event chain: Domain Event + Event Sourcing + Aggregate support Saga execution. Transactional Outbox ensures reliable event publishing via Polling Publisher or Transaction Log Tailing.
Organization chain: Microservice Architecture → enables Two-Pizza Teams → enables Continuous Delivery/Deployment. Conway's Law + Reverse Conway Maneuver: your org structure should mirror your service architecture.