ИТ развој

System Architecture: Why Your API Gateway Needs a Brain

Архитектура на системот: Зошто на вашиот API-портал му е потребен мозок

An API gateway begins as a convenient front door: one hostname, one authentication check, a few routes. Then the system grows. Services multiply, clients develop different needs, and a harmless-looking proxy becomes a place where business-critical decisions quietly accumulate.

That is why an API gateway needs a brain. Not a giant application that knows everything about every service, but a deliberate control layer that can make consistent decisions about traffic, identity, resilience, and visibility. Without that layer, complexity does not disappear. It leaks into every client and every backend service.

A gateway is more than a reverse proxy

A reverse proxy forwards requests. A thoughtful gateway interprets them in the context of a system.

For example, a client request for an account dashboard may require an authenticated user, tenant-aware rate limiting, calls to several internal services, a deadline, and a response shaped for a mobile application. If every client must understand those details, the public API becomes tightly coupled to internal architecture. Renaming a service, splitting a database, or changing an internal protocol becomes an expensive client migration.

The gateway should provide a stable edge while allowing the internals to evolve. Its “brain” is the collection of policies and orchestration decisions that belong at that edge.

  • Authenticate requests and establish trusted identity context.
  • Authorize broad API-level access before work enters the system.
  • Route requests to appropriate backend capabilities.
  • Apply rate limits, quotas, request-size limits, and timeouts.
  • Translate public contracts into internal protocols when needed.
  • Aggregate responses only when that simplifies the client meaningfully.
  • Produce consistent logs, traces, metrics, and error responses.

The distinction matters because gateways can also become dangerous. A gateway that contains core domain rules turns into a distributed monolith with a particularly awkward deployment path. It should coordinate and enforce cross-cutting policy, not become the owner of orders, invoices, inventory, or customer state.

Keep domain ownership behind the boundary

A useful rule is simple: if a rule determines the truth of a business entity, the service that owns that entity should enforce it. The order service decides whether an order can be cancelled. The billing service decides whether a payment can be refunded. The gateway may reject a caller lacking the required scope, but it should not duplicate the refund rules.

This separation avoids a subtle failure mode. Suppose a gateway checks that a user may update a project, while the project service also has project membership logic. Over time, those checks drift. One path grants access the other denies, and security bugs become difficult to diagnose because both components appear reasonable in isolation.

Put identity verification and coarse-grained access controls at the gateway. Pass verified identity claims downstream in a form services can trust, using protected internal networking and a clear trust boundary. Let services perform resource-level authorization where they have the necessary data and domain context.

Make routing a policy, not a tangle of conditionals

Routing rules deserve the same care as application code. They should be reviewable, testable, and explicit about their matching behavior. A route definition needs more than a path and destination: it should describe allowed methods, authentication requirements, timeout budgets, retry behavior, and the handling of headers and request bodies.

Consider a PHP edge service that delegates to an internal profile service. The gateway should give the upstream a bounded deadline and preserve a correlation ID. It should not wait indefinitely simply because the client was willing to wait.

<?php

$correlationId = $_SERVER['HTTP_X_REQUEST_ID'] ?? bin2hex(random_bytes(16));

$context = stream_context_create([
    'http' => [
        'method' => 'GET',
        'header' => [
            "X-Request-ID: {$correlationId}",
            "X-User-ID: {$verifiedUserId}",
        ],
        'timeout' => 2,
        'ignore_errors' => true,
    ],
]);

$response = @file_get_contents(
    "http://profile-service.internal/v1/profiles/{$verifiedUserId}",
    false,
    $context
);

if ($response === false) {
    http_response_code(503);
    header('Content-Type: application/json');
    echo json_encode([
        'error' => 'profile_service_unavailable',
        'request_id' => $correlationId,
    ]);
    exit;
}

header('Content-Type: application/json');
header("X-Request-ID: {$correlationId}");
echo $response;

This is deliberately modest. Production code also needs careful URL construction, response-status handling, connection pooling where the runtime supports it, and a trusted authentication implementation. The architectural lesson is the deadline: every network hop consumes part of a finite request budget.

Aggregation should earn its complexity

A gateway can reduce client round trips by composing a response from multiple services. That can be valuable for dashboards, product pages, and mobile clients operating on variable networks. But aggregation also increases coupling and expands the blast radius of a dependency failure.

Before adding a composite endpoint, ask whether the client truly needs a unified representation or merely wants fewer requests. If the latter, HTTP/2, caching, or a purpose-built backend-for-frontend may be a better answer. If a composite response is justified, decide explicitly what happens when one dependency fails.

  • Fail the whole response when every component is essential.
  • Return partial data only when the contract clearly identifies absent sections.
  • Use cached or stale-but-acceptable data only where correctness permits it.
  • Set separate time budgets for each downstream call; do not let one slow dependency consume the entire request.

Parallel calls can reduce latency, but only when the gateway has concurrency controls. Unbounded fan-out turns a busy gateway into a multiplier of downstream load. A slow database can then trigger more waiting connections, more retries, and eventually a broader outage.

Retries are a reliability feature only when bounded

Retries often look like resilience and behave like amplification. Retrying a failed read may be reasonable if the operation is idempotent, the failure is plausibly transient, and the retry fits within the deadline. Retrying a payment creation without an idempotency key can create duplicate charges.

At the gateway, retry policy should be narrow: a small number of attempts, jittered backoff, and clear rules for eligible errors. A client may retry too, so coordinate ownership of retries rather than allowing every layer to repeat the same request. Circuit breaking and load shedding are equally important. Sometimes the healthiest response is a fast, understandable failure that protects the rest of the system.

Observability is part of the gateway’s job

The gateway sees the request journey in a way individual services cannot. It should generate or accept a validated request ID, forward trace context, record route and upstream outcome, and measure latency by dependency. Logs should avoid secrets, authorization headers, raw credentials, and unnecessary personal data.

A useful operational question is not merely “Did the gateway return a 500?” It is “Which route failed, which upstream call consumed the budget, for which deployment version, and how many requests were affected?” Good gateway telemetry turns an incident from guesswork into a bounded investigation.

Give the edge a brain, not a second business system

The best API gateways are disciplined. They make common policies consistent, protect services from unsafe traffic, and present clients with stable contracts. They do not hoard domain knowledge or hide every architectural weakness behind another route.

Think of the gateway as the system’s air-traffic controller. It needs awareness, rules, limits, and clear signals. But it should not fly the planes. When that boundary remains clear, the gateway becomes a force multiplier for performance, maintainability, and safer change.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.