Iterators and Aggregators are the two modules that make Make's linear scenario model work with array data at all. An Iterator takes an array and emits its elements as separate bundles, one execution cycle each, so that everything downstream operates on a single record instead of a list. An Aggregator does the reverse: it collects a stream of bundles back into a single array (or a single text blob) further down the flow. Almost every ERP or bulk-data scenario ends up needing both, and the details of how they interact with operation count and memory are where scenarios start to misbehave at scale.
What an Iterator actually does to the run
Feeding an array of 500 order lines into an Iterator produces 500 separate bundles, each one flowing independently through every module after it in the flow. That means a 3-module chain after the Iterator runs 1,500 times, not 3 — the per-item cost compounds with every downstream module, which is the single most common reason a scenario's operation count is much higher than the module count in the editor suggests.
{
"flow": [
{"module": "flow:iterator", "name": "Split order lines",
"parameters": {"array": "{{1.data.lineItems}}"}},
{"module": "tools:setVariable", "name": "Compute line total"},
{"module": "flow:aggregator", "name": "Collect updated lines",
"parameters": {"source": "Split order lines"}}
]
}
Aggregators need to know which Iterator they're collecting from
An Aggregator has to be told which upstream module's bundles it's gathering, and it only collects bundles that flowed through the branch it's attached to. If a Router splits the flow after the Iterator and the Aggregator sits on the wrong branch, it silently collects a partial set — this is a common, quiet bug: the scenario runs without error, but the aggregated output is missing whatever went down the other branch.
Grouping changes what comes out the other side
Aggregators support a group-by field, which collapses bundles sharing a key into one aggregated array per group instead of one array overall — useful when you've iterated over order lines but want to re-group them by order ID before writing back a summary per order. Getting the group-by field wrong (or leaving it on a field that isn't actually shared across the bundles you expect) produces more groups than intended, each with fewer items than expected, and it's easy to miss in testing with a small sample dataset.
Make caps how long a single scenario execution can run. An Iterator over tens of thousands of items with several modules per item can hit that ceiling before finishing, leaving the aggregation incomplete. Page the source data (fetch and process in chunks across multiple scheduled runs) rather than iterating over an unbounded array in one execution.
Batch instead of iterate, when the target API allows it
If the destination system accepts an array in a single request, aggregate first and send once, rather than iterating and sending one request per item. This is almost always cheaper in operations and faster in wall-clock time — the Iterator/Aggregator pair is for when per-item transformation logic genuinely needs to run individually, not a default pattern for every array you touch.
A quick sanity check — comparing the aggregated array length to the original array length — catches misattached Aggregators and dropped-branch bugs immediately, instead of discovering the gap in a downstream report weeks later.
Wrapping up
The Iterator/Aggregator pair is what lets a linear scenario model handle arrays, but each element flowing through downstream modules multiplies both cost and failure surface. Know exactly which branch an Aggregator is attached to, batch instead of iterate whenever the destination API allows a bulk call, and page large arrays across multiple runs rather than trusting a single execution to make it through a five-figure item count.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.