Research these three terms and you’ll make a peculiar discovery. Searching for API composition surfaces articles about orchestration. Searching for aggregation returns texts that use the two terms interchangeably. And in some vendor documentation, the same word names two different features depending on the chapter. The confusion sits less with the readers than with the literature itself, which has treated the three terms as synonyms for years even though they describe different patterns.
The distinction is no academic exercise. Behind each of the three terms stands a pattern of its own, with its own demands on state, failure handling, and team responsibility. Assign a use case to the wrong pattern and you either build too much, because a simple data bundling suddenly grows compensation logic, or too little, because a writing flow gets treated like a read query and leaves half-finished state behind on failure.
API aggregation bundles the results of several independent read calls into one response. API composition assembles a coherent business view from several sources, typically as a query pattern over distributed data. API orchestration controls a sequence of interdependent calls, including order, data handoffs, and failure handling. The patterns differ mainly in whether data only gets read or also changed, and in whether the calls depend on each other.
The overview before the detail
Before the three patterns get their individual treatment, a rough sketch helps. All three coordinate multiple API calls, and that shared trait is what feeds the confusion. The differences sit in three questions. Does data get changed or only read? Do the calls depend on each other or run independently? And does anything need to be rolled back on failure?
| Question | Aggregation | Composition | Orchestration |
|---|---|---|---|
| Read or write | Read only | Read only | Read and write |
| Dependency between calls | Independent, can run in parallel | Partially dependent, results get joined | Strongly dependent, order is mandatory |
| On failure | Partial response or error, nothing to clean up | Incomplete view, nothing to clean up | Compensation of already completed steps |
This sketch alone settles most assignment questions. For everything beyond it, the patterns deserve a closer look, each with an example from the same business domain, so the differences stay directly comparable.
Where the terms come from
Part of the confusion traces back to where the three words come from, since they grew up in different eras and communities. Orchestration is the oldest of the three in the API space. It hails from the SOA world of the 2000s, where business processes ran on BPEL and central process engines. That inheritance gives the word a lingering aftertaste of heavyweight middleware, which makes the debate more emotional than the substance calls for.
Composition was coined by the microservices literature, and rather precisely there, as a query pattern over distributed data stores, the stand-in for the distributed join that no longer exists in a world of per-service databases. Only the later, looser usage watered the word down into a catch-all for any assembling of APIs.
Aggregation, finally, comes out of the gateway world, where it named a product feature from the start, the bundling of several backend responses into one. Three communities, three decades, three meanings, and because all three patterns superficially do the same thing, combining several calls, the words eventually drifted freely between the meanings.
Aggregation, the data bundling
The simplest of the three patterns is aggregation. One call collects results from several independent sources and returns them bundled. The classic example is the home screen of a customer app. It shows the customer’s name, their last three orders, and the status of their support tickets. Three services, three calls, one response.
What matters is what’s missing here. There is no order, and the calls can run in parallel. There is no data flow between the calls, since none needs another’s result. And there is nothing to compensate, because nothing changes. If one source goes down, a tile on the home screen simply stays empty, and many implementations deliberately return a partial response instead of an error.
This modesty is why aggregation may live in places that are off limits to real flows. An API gateway can host it, as can a backend-for-frontend. Where that hospitality ends is what the comparison API orchestration vs. API gateway spells out, and the line runs exactly at the first writing step.
One design question stays with aggregation despite all its simplicity, namely how to handle uneven response times. When two sources answer in fifty milliseconds and the third takes two seconds, the slowest one sets the overall response. The usual answers are per-source timeouts with flagged partial responses, or a short-lived cache for the sluggish sources. Both are read-side optimizations with no flow logic, which is why they stay inside the pattern.
Composition, the assembled view
Composition is the term with the blurriest usage, and it deserves the closest look. In the narrow sense, as the microservices literature shaped it, API composition names a query pattern over distributed data. A business question cannot be answered from one service alone, because the data is spread across several services, each with its own database.
The example from the same business domain would be the question of all open orders of a customer, with delivery status and invoice totals. The order service knows the orders, the logistics service the deliveries, the billing service the amounts. A composition call fetches the orders first, uses their IDs to find the matching deliveries and invoices, and joins everything into one response. Unlike with aggregation, the calls depend on each other here, since without the order IDs from the first step, the follow-up queries cannot even be formed.
Composition stays a read pattern all the same. It changes nothing, and on failure the worst case is an incomplete view, no inconsistent state. The challenges sit elsewhere, in data volume, when the first query returns a thousand orders, and in the question of how fresh the assembled view has to be. With large volumes, the pattern therefore tends to migrate from the synchronous query to a precomputed view kept current through events, which is where the kinship with choreography begins, the subject of Orchestration vs. choreography.
In a retail project, a composition query first answered the order overview live across three services. As the catalog grew, response times climbed to several seconds, because the second query stage fired individual calls for every order. The fix was a precomputed read view updated through events, with no orchestration. Assigning the case to the right pattern spared the team a flow-control build that would have left the actual problem, the query load, unsolved.
Orchestration, the controlled flow
The third pattern differs from the other two categorically, because it writes. An orchestration executes a business transaction that changes data, across several services, in a binding order. In our shared business domain, that is the order itself. Inventory gets reserved, the payment authorized, the shipment triggered, and every step builds on the result of the one before.
Writing brings obligations. The flow needs state that records which step is done. It needs failure handling that rolls back completed steps when a later one fails for good. And it needs an owner who answers for the whole transaction. None of this can be optimized away, because an aborted write flow without compensation has real consequences, from blocked stock to double bookings. The building blocks in detail, from idempotency through the saga pattern to time limits, are covered in the overview of API orchestration.
It is remarkable how often this pattern shows up in disguise. An "aggregation" that writes a confirmation record at the end is an orchestration. The same goes for a "composition" that updates a cache in the source system on the side. The first writing step switches the pattern, no matter what the feature is called in the code or in the product catalog.
The gray zones, named honestly
For all the matrix’s clarity, there are zones where the assignment takes real effort, and hiding them would be dishonest.
The first is GraphQL. A GraphQL layer over several services is composition by shape, answering queries over distributed data and joining them by key. The moment that same layer offers mutations that change several services in sequence, an unspoken orchestration lives inside it, frequently without orchestration’s obligations. The technology does not decide the pattern. The individual operation does.
The second gray zone is batch processing. A nightly run that pushes ten thousand records through three services is an orchestration in a loop by nature, yet rarely gets treated as one. The assignment pays off precisely here, because repeatability and compensation decide the cleanup after an aborted mass run.
The third zone is the precomputed read view kept current through events. It answers composition questions while being filled by choreographed events. The assignment gets easier when you split it into two parts. The filling is event processing, the querying is a plain single lookup, and the original composition problem was simply settled at write time.
The decision matrix for daily work
For quick assignments in daily work, a matrix with five test criteria has proven itself.
| Criterion | Aggregation | Composition | Orchestration |
|---|---|---|---|
| Typical question | "Show me everything at a glance" | "Answer one question over distributed data" | "Execute a transaction" |
| Data flow between calls | None | IDs and keys from stage one | Each step’s results |
| State | None | None, a cache at most | Per transaction, sometimes long-lived |
| On partial failure | Partial response acceptable | Incomplete view, flag it | Compensation, defined end state |
| Natural home | Gateway or backend-for-frontend | Read service or precomputed view | Dedicated orchestration layer |
The matrix doubles as a review tool. To classify an existing feature, walk the five rows and check whether every answer lands in the same column. When they do not, the feature is usually a hybrid that has quietly grown into the next, more demanding pattern without taking on its obligations.
How the three patterns appear together
In a real application, the patterns rarely appear in isolation. Mostly they serve different stations that a user passes through one after another. The order example shows all three within a few minutes of use.
Opening the app loads the home screen through aggregation. Customer name, recent orders, and ticket status come from three services, queried in parallel, delivered bundled, and if the ticket service is sluggish, one tile simply stays empty. When the customer clicks into their order history with delivery status and invoice amounts, a composition works behind the scenes, resolving order IDs and joining the matching data from logistics and billing. When they finally place a new order, orchestration takes over, with reservation, payment, and shipping in a fixed order and compensation for the failure case.
Three stations, three patterns, three completely different requirement profiles, and yet a quick glance at the architecture would see the same thing everywhere, a component calling several services. That is exactly why the precise terms pay off. They make visible that the home screen needs no saga pattern and the order path very much does, even though both "combine several APIs".
For teams, one practical consequence concerns reuse. The temptation to use one "combine stuff" component for all three stations is real. That mostly ends in a component too heavy for the home screen and too light for the order path. You get more mileage from giving each station its own pattern and sharing only the genuinely common building blocks, the correlation ID propagation and the unified error format, say.
Why the mix-up costs money
You could dismiss the terminology question as hair-splitting for as long as the software runs. The costs of the mix-up show up at three concrete spots.
- Wrong tooling. Mistake orchestration for aggregation and you buy or build a workflow engine for a problem a parallel read fan-out would have solved. Hold aggregation to be enough where writes happen, and you skip the compensation and state the transaction needs.
- Wrong ownership. Aggregation may live with a platform team in the gateway, while an orchestrated business transaction belongs to the business team. The mix-up puts business flows into infrastructure hands, or burdens business teams with infrastructure duties.
- Wrong tests. Read patterns get tested with simple response comparisons. A writing flow needs tests for failure paths, retries, and compensations. Classify it as a read pattern and the critical paths get no tests at all.
In architecture reviews, it pays to listen for the word "just". Sentences like "we just aggregate three calls there" deserve the follow-up question whether one of them writes. Surprisingly often the answer is yes, and with it the entire requirements picture changes.
Classify any multi-call feature with two questions. Does at least one call change data, and does any call depend on another call’s result? Two times no means aggregation. Only the second question answered with yes means composition. And as soon as the first question draws a yes, you are looking at orchestration, with all the obligations that come with it.
Three patterns, one clear assignment
In the end, the three terms describe three rising levels of obligation. Aggregation bundles, composition joins, orchestration executes. Each level adds requirements, from the dependency between calls through state to compensation, and each level has its natural home in the architecture.
Once the distinction sinks in, even the blurry literature reads more calmly. Behind an article on "API composition" can hide any of the three patterns, yet the two test questions about writing and dependency sort any concrete case within minutes. Take your system’s three most used multi-call features and run them through the matrix. Every feature whose answers refuse to land in one column is a worthwhile candidate for the next architecture review.