The word "microservice" gets defined many ways. Some call it "a small service." Others point at Docker, Kubernetes, or a REST API. None of those is wrong, but none captures what separates a microservices architecture from a monolith spread across a few machines.
Here is the working definition this course uses throughout:
Microservices are independently deployable services, each organized around a business capability, each owning its own data, communicating with one another over the network.
This chapter takes that sentence one clause at a time, starting with the property that matters most.
The diagram shows the shape we are after. Each service is a separate box. Each one talks to its own database, never to another service's database. They reach each other through the network, either by direct calls through a gateway or by publishing events to a broker. The rest of the chapter unpacks the properties that make it work.
Independent deployability is the most important characteristic of a microservices architecture, and it is the clearest test of whether something counts as microservices.
The test is simple. Can you ship a change to service A, into production, without redeploying any other service? If the answer is yes, you have a real service boundary.
If shipping A means you also have to rebuild, retest, and redeploy B and C in lockstep, then A, B, and C are not three services. They are one system split into three deployments that move together.
This is the property that gives microservices their value. When a team can deploy its service on its own schedule, it stops coordinating release trains with every other team. It can fix a bug at 2pm and ship it at 2:30pm without waiting for the weekly company-wide deploy. It can roll back its own service without affecting anyone else.
That autonomy is where the value comes from. Almost every other benefit people attribute to microservices flows from this one.
Independent deployability also forces good behavior. To deploy A without B, A and B must agree on a stable contract (an API or an event schema) and nothing else. They cannot share a database schema, because a schema change would force a coordinated deploy. They cannot share in-process code that changes often, because recompiling that shared code means redeploying both.
The discipline of "ship independently" pushes you toward loose coupling whether you planned for it or not.
When you describe an architecture, lead with this. Saying "these are independently deployable, so the payments team ships on its own cadence without touching the order service" tells more about the design than any amount of infrastructure detail.
The next clause in our definition is that each service is organized around a business capability. A common failure is to read "single responsibility" and conclude that each service should manage one entity: a User service, an Address service, a Product service, a Price service.
This is decomposing the database, not the domain, and it produces services that are too small to be useful on their own.
The right unit is a bounded context, a term from Domain-Driven Design. Think of it as a slice of the business with its own consistent vocabulary and its own set of rules. "Ordering" is a bounded context. "Fulfillment" is another. "Billing" is another. Each one owns a cluster of related entities and the logic that governs them.
Why does the entity-per-service approach fail? Because real business operations almost never touch a single entity. Placing an order touches the order, the customer, the line items, the inventory reservation, and the pricing rules.
If each of those lives in its own service, a single user action becomes many network calls between tiny services that exist only to call each other. You have multiplied the network without adding any autonomy.
A bounded context, by contrast, contains enough of the domain to do meaningful work behind a single boundary.
The Ordering service can validate an order, apply its rules, and persist it without calling a dozen other services first. The boundary maps to something the business recognizes, which means it also tends to map to a single team that understands that part of the business.
One capability, one context, one service, one team. That is the alignment you are aiming for, and when you find it, the rest of the design tends to fall into place.
Each service owns its own data. This is the clause that breaks the most designs in practice, and the one worth defending most firmly.
Owning your data means the service is the only thing that reads or writes its persistent store. No other service connects to its database. Not for writes, and not for reads either.
The moment a second service runs a query against your tables, you have lost the ability to change your schema freely, because someone else now depends on its shape. A read-only dependency today becomes a blocker on every migration tomorrow. A quick reporting query against another team's tables can turn into a multi-week migration negotiation a year later, once the schema needs to change.
The tempting shortcut is a shared database that several services all connect to. It feels efficient. You can join across everything, there is one place to back up, and nobody has to build APIs to move data around. The diagram below shows why this shortcut breaks the architecture.
In the top arrangement, the three services share one database, so they share one schema. Change a column the Order service needs and you risk breaking the Payment service's queries. The teams must now coordinate every schema change, which means they cannot deploy independently, which means they are no longer microservices.
They are a distributed monolith, an architecture that pays the operational cost of distribution while keeping the coupling of a monolith. It is the worst of both worlds.
In the bottom arrangement, each service has its own store. Each team changes its schema whenever it likes. The price of this independence is that you can no longer join across services with a single SQL query.
When the Order service needs customer details that live in the Customer service, it has to ask over the network or keep its own local copy fed by events.
Handling cross-service data is a large topic in its own right, but the rule that makes all of it possible starts here: each service owns its data, and nobody reaches across the boundary.
This phrase, coined by Martin Fowler and James Lewis, captures where the intelligence in the system should live. The "endpoints" are your services. The "pipes" are whatever moves messages between them: the network, a message broker, an API gateway.
Smart endpoints, dumb pipes means the business logic belongs in the services, and the connecting infrastructure should do as little thinking as possible.
This was a direct reaction to the previous generation of architecture, where the integration layer (the Enterprise Service Bus) accumulated routing rules, data transformations, and business logic.
The bus became the place where everything happened, which made it a bottleneck and a single point of failure. Worse, the logic that defined how the business worked was scattered into middleware that no single team owned.
The microservices answer is to keep the pipes simple. A message broker should reliably deliver messages and nothing more. An API gateway should route requests, handle authentication, and apply rate limits, but it should not contain rules like "if the order total exceeds five hundred dollars, route it to the fraud team."
That rule is business logic, and it belongs inside a service that owns it. When you find yourself wanting to put domain logic into the gateway or the broker, treat it as a signal that a service is missing.
A traditional monolith is often organized in horizontal layers: a presentation layer, a business layer, a data-access layer. A natural but wrong instinct is to turn each layer into a service: a UI service, an API service, a database service.
This mirrors the old code structure, but it ignores the domain entirely, and it recreates the coordination problem you were trying to escape.
The contrast is worth making concrete.
| Decomposed by technical layer | Decomposed by business capability |
|---|---|
| UI Service, API Service, Data Service | Order Service, Payment Service, Shipping Service |
| A single feature (place an order) cuts across all three services | A single feature lives inside one service |
| Every change requires touching multiple services | Most changes stay within one service and one team |
| Teams organized by technology, not ownership | Teams own a capability end to end |
| Network hops added with no autonomy gained | Network hops align with real boundaries |
Layer-based services look tidy on a diagram and fail in practice, because business features do not respect technical layers. Adding a discount feature touches the UI, the API, and the data, so it touches all three "services," which means three teams coordinating on one change. Capability-based services keep that feature inside a single boundary.
The "I want to..." test is a quick way to find capabilities: every service should complete a sentence like "I want to place an order" or "I want to charge a card." If a proposed service only completes "I want to access the database," it is a layer, not a capability.
In a monolith, a function call to another module either returns or throws, and it does so in microseconds. In a distributed system, every call to another service crosses the network, and the network is not reliable.
The call can be slow, it can time out, it can return a confusing partial result, or the service on the other end can be down entirely. A microservice has to assume all of this will happen and decide in advance how it will behave when it does.
This is a shift in mindset more than any single technique. A service treats every dependency as something that will fail, not something that might.
The Order service does not assume the Recommendation service is up. It assumes the Recommendation service could be down right now, and the checkout flow still has to work, maybe by dropping the recommendations panel instead of blocking the purchase.
There is a whole family of concrete tools for this, such as timeouts, retries, circuit breakers, bulkheads, and graceful degradation.
The property itself is simpler than any of those tools: failure handling is part of the service's job, not something you bolt on after launch. A service that only works when all of its dependencies are healthy will not survive its first bad week in production.
Because services are independent and talk only over the network, each one is free to use a different language and a different database. The Recommendation service can run on Python with a vector store while the Payment service runs on Java with Postgres. Nothing about the architecture forbids it.
This freedom gets sold as a headline benefit of microservices, and it does pay off when a workload needs a tool the default cannot provide, say a search index that has no business living in your transactional database.
The cost is real. Every extra language and datastore is one more thing your organization has to operate, monitor, secure, patch, and hire for. Five languages means five build pipelines and five sets of libraries to keep current. A four-person team running three languages and two databases gains nothing from the variety except a longer on-call rotation.
Polyglot persistence and polyglot programming are options the architecture hands you, not goals to chase. The sensible default is one language and one database for most services, with exceptions made only when a service has a need the default cannot meet.
Half of getting the definition right is knowing what it excludes. A lot of things travel under the microservices banner while being nothing more than implementation details. They may be present in a microservices system, but their presence does not make a system microservices, and their absence does not disqualify one.
| Commonly confused with microservices | What it really is |
|---|---|
| REST APIs | A communication style. A monolith can expose REST, and services can talk over gRPC or events instead. |
| Docker containers | A packaging and deployment mechanism. You can containerize a monolith, and you can run services without containers. |
| Kubernetes pods | An orchestration detail. It runs whatever you give it, monolith or service. |
| "Small" codebases | Size is a symptom, not a definition. A service is sized by its bounded context, not a line count. |
This distinction is worth being strict about, because "what makes this a microservices architecture?" is a question you want a clean answer to. The answer is not "it runs on Kubernetes" or "it speaks REST." The answer is independent deployability and decentralized data, organized around business capabilities.
Anchor to those properties and ignore the infrastructure underneath. A team can run containers on Kubernetes behind a REST gateway and still have a distributed monolith, as long as the services share a database and ship in lockstep. The platform is real, but it is not the definition.
Pulling the clauses back together, here is the checklist that defines a microservice. When you look at any box on an architecture diagram and want to know whether it is a service at all, run it through these.
When is the cost of all this independence worth paying, and when are you better off with a single deployable? That trade-off between a monolith and microservices is where we go next.
10 quizzes