Uber DOMA: Structure Above Microservices
also called Domain-Oriented Microservice Architecture
Having reached thousands of microservices, Uber introduced domains and layers above them because independent services had recreated the coordination problem they were meant to solve.
The problem
Uber decomposed aggressively and by around 2018–2020 was operating on the order of 2,200 microservices. Their published account describes the consequence honestly: the benefits of microservices — independent deployment, clear ownership, technology freedom — had been substantially offset by the difficulty of understanding, changing and operating a system with that many parts.
Specific symptoms: a change requiring coordination across many services; no coherent view of what depended on what; onboarding requiring knowledge of dozens of services; and no meaningful architectural constraint on which service could call which.
What they did
Domain-Oriented Microservice Architecture groups related services into domains, each fronted by a gateway that presents the domain's interface. Consumers depend on the domain, not on the individual services inside it, so internal restructuring does not break callers.
They also introduced a layered design with dependency rules — a service in one layer may depend on lower layers but not upward — which prevents the dependency graph becoming arbitrary and makes reasoning about blast radius possible.
This is, deliberately, structure imposed above the service level: an acknowledgement that "many small independent services" is not by itself an architecture.
The trade-off
Domains reintroduce a coordination boundary and a gateway hop. They constrain the autonomy that motivated microservices, and defining domain boundaries has the same difficulty as defining service boundaries — with higher stakes, since they are harder to move.
The transferable lesson
Service count is not a goal, and past a point it is a cost. The benefit of decomposition comes from team autonomy and independent deployment; beyond the point where those are achieved, more services add coordination overhead without adding autonomy.
Two practical questions follow. How many services does a typical feature touch? If routinely more than one or two, the boundaries are wrong regardless of count. And is there any rule constraining which service may call which? Without one, the dependency graph becomes arbitrary, and arbitrary graphs cannot be reasoned about, tested in isolation, or safely changed.
Structure above services — domains, layers, published interfaces — is what makes a large estate tractable, and it is easier to impose early than to retrofit at 2,200.