BFF pattern
Backend-for-Frontend — one backend per client experience (mobile, web, partner), shaping responses for that UI's needs.
Not a new domain service — a thin orchestration layer over core microservices.
Why not one API for all clients?
| Client | Needs |
|---|---|
| Mobile | Small payloads, batch endpoints, offline-tolerant |
| Web SPA | Rich graphs, fewer round-trips |
| Partner | Strict SLA, API keys, webhooks |
Single generic API → over-fetching, versioning conflicts, release coupling.
BFF responsibilities
- Aggregate multiple downstream calls
- Translate errors to client-friendly shapes
- Client-specific auth (mobile device attestation)
- Owns GraphQL schema or REST surface for that client
Must not own core business rules — checkout pricing stays in Order/Pricing services.
Aggregation patterns
Parallel fan-out:
Promise.all([getUser(), getOrders(), getRecommendations()])
Watch tail latency — p99 = max(subcall p99), not sum.
Cached denormalized reads — Redis materialized view for home feed; invalidate on events.
GraphQL gateway — federated subgraphs; BFF team owns supergraph composition (Communication → GraphQL).
Feed, timeline & notification fan-out
High-follower read paths are the classic Staff deep dive — storage layout is in Databases → Search; aggregation lives here.
Fan-out on read vs fan-out on write
| Approach | Write cost | Read cost | Freshness | Pick when |
|---|---|---|---|---|
| Fan-out on read | O(1) per post | O(followers) or cached merge | Pull model; cache helps | Most users have modest follower counts |
| Fan-out on write | O(followers) per post | O(1) per home timeline read | Push precomputed timelines | Read-heavy timelines, celebrity problem handled separately |
| Hybrid | Normal users: write fan-out; celebrities: read merge | Mixed | Industry default (Twitter-style) | Large social products at scale |
Celebrity problem: one user with 100M followers — never fan-out on write to all timelines. Use a celebrity bucket: merge celebrity posts at read time from a hot cache or dedicated shard.
BFF role in feed assembly
GET /home-feed
→ BFF loads timeline IDs (precomputed or cache)
→ parallel fetch post bodies + authors (batch APIs)
→ optional ranking service for "For You"
→ merge, truncate payload for mobileMaterialized feed cache in Redis (feed:userId → list of post IDs) — invalidate or append on new post event. Cross-reference Databases → Application Caching for stampede and invalidation.
Notifications
Fan-out on write is common: event → queue → per-device delivery workers. Idempotent consumers (Communication → idempotency). Rate-limit per user to prevent notification storms.
Timed case practice → System Design Use Cases (news feed, notification system).
Anti-patterns
- Mega-BFF — one team gatekeeping all product APIs
- BFF calling BFF — layers of aggregation
- Business logic creep — discount rules in BFF because "it's easier"
BFF vs API gateway
| Gateway | BFF | |
|---|---|---|
| Audience | Often all external traffic | Specific client type |
| Logic | Policy, routing | Aggregation, shaping |
| Count | 1–2 per environment | 1 per client surface |
Can combine: mobile BFF behind same gateway with path routing /mobile/*.
Staff interview
"Where does the BFF live in deploy topology?" — same cluster as edge, autoscales with client traffic, stateless, no private data store (cache only).
Further Reading
Hands-On Tasks (Optional)
Architecture drills and whiteboard exercises. Assumes Communication & Data Transfer and Distributed Systems fundamentals.
- Design BFF split for mobile vs web15m
Same product, different payloads: mobile needs minimal fields + push tokens; web needs rich admin. One BFF or two? What stays in domain services?