InterviewKata
Software architecture · Case study 09 · Structure and evolution

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.

Real caseAnalysed from "Introducing Domain-Oriented Microservice Architecture" (Adam Gluck, Uber Engineering, July 2020).

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

3. The options

OptionGoodBad
Back to fewer, larger servicesFewer moving partsGives up the independence that motivated microservices; a vast migration
Keep 2,200 services as they areNo migrationComplexity and cross-team coordination keep growing with every service
Group services into domains with gateways, layers and extensionsCallers see one interface per domain; dependencies have a direction; teams extend without editingA 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
LayerContains
EdgeExposes Uber's services to the outside world safely; aware of mobile apps
PresentationFeatures of a consumer-facing app (mobile or web)
ProductLogic for one line of business, used by several apps: "request a ride" for Rider, Rider Lite and m.uber.com
BusinessFunctionality all of Uber uses, not specific to Rides, Eats or Freight
InfrastructureWhat 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

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

9. Your turn

  1. 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?
  2. Assign layers (10 min). Put each domain in one of the five layers. Find one dependency that points upwards.
  3. 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?
Say it in the interview
  • "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?

"Layers only depend on the layers under them", which makes blast radius predictable.

What does a product team call to use a domain?

The gateway is the single entry point; the services behind it can change without migrating callers.

What does the post advise small organisations?

It suggests holding off on microservices for some time in small organisations.

11. Sources and further reading

SourceWhy
Introducing Domain-Oriented Microservice Architecture (Uber, 2020)The original post: every fact on this page.
Bounded Context (Martin Fowler)The DDD idea behind domains.