Uber: domain-oriented microservices
Uber moved from two monoliths to microservices around 2012 and 2013 and grew to about 2,200 critical services. The benefits were real, and so was the cost: a simple feature touched services owned by many teams, and investigating one problem meant working through around 50 services across 12 teams. Uber didn't go back to a monolith. It grouped the services into domains, put each domain behind one gateway, ordered the domains in layers that may only depend downwards, and added extension points so other teams could add behaviour without editing a domain's code.
1. The situation
Uber's microservices solved the problems its monoliths had: one regression could take down all of Uber, deployments were risky, and as Uber grew from tens to hundreds of engineers, teams couldn't work independently. With services, teams gained independent deployments, clear ownership and better reliability.
At around 2,200 critical services the costs showed. In the post's words, building a simple feature often meant working across multiple services "owned by different individuals and teams", with meetings, design and code review across them; teams ended up building code in each other's services, changing each other's data models and even deploying on each other's behalf. A latency spike deep in a call chain cascaded upstream.
2. What mattered
- Keep the benefits: independent deployment, ownership, reliability.
- Reduce the number of things a feature has to touch, and the context an engineer needs.
- Contain blast radius: know which services can break which.
- Survive churn: Uber measured the half-life of a microservice at 1.5 years: every 18 months, half of the services change. Callers shouldn't have to migrate every time.
3. The options
| Option | Good | Bad |
|---|---|---|
| Back to fewer, larger services | Fewer moving parts | Gives up the independence that motivated microservices; a vast migration |
| Keep 2,200 services as they are | No migration | Complexity and cross-team coordination keep growing with every service |
| Group services into domains with gateways, layers and extensions | Callers see one interface per domain; dependencies have a direction; teams extend without editing | A large classification and migration effort; gateway API design becomes critical |
4. The decision
Decision: Domain-Oriented Microservice Architecture (DOMA)
Domains: a collection of one or more microservices with one logical role
(map search, fares, matching). No size rule; not tied to the org chart.
Layers: infrastructure, business, product, presentation, edge.
"Layers only depend on the layers under them."
Gateways: one entry point per domain; callers depend on the gateway, never on
the services behind it.
Extensions: logic extensions (a plug-in interface the domain defines) and data
extensions, so other teams add behaviour without changing the domain's code.
Consequences:
+ A feature calls one gateway instead of many services
+ Services behind a gateway can be replaced without migrating callers
+ Lower layers are known to have the largest blast radius
- Classifying and migrating thousands of services takes years
- Gateway APIs must be designed well: they are long-lived contracts
| Layer | Contains |
|---|---|
| Edge | Exposes Uber's services to the outside world safely; aware of mobile apps |
| Presentation | Features of a consumer-facing app (mobile or web) |
| Product | Logic for one line of business, used by several apps: "request a ride" for Rider, Rider Lite and m.uber.com |
| Business | Functionality all of Uber uses, not specific to Rides, Eats or Freight |
| Infrastructure | What any engineering organisation could use: storage, networking |
Each layer up is more specific and has a smaller blast radius: fewer things depend on it. Functionality tends to move down over time, as a feature built for one product becomes a platform for several.
5. How it works
A product team needs fare information for a new feature. Watch the calls before and after, then an extension and a replacement.
6. The numbers
- About 2,200 critical microservices, classified into 70 domains; roughly half implemented at the time of the post.
- Onboarding a new feature onto a platform: 25 to 50% less time, because product teams call one gateway instead of many services.
- One early adopter of extensions cut the time to prioritise and integrate a new feature from three days to three hours.
- Microservice half-life: 1.5 years. Uber Maps alone: 80 services in three domains behind three gateways.
7. What happened
The post reports simpler integration for product teams, faster onboarding onto platforms, and gateways that protect callers from churn. It presents DOMA as "a dynamic and progressive process", not a one-time re-architecture. It is equally direct about who shouldn't copy Uber: in small organisations, "the operational benefit likely does not offset the increase in architectural complexity", and "it isn't unreasonable to hold off on microservices altogether for some time". For those who do adopt them, it advises thinking of microservices as one large distributed application and taking the first service boundaries seriously, because they tend to last longest.
8. Lessons
- Bounded contexts above services. A domain is a bounded context implemented by several services; the gateway is its published interface (Module 08a).
- Give dependencies a direction. Layers that only depend downwards are the layered style applied to services, and they make blast radius predictable (Module 11, 07).
- Extension points beat shared code edits. Logic extensions are the microkernel's plug-in idea at service scale (Module 12).
- Interfaces absorb churn. With a 1.5-year half-life, a gateway saves every caller a migration (Module 25).
- Teams and domains are separate maps. Uber's domains "don't always follow company org structure"; decide domains on logical role, then align teams (Module 09a).
9. Your turn
- Group the services (15 min). List 15 services from a system you know and group them into domains by logical role. Which domain would each caller talk to?
- Assign layers (10 min). Put each domain in one of the five layers. Find one dependency that points upwards.
- Design one extension (5 min). Which behaviour do other teams keep adding to one of your services? What interface would let them plug it in?
- "We have hundreds of microservices and every feature touches ten of them. What would you do?" Group services into domains by logical role, put each behind one gateway, and order domains in layers that only depend downwards, as Uber did with DOMA. Add extension points where other teams keep editing a domain's code.
10. Check yourself
In Uber's DOMA, a domain in the product layer may depend on domains in which layers?
What does a product team call to use a domain?
What does the post advise small organisations?
11. Sources and further reading
| Source | Why |
|---|---|
| Introducing Domain-Oriented Microservice Architecture (Uber, 2020) | The original post: every fact on this page. |
| Bounded Context (Martin Fowler) | The DDD idea behind domains. |