Architecture Patterns for API Design: Integration Overview

Summary

A technical overview of core architectural patterns for API design and service integration in distributed systems. These patterns address different concerns in system architecture: centralised access control, client-specific optimizations, read/write segregation, domain separation, data aggregation, and interface simplification.

Core Patterns Covered:

  • API Gateway: Centralised entry point for routing and managing API requests

  • Backend for Frontend (BFF): Client-specific backend adaptations

  • Command-Query Responsibility Segregation (CQRS): Separation of read and write operations

  • API Design in Bounded Contexts: Domain-aligned API structuring

  • Aggregator Pattern: Composition of data from multiple services

  • API Facade Pattern: Simplification layer for complex or legacy systems

Each pattern serves distinct architectural needs and can be implemented independently or combined based on system requirements. The guide includes implementation considerations, use cases, and decision criteria for pattern selection.

Patterns

  1. API Gateway

Client → API Gateway → Microservices/Bounded Contexts
  • Purpose: Single entry point that routes requests to appropriate services

  • When to use:

    • Need centralised authentication/authorization

    • Want to reduce round trips from client

    • Need request/response transformation

    • Want to hide internal service structure from clients

  • Example use case: Mobile app needs data from multiple services but wants single API endpoint

  1. Backend for Frontend (BFF)

Mobile App → Mobile BFF  → Services
Web App    → Web BFF     → Services
Desktop    → Desktop BFF → Services
  • Purpose: Tailored backend for specific frontend needs

  • When to use:

    • Different clients need different data shapes

    • Want to optimise for specific client needs

    • Need to reduce unnecessary data transfer

  • Example use case: Mobile app needs lighter payload than web app

  1. CQRS (Command Query Responsibility Segregation)

Command side (writes)
class CampaignCommandAPI:
    def deactivate_campaign(self, id: str):
        command = DeactivateCommand(id)
        command_bus.send(command)

# Query side (reads)
class CampaignQueryAPI:
    def get_campaign(self, id: str):
        return query_db.get_campaign_view(id)
  • Purpose: Separate read and write operations

  • When to use:

    • Read and write operations have very different requirements

    • Need different optimization for reads vs writes

    • Complex reporting needs

  • Example use case: High-read, low-write system like product catalog

  1. API Design in Bounded Contexts

Shipping Context API
class ShippingAPI:
    def create_shipment(self, order_id: str):
        pass

# Payment Context API
class PaymentAPI:
    def process_payment(self, order_id: str):
        pass
  • Purpose: Each bounded context has its own API aligned with its domain

  • When to use:

    • Different teams own different domains

    • Need clear separation of concerns

    • Want to maintain domain integrity

  • Example use case: E-commerce system with separate teams for orders, shipping, payments

  1. Aggregator Pattern

Client → Aggregator → Multiple Services
  • Purpose: Combines data from multiple services into single response

  • When to use:

    • Need to reduce client requests

    • Complex data aggregation requirements

    • Composite services

  • Example use case: E-commerce product page needing product details, inventory, pricing, reviews, and related products from different services in one call

  1. API Facade Pattern

Client → Facade → Legacy/Complex System
  • Purpose: Simplifies complex APIs or legacy systems

  • When to use:

    • Modernising legacy systems

    • Simplifying complex interfaces

    • Creating consistent API layer

  • Example use case: Modern REST API interface for a legacy system, converting complex multi-step operations into simple, single endpoints

Comparison Table

Pattern          | Main Benefit           | Complexity | Best For
-----------------+------------------------|------------+------------------
API Gateway      | Centralization        | Medium     | Microservices
BFF              | Client Optimization   | High       | Multiple Clients
CQRS             | Performance           | High       | Complex Reads
Bounded Context  | Domain Separation     | Medium     | Large Teams
Aggregator       | Data Composition      | Medium     | Complex Data
Facade           | Interface Simpl.      | Low        | Legacy Systems

Decision Framework

  1. Start with Bounded Contexts if:

    • Large system

    • Multiple teams

    • Clear domain separation

  2. Add API Gateway if:

    • Multiple clients

    • Need central authentication

    • Want to hide internal structure

  3. Add BFF if:

    • Different client needs

    • Performance critical

    • Client-specific optimization needed

  4. Add CQRS if:

    • Read/write patterns very different

    • Complex reporting needs

    • Performance bottlenecks

  5. Add Aggregator if:

    • Multiple service calls needed for single operation

    • Need to reduce client-side data assembly

    • Complex data relationships across services

  6. Add Façade if:

    • Working with legacy systems

    • Need to simplify complex APIs

    • Want to standardize interfaces

Common Combinations

  1. API Gateway + BFF:

Mobile → Mobile BFF → API Gateway → Services
Web    → Web BFF    → API Gateway → Services
  1. API Gateway + CQRS:

Client → API Gateway → Command API
                     → Query API
  1. API Gateway + Aggregator:

Client → API Gateway → Aggregator → Multiple Services
  1. Facade + Legacy Integration:

Client → API Gateway → Facade → Legacy System
                     → Modern Services
  1. Complete Stack:

Mobile → Mobile BFF → API Gateway → Aggregator → Services
Web    → Web BFF    ↗             → Facade → Legacy Systems
                                  → Command/Query APIs → Bounded Contexts

Tips

  • Start simple

  • Add complexity only when needed

  • Each pattern adds maintenance overhead

  • Patterns can be combined as system grows