Skip to content

PATTERN Cited by 1 source

Unified GraphQL Backend-For-Frontend (UBFF)

When to use

You have:

  • Many frontend surfaces (Web, iOS, Android, partner APIs) consuming roughly the same business data.
  • Many backend microservices owning that data (dozens of domains).
  • An accumulated cost from a per-surface BFF deployment: duplicated logic, inconsistent customer experience across platforms, fragmented auth/observability, Conway's-Law drift between surfaces (see concepts/backend-for-frontend).
  • Capacity to commit to one schema across the company (unified graph discipline), with a dedicated platform team.
  • A preference for simpler operations and unified tooling over per-domain deploy independence.

The pattern

Deploy a single GraphQL service that:

  • Owns one unified schema spanning all product domains.
  • Is contributed to by every product team via a shared-ownership monorepo with explicit contribution principles.
  • Is consumed by every frontend surface (Web, iOS, Android, partners, internal) through the same endpoint.
  • Follows a strict No Business Logic principle — aggregates and shapes only; business logic lives in downstream presentation- layer backends.
      all frontends (Web, iOS, Android, internal, partner)
             ┌──────────────────────────────┐
             │   Single UBFF GraphQL service │
             │   (one schema, one deploy-     │
             │    ment artifact, one repo)    │
             └────────────────┬──────────────┘
                presentation-layer backends
              (platform + domain specific logic)
                    domain microservices

Why not Apollo Federation?

The explicit anti-pattern within this pattern. Apollo Federation is many services, one schema, composed at a router at runtime. The UBFF is one service, one schema, no runtime composition. The trade named in Zalando's 2021 post:

"instead of having multiple Graphs connected via a library and gateway we have a single service at Zalando which connects all the domains in a single schema Graph. This has tradeoffs which we have addressed as mentioned here, since we gain by keeping a single Graph in terms of tooling, deployment and governance."

The UBFF wins on: unified tooling, single deployment artifact, single governance surface. It loses on: per-domain deploy independence (all domains share the same release train) and per-domain runtime isolation (a bad resolver in domain X can degrade domain Y).

Canonical wiki instance: Zalando UBFF

Zalando's Unified Backend-For-Frontends GraphQL (Source: sources/2021-03-03-zalando-how-we-use-graphql-at-europes-largest-fashion-e-commerce-company):

  • Production since end of 2018; development began H1 2018.
  • 12+ domains in one monorepo under shared ownership.
  • 150+ contributors (from 50 in 2020); 200+ consuming developers across 25-30 feature teams.
  • >80% of Web and >50% of App use cases served.
  • Runs on graphql-jit — Zalando's open-source JIT-compiled GraphQL executor.
  • Uses an Entity system as first-class schema citizens (Product, Campaign, …), cross-linked into "a dense graph."
  • Deployed with per-platform bulkhead — separate deployments for Web and mobile Apps, same codebase.

How the monorepo-as-god-component risk is managed

The post names the concern: a 12+ domain monorepo is a God Component design smell. Two mitigations:

  1. Architectural: shared ownership across 12+ teams guided by a documented contribution framework; no single team can unilaterally reshape the graph.
  2. Operational: reliability patterns (Circuit Breakers, Timeouts, Retry) plus Bulkhead at the deployment level.

Adoption levers (from Zalando's retrospective)

Four levers scaled adoption 50 → 150+ contributors and 70 → 200 consumers in one calendar year:

  • One-stop-shop Documentation (Divio framework) with embedded GraphiQL + Voyager + practice exercises.
  • Dedicated support chat for users and contributors.
  • Company-wide training (one session hit 150+ attendees).
  • Schema-design consultation hours provided by the platform team for every new domain integrating into the graph.

Hard problems this pattern introduces

  • Single-service deploys. No per-domain deploy independence — every team's changes ride the same release train.
  • Single-service blast radius. A bad resolver in domain X can degrade latency or error rates for domain Y. Per-platform bulkhead only splits by Web/App, not by domain.
  • Schema governance is load-bearing. Without a dedicated governance team and explicit contribution principles, the graph fractures.
  • Scaling the execution engine. At Zalando's density the reference graphql-js interpreter was insufficient; they had to build and open-source graphql-jit.
  • The Entity system is hard to get right. Cross- domain first-class entity identity is a schema-design problem every organisation has to solve uniquely.

Contrast with adjacent patterns

Seen in

Last updated · 476 distilled / 1,218 read