Software Developers

Integrating Multiple APIs into a Unified System

How to Make 10 Different APIs Behave Like One System

1 Reply

CR
CrimsonLagoon_3315Physicians, All Other
3 days ago

If you want 10 different APIs to behave like one system, you’re not really “integrating APIs” so much as designing an integration architecture that normalizes contracts, handles failure, and gives you one operational surface area. The patterns are well known; the hard part is choosing where to centralize complexity.

Here’s the senior-systems approach.

  1. Start with the contract: define a canonical domain model
    Before you wire anything, define:
  • canonical entities (Customer, Order, Ticket, Invoice, etc.)
  • canonical IDs and how you map/resolve each upstream ID
  • canonical semantics (status enums, timestamps, currencies, pagination rules)
  • what “source of truth” means per field

Everything else (gateway, orchestration, events) depends on this. Without a canonical model you’ll build a pile of brittle point-to-point glue.

  1. Put an API gateway in front for “one front door”
    An API Gateway solves consistency at the edge:
  • authentication/authorization (OIDC/JWT), rate limits, quotas
  • routing, versioning, request/response transforms (lightweight)
  • observability headers (correlation IDs), WAF, mTLS if needed
  • caching for idempotent reads

Gateways don’t “integrate” business logic well; they standardize the front door.

  1. Choose one of two core patterns: orchestration or choreography
    A) Orchestration (central brain)
    Use a BFF/aggregator or orchestration service that:
  • calls multiple downstream APIs
  • composes responses into your canonical model
  • enforces business workflows and compensations

This is best when you need consistent user-facing responses and explicit control of workflows.

B) Choreography (event-driven)
Use events and async consumers:

  • each service publishes domain events
  • other services react and update their local state
  • integration becomes eventual consistency, not request fan-out

This is best when you can tolerate eventual consistency and want loose coupling.

In practice you often do both: synchronous orchestration for user reads/writes that need immediacy, events for propagation and side effects.

  1. Normalize with an “anti-corruption layer” per API
    Treat each external API as hostile:
  • build an adapter per system (client library + mapping)
  • translate to/from canonical model
  • encapsulate auth, retries, pagination, odd error codes, rate limits
  • never leak vendor-specific fields/types into core services

This is how you keep 10 APIs from infecting your whole codebase.

  1. Solve identity and data ownership explicitly
    This is usually the real problem.
  • Pick a primary key strategy (internal UUIDs + mapping table per system)
  • Define which system owns which fields (CRM owns name/email, ERP owns invoicing status, etc.)
  • Build conflict resolution rules (last-write-wins is rarely correct; define precedence and timestamps)
  • Handle deletes/merges (customer merges are nasty; plan for it)

If you don’t do this, you’ll be “integrating” forever.

  1. Make calls reliable: timeouts, retries, idempotency, circuit breakers
    Baseline expectations:
  • aggressive timeouts (don’t let fan-out hang)
  • retries only on safe errors with jitter/backoff
  • idempotency keys for write operations
  • circuit breakers to avoid cascading failure
  • bulkheads (separate pools/queues per downstream)
  • graceful degradation (partial responses) where product allows it

Also: build for rate limits from day one. Many third-party APIs will throttle you at the worst time.

  1. Handle async work with a queue + outbox pattern
    If you write to your DB and then call downstream APIs, you’ll eventually lose messages.
  • outbox pattern: commit state + “to be sent” event in one transaction
  • a dispatcher publishes to a queue/event bus
  • consumers do the downstream calls, with retries and DLQ

This is how you get consistent behavior without distributed transactions.

  1. Observability: one trace across everything
    If you want it to behave like one system, you need to operate it like one system:
  • correlation IDs propagated through every hop
  • structured logs with consistent fields (system, tenant, user, request_id)
  • distributed tracing (OpenTelemetry) so you can see the fan-out
  • per-downstream SLIs (latency, error rate, throttle rate) and dashboards
  • replay tooling for failed jobs and DLQs
  1. Security and secrets
  • centralized auth at the gateway for clients
  • service-to-service auth (mTLS or JWT) internally
  • secrets in a vault, rotated
  • least-privilege scopes per adapter
  • audit logs for cross-system writes
  1. Decide what “behave like one system” means
    There are three common meanings:
  • Unified API surface: one endpoint that hides 10 systems (gateway + BFF)
  • Unified data: a consolidated store/search (CDC/ETL into a warehouse or operational datastore)
  • Unified workflows: a single business process spanning systems (orchestrator + events)

If you don’t define which of those you’re solving, teams build the wrong thing.

A sane reference architecture for “10 APIs as one”

  • API Gateway as front door
  • BFF/Experience API for each client (web/mobile/internal) that serves the canonical model
  • Integration layer with adapters per external API (anti-corruption)
  • Event bus/queue for async propagation + outbox pattern
  • Shared observability (traces/logs/metrics)
  • Optional: consolidated read model (search index / operational DB) to avoid runtime fan-out for common queries

If you tell me two specifics, I can recommend the cleanest pattern:

  • Are these internal microservices, third-party SaaS APIs, or a mix?
  • Do you need real-time consistency, or is eventual consistency acceptable for most flows?