A BFF per client, and when the duplication earns its keep
Four gateways for one product looks like waste on a whiteboard. The duplication is real, but it is shallower than it looks — and what it buys is specific enough to test for.
On a food delivery platform we took from zero to production, the architecture had four backends-for-frontends: one each for the customer app, the delivery partner app, the restaurant portal and the admin console. That drawing gets challenged in every review it appears in, and the challenge is fair — four codebases doing broadly the same job is a maintenance bill you pay every sprint.
The reason it survived the review is that the four clients were not variations on one consumer. They were four different products sharing a domain.
What the clients actually needed
The customer app is read-heavy and latency-sensitive: catalogue, search, a live order view. It wants aggressive caching and one fat response per screen, because every extra round trip on a mobile network is felt.
The delivery partner app is the opposite: write-heavy location updates from a device with intermittent connectivity, needing batching, tolerance for replay, and a much smaller payload budget. Its release cadence is tied to app store review, not to the web.
The restaurant portal lives on a screen in a kitchen for twelve hours at a stretch — long-lived sessions, a push channel for the order queue, and a hard requirement that it degrade visibly rather than silently.
The admin console reads broadly across every domain at trivial volume, with an entirely different authorisation surface and no interest in any of the performance work above.
Put those four behind one API and every change negotiates with three consumers that do not care. The customer app's caching strategy becomes an argument in the admin backlog. The partner app's release freeze blocks a web fix.
A BFF per client earns its keep when at least two of three are true: release cadences differ, response shapes differ materially, and ownership differs. One out of three is not enough.
The duplication is shallow — and that is the load-bearing claim
What repeats across four BFFs is orchestration and shaping: call these services, compose this payload, apply this client's auth and rate limits. What must not repeat is business rules. Pricing, promotion eligibility, order state transitions and driver assignment live in the domain services behind the gateway, and each BFF is a translation layer with no opinions of its own.
That distinction is the whole argument. If a rule ever needs changing in two BFFs, the pattern has failed and you have built four small monoliths. It is worth making that an explicit review question rather than a hope.
What you are signing up for
Four gateways means four pipelines, four sets of dashboards and alerts, four places a dependency version can drift, and four contracts that can quietly diverge from the services behind them. None of that is fatal, but all of it has to be planned rather than discovered: shared libraries for cross-cutting concerns, generated clients from a single service contract, and one observability convention so an on-call engineer reads all four the same way.
The case where we would not do it: one client, or clients whose response shapes are nearly identical and ship on the same cadence. Then you have paid the operational tax and bought nothing, and a single gateway with per-client response profiles is the better answer.
The honest summary is that a BFF is not an architectural upgrade. It is a place to put client-specific mess so it stops leaking into your domain — and it is worth having exactly as many of them as you have genuinely different clients.