No matter what the books say, nobody likes making 20 requests to render a page.
3 Aggregation Patterns your consumers will love.
The reality is that more calls to the server add friction to development, and friction is No Bueno.
Nobody enjoys making 20 API requests to render a single page. This not only frustrates users but also adds unnecessary complexity to both the FrontEnd and the BackEnd.
If you're working with microservices or any backend-heavy architecture, aggregation patterns can help.
Let's explore three of my favorites.
Thank you to our sponsors who keep this newsletter free:
Multiplayer's Platform Debugger provides deep session replays with every detail you need to find and fix a bug. From Frontend screens to Backend traces, metrics, and logs, all in one place. Debug faster and fix customer problems more easily, so you and your team can stay focused on building great software, not combing through APM data.
1. Central Aggregating Gateway
The Central Aggregating Gateway acts as middleware between user interfaces and microservices. It filters and consolidates calls, reducing the number of requests the Frontend needs to make.
How It Works
The Frontend sends a single request to the gateway.
The gateway makes multiple Backend calls, aggregates the data, and returns a unified response.
Pros:
Simplifies client-side logic by reducing the number of API calls.
Provides a centralized place for common tasks like authentication, rate limiting, and logging.
Cons:
It can become a bottleneck if many teams need to make changes.
Requires significant coordination across teams, slowing down development.
Risks becoming a single point of failure.
Best Practices:
Avoid business logic in the gateway. Keep it focused on aggregation and filtering.
Use caching and rate limiting to manage load.
2. Backend for Frontend (BFF)
A Backend for Frontend (BFF) provides a dedicated Backend tailored to the specific needs of a Frontend. This approach separates the concerns of different UIs (e.g., mobile, web), with each having its own BFF.
How It Works
Each team owns their Frontend and its corresponding BFF.
The BFF fetches, processes, and returns only the data needed for its Frontend.
Pros:
Reduces unnecessary data fetching and optimizes performance.
Eases management and evolution, as the same team maintains the UI and the BFF.
Minimizes cross-team coordination, enabling faster development.
Cons:
Increases duplication across BFFs (e.g., repeated authentication logic).
Leads to maintenance overhead as the number of BFFs grows.
Best Practices:
Share common logic (e.g., authentication or logging) across BFFs using libraries or services.
Keep BFFs lightweight and tightly focused on their Frontend's needs.
3. GraphQL
GraphQL is a query language for APIs that allows clients to request only the data they need. It solves over-fetching and under-fetching issues by enabling precise, flexible queries.
How It Works
Clients send a query specifying exactly what data they need.
GraphQL executes the query, fetching data from multiple services if necessary, and returns a single response.
Pros:
Reduces the number of API calls and the volume of transferred data.
Provides flexibility to clients, enabling them to adjust queries without Backend changes.
Can act as a flexible aggregation layer, similar to a BFF.
Cons:
Complex and requires significant upfront investment.
Poorly designed schemas or unguarded queries can lead to performance issues.
Risks becoming a bottleneck, similar to a central gateway.
Best Practices:
Use query complexity guards (e.g., depth limits, cost analysis) to prevent inefficient queries.
Educate Frontend teams about the impact of their queries on Backend systems.
Leverage tools like persisted queries to improve performance and security.
When to Use Each Pattern?
Central Aggregating Gateway: Use when simplicity and centralized control outweigh potential bottleneck concerns. Ideal for monolithic-to-microservices transitions.
Backend for Frontend (BFF): Use when UIs have distinct data needs, and teams want more ownership of their stack. Ideal for multi-platform systems (e.g., mobile, web, IoT).
GraphQL: Use when flexibility and precise data fetching are key. Ideal for dynamic, client-driven applications or when serving multiple consumers with varying data requirements.
Final Thoughts
Each aggregation pattern has strengths and trade-offs. The best choice depends on your system's architecture, team structure, and performance requirements.
Personally, I think BFF strikes a good balance between performance and team autonomy. However, GraphQL can be game-changing when flexibility and dynamic data needs are high.
In practice, a hybrid approach often works best.
GraphQL + BFF: A BFF using GraphQL internally for querying microservices, offering REST endpoints to Frontends.
Gateway + BFF: Use a central gateway for shared functionality (e.g., authentication) and BFFs for UI-specific needs.
GraphQL + Gateway: Place GraphQL behind a central gateway to manage rate limiting, authentication, and caching.
Good API design is like great architecture—invisible when done well, painful when done poorly.
System Design Classroom is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.
Articles I enjoyed this week
8 Must-Know Strategies to Build Scalable Systems by
The whole University IT system stopped working by
andThe easy road to become a TOP performer software engineer: Simplifying productivity by
Writing as a software engineer by
Thank you for reading System Design Classroom. If you like this post, share it with your friends!
GraphQL offers a more efficient and flexible way to work with APIs. While it has its challenges, such as increased complexity and potential performance issues, its benefits in terms of data retrieval flexibility and efficiency can significantly enhance the functionality of web and mobile applications
Great article, Raul! 🙌
Great Article! Thanks for sharing!