:PROPERTIES:
:ID:       49b195c8-e116-40ca-86e8-62c65dbb5a4f
:END:
#+title: API Architecture
#+filetags: :technical:notes:api:
#+category: Career Concepts

*Z notes on API architecture - companion to [[id:56fabaf6-e8aa-45d0-a1c1-89f247f0a93f][APIM notes]]*

* 1. What is API Architecture?

API architecture is the set of rules, patterns, and structural decisions that govern how APIs are designed, exposed, consumed, and maintained across a system. It sits above individual API implementation - it's about *how APIs fit together* as a platform.

In Microlise's context: the APIOps pipeline, the APIM gateway layer, OpenShift clusters, and the OpenAPI specs are all artefacts *of* an architectural decision. Understanding the architecture behind them makes the pipeline choices make sense.

* 2. Architectural Styles

Different styles define how clients and servers communicate. These are not mutually exclusive - a platform can expose multiple styles simultaneously (e.g. REST externally, gRPC internally).

** 2.1 REST (Representational State Transfer)

The dominant style for public and partner APIs. Key constraints:

- *Stateless*: Each request must contain all the context needed to fulfil it. No session state is stored server-side between calls.
- *Resource-oriented*: APIs are modelled around nouns (resources), not verbs (actions).
  - Good: ~GET /vehicles/{id}~
  - Bad: ~POST /getVehicle~
- *Uniform interface*: Standard HTTP verbs carry semantic meaning:
  | Verb   | Meaning                        |
  |--------+--------------------------------|
  | GET    | Read a resource                |
  | POST   | Create a resource              |
  | PUT    | Replace a resource entirely    |
  | PATCH  | Partially update a resource    |
  | DELETE | Remove a resource              |
- *Layered system*: Clients don't know if they're talking to the real backend or a gateway/proxy/cache. This is exactly what Microlise's APIM layer provides.
- *Cacheable*: Responses should declare whether they can be cached, enabling CDN and client-side optimisation.

OpenAPI Specification (OAS/Swagger) is the standard way to *describe* a REST API. The ~spec~ referred to throughout the APIOps pipeline is this document.

** 2.2 GraphQL

A query language for APIs developed by Meta. Instead of fixed endpoints, clients send a query describing exactly what data they need.

- Single endpoint: ~POST /graphql~
- Client drives the shape of the response - no over-fetching or under-fetching.
- Good for: complex, interconnected data models; front-end teams who iterate quickly.
- Trade-off: harder to cache, more complex server-side resolver logic, linting/governance is less mature than OAS.

Not currently the Microlise APIM pattern but worth understanding as a contrast.

** 2.3 gRPC (Google Remote Procedure Call)

Uses Protocol Buffers (protobuf) as the interface definition language and HTTP/2 as transport.

- Strongly typed contracts defined in ~.proto~ files.
- Extremely high performance - binary serialisation, multiplexed streams.
- Ideal for: internal service-to-service calls, microservices, high-throughput scenarios.
- Trade-off: not human-readable, harder to test with standard tooling (curl, Postman), less browser-friendly.

Think of gRPC as what might live *behind* an API gateway - internal communication between microservices - while REST/OAS faces outward toward customers.

** 2.4 AsyncAPI / Event-Driven APIs

Not all APIs are request-response. Event-driven APIs use messaging patterns:

- *Webhooks*: Server POSTs to a client-registered URL when an event occurs.
- *WebSockets*: Persistent bi-directional connection between client and server.
- *Server-Sent Events (SSE)*: One-way stream from server to client.
- *Message queues* (Kafka, RabbitMQ, Azure Service Bus): Decoupled async messaging.

AsyncAPI is the OAS equivalent for event-driven interfaces - a specification format for documenting these contracts.

* 3. API Gateway Pattern

This is the core of what APIM implements. An API gateway sits as an intermediary between consumers (customers, internal teams) and backend services.

#+begin_src mermaid
flowchart TD
    EC[External Consumer]
    GW["API Gateway / APIM
    ────────────────────
    Auth · Rate Limiting
    Transforms · Routing
    Logging · Caching"]
    SA["Service A\n(OpenShift)"]
    SB["Service B\n(OpenShift)"]
    SC["Service C\n(OpenShift)"]

    EC --> GW
    GW --> SA
    GW --> SB
    GW --> SC
#+end_src

** 3.1 What the gateway does

| Concern                  | What it means                                                                 |
|--------------------------+-------------------------------------------------------------------------------|
| *Authentication*         | Verifies who the caller is (OAuth2 tokens, API keys, mutual TLS)              |
| *Authorisation*          | Determines what the caller is allowed to do (scopes, claims)                  |
| *Rate limiting*          | Caps requests per second/minute/hour per consumer or globally                 |
| *Throttling*             | Gracefully slows or queues excess requests rather than rejecting them         |
| *Request transformation* | Rewrites headers, payloads, or paths before forwarding to backends            |
| *Response transformation*| Strips internal fields, reformats responses for the consumer contract         |
| *Routing*                | Directs traffic to the correct backend based on path, headers, or content     |
| *Load balancing*         | Distributes traffic across backend instances                                  |
| *Caching*                | Stores responses to reduce backend load for idempotent requests               |
| *Observability*          | Centralises access logs, metrics, and tracing across all APIs                 |

In Azure APIM specifically, these concerns are implemented as *policies* - XML-based declarative rules that run at gateway level.

** 3.2 Reverse Proxy vs API Gateway

The Microlise notes reference a Reverse Proxy pipeline alongside APIM. These are related but distinct:

| Aspect            | Reverse Proxy                             | API Gateway                                      |
|-------------------+-------------------------------------------+--------------------------------------------------|
| Primary purpose   | Routing and TLS termination               | Full API lifecycle management                    |
| Protocol awareness| Layer 4/7 (TCP/HTTP)                      | Layer 7, API-aware (understands REST, OAS)       |
| Policy engine     | Minimal (Nginx/HAProxy config)            | Rich (auth, transforms, quotas, subscriptions)   |
| Developer portal  | No                                        | Yes - consumer-facing API catalogue              |
| Examples          | Nginx, HAProxy, Traefik                   | Azure APIM, Kong, AWS API Gateway                |

A common pattern (and likely what Microlise uses) is: Reverse proxy handles ingress and TLS termination → traffic forwarded to APIM for policy enforcement → APIM routes to OpenShift services.

* 4. API Design Principles

** 4.1 Contract-First Design

Define the OpenAPI spec *before* writing implementation code. The spec is the source of truth.

Benefits:
- Frontend/consumer teams can mock and build against the spec immediately.
- Linting pipelines (like the one in APIOps gated build) can enforce governance before any code ships.
- Breaking change detection is automated.

The APIOps pipeline enforces this: the spec is committed to source control, linted, reviewed by the API Governance Council, and only then does the publish pipeline sync it to APIM environments.

** 4.2 Versioning Strategies

APIs evolve. Versioning prevents changes from breaking existing consumers.

| Strategy            | Example                               | Trade-offs                                          |
|---------------------+---------------------------------------+-----------------------------------------------------|
| *URI versioning*    | ~/v1/vehicles~, ~/v2/vehicles~        | Explicit, cacheable, easy to route. Pollutes paths. |
| *Header versioning* | ~Accept: application/vnd.api.v2+json~ | Clean URIs. Harder to test, less cache-friendly.    |
| *Query param*       | ~/vehicles?version=2~                 | Simple but considered poor practice for REST.       |

URI versioning is the most common and is what Azure APIM handles well via routing rules.

** 4.3 Breaking vs Non-Breaking Changes

Knowing what constitutes a breaking change is critical for API governance (i.e. why the API GC reviews spec PRs).

| *Non-breaking (additive)*              | *Breaking*                                       |
|----------------------------------------+--------------------------------------------------|
| Adding a new optional field to response | Removing or renaming a field                    |
| Adding a new endpoint                  | Changing a field's type                          |
| Adding a new optional query parameter  | Making an optional parameter required            |
| New enum value (with caution)          | Changing HTTP status codes for existing scenarios|
|                                        | Changing authentication schemes                  |

** 4.4 Resource Naming Conventions

- Use *nouns*, not verbs: ~/journeys~ not ~/getJourneys~
- Use *plural* for collections: ~/vehicles~ not ~/vehicle~
- Use *kebab-case* for multi-word: ~/driver-events~ not ~/driverEvents~
- Nest to show ownership, but limit depth: ~/vehicles/{id}/journeys~ is fine; ~/vehicles/{id}/journeys/{jid}/events/{eid}/metadata~ is not.
- Never expose internal implementation details in paths (~/{internalDatabaseId}~ leaks schema).

** 4.5 HTTP Status Codes

Correct status codes are part of the API contract. Misuse breaks consumers who rely on them.

| Code | Meaning                       | When to use                                        |
|------+-------------------------------+----------------------------------------------------|
| 200  | OK                            | Successful GET, PUT, PATCH                         |
| 201  | Created                       | Successful POST that created a resource            |
| 204  | No Content                    | Successful DELETE or action with no response body  |
| 400  | Bad Request                   | Client sent malformed/invalid data                 |
| 401  | Unauthorized                  | Not authenticated (no or invalid token)            |
| 403  | Forbidden                     | Authenticated but not authorised for this resource |
| 404  | Not Found                     | Resource does not exist                            |
| 409  | Conflict                      | State conflict (duplicate, version mismatch)       |
| 422  | Unprocessable Entity          | Semantically invalid (e.g. invalid date range)     |
| 429  | Too Many Requests             | Rate limit exceeded                                |
| 500  | Internal Server Error         | Unhandled server-side failure                      |
| 503  | Service Unavailable           | Downstream dependency down, circuit breaker open   |

* 5. API Security Architecture

** 5.1 Authentication Patterns

| Pattern          | How it works                                               | Typical use                              |
|------------------+------------------------------------------------------------+------------------------------------------|
| *API Keys*       | Static key passed in header (~x-api-key~) or query param   | Simple, internal/partner APIs            |
| *OAuth 2.0*      | Token-based; client obtains a bearer token from auth server| Public APIs, delegated access            |
| *OpenID Connect* | OAuth 2.0 + identity layer (ID tokens, user info endpoint) | APIs that need to know *who* the user is |
| *Mutual TLS*     | Both client and server present certificates                | High-security service-to-service         |
| *JWT*            | Signed token carrying claims; verified without calling auth server | Stateless auth at gateway level |

Azure APIM supports all of these via policies. A common pattern: APIM validates the JWT at the gateway before the request ever reaches an OpenShift pod.

** 5.2 OAuth 2.0 Grant Types

| Grant type              | Use case                                                       |
|-------------------------+----------------------------------------------------------------|
| *Client Credentials*    | Machine-to-machine (no user involved). Most common for APIs.   |
| *Authorization Code*    | User-facing apps; user logs in and delegates access            |
| *Authorization Code + PKCE* | Same as above but for SPAs/mobile (no client secret)      |
| *Implicit* (deprecated) | Was used for SPAs - replaced by Auth Code + PKCE               |

** 5.3 Zero Trust at the API Layer

Zero Trust means: *never trust, always verify* - even internal services must authenticate.

Principles applied to APIs:
- Every service-to-service call requires a valid token (no implicit trust on the internal network).
- Tokens have minimum required scopes (principle of least privilege).
- mTLS between internal services adds a second layer even if a token is compromised.
- All traffic - internal and external - goes through the gateway and is logged.

* 6. API Lifecycle Management

This maps directly to the APIOps workflow in the APIM notes.

#+begin_src mermaid
flowchart LR
    Design[Design]
    Develop[Develop]
    Test[Test]
    Publish[Publish]
    Monitor[Monitor]
    Retire[Retire]

    Design --> Develop --> Test --> Publish --> Monitor --> Retire

    Design -.-> D1["OAS Spec\nContract First"]
    Develop -.-> D2["C# Project\nTemplates\nOpenShift"]
    Test -.-> D3["Gated Build\nPipeline\nLinting + API GC"]
    Publish -.-> D4["APIM Publish\nPipeline\nDev → Cert → Prod"]
    Monitor -.-> D5["Analytics\nDashboards\nAPIM Portal"]
    Retire -.-> D6["Deprecation\nNotices\nVersion Sunset"]
#+end_src

** 6.1 API Governance

The API Governance Council (API GC) referenced in the notes is the enforcement body for architectural standards. Common governance concerns:

- *Linting*: Automated rules against the OAS spec. The APIOps pipeline uses scripts from ~ApiManagement.Pipeline.AgentScripts~ to enforce this.
- *Review gates*: No spec change merges without GC approval - prevents inconsistent or insecure APIs reaching production.
- *Naming standards*: Enforced in the spec review (see §4.4).
- *Breaking change policy*: Defines how long old versions must be supported before retirement.
- *Security policy*: All APIs must use approved auth methods; no unauthenticated endpoints in production.

** 6.2 APIOps (GitOps for APIs)

APIOps applies GitOps principles to API management: the APIM configuration is stored as code in a Git repository and the pipeline is the only mechanism that changes APIM state.

Key properties:
- *Declarative*: The ~Microlise.APIOps~ repo describes the desired state of all APIs in APIM.
- *Versioned*: Every change is a PR - full audit trail.
- *Automated*: The publish pipeline does the two-way sync; no manual APIM portal edits.
- *Environment promotion*: Changes flow Dev  ->  Cert  ->  Prod, with a manual approval gate before Prod.

This is analogous to how Terraform or Helm work for infrastructure - the repo *is* the truth.

* 7. API Observability

An often-overlooked architectural concern. APIs you can't observe are APIs you can't operate.

** 7.1 The Three Pillars

| Pillar    | What it captures                                            | Tooling examples                   |
|-----------+-------------------------------------------------------------+------------------------------------|
| *Logs*    | Discrete events: requests, responses, errors, auth failures | Azure Monitor, ELK, Splunk         |
| *Metrics* | Aggregated numbers over time: latency, error rate, RPS      | Prometheus, Azure Metrics, Grafana |
| *Traces*  | End-to-end request journey across services                  | Jaeger, Zipkin, Azure App Insights |

** 7.2 Key API Metrics to Track

- *Latency*: p50, p95, p99 - not just average. Averages hide outliers.
- *Error rate*: 5xx rate (server errors) and 4xx rate (client errors) separately.
- *Throughput*: Requests per second - used to set rate limits and plan capacity.
- *Availability*: Uptime percentage. SLAs are usually defined here (99.9% = ~8.7h downtime/year).
- *Quota consumption*: How much of a consumer's rate limit are they using?

Azure APIM exposes all of these natively and can emit them to Azure Monitor.

** 7.3 Correlation IDs

Every request should carry a unique ~correlation-id~ (or ~x-request-id~) header. The gateway generates one if the client doesn't provide it and forwards it to all downstream services. This makes it possible to trace a single user request across multiple microservice logs.

#+begin_src mermaid
flowchart LR
    C[Client]
    APIM["APIM\ngenerates correlation-id: abc-123"]
    SA["Service A logs\nabc-123 · vehicle lookup"]
    SB["Service B logs\nabc-123 · journey history"]

    C --> APIM
    APIM --> SA
    SA --> SB
#+end_src

* 8. Microservices & API Design

The OpenShift deployment model in Microlise's stack implies microservices. API architecture must account for how services communicate internally vs. externally.

** 8.1 Internal vs External APIs

| Aspect         | Internal (East-West)                       | External (North-South)                       |
|----------------+--------------------------------------------+----------------------------------------------|
| Consumers      | Other microservices                        | Customers, partners, third parties           |
| Protocol       | gRPC, internal REST, message queues        | REST over HTTPS via APIM                     |
| Auth           | mTLS, service accounts, internal tokens    | OAuth2, API keys managed by APIM             |
| Discoverability| Service mesh / internal DNS                | Developer portal in APIM                     |
| Governance     | Team-level conventions                     | API GC, formal versioning, SLA commitments   |

** 8.2 API Aggregation / BFF Pattern

Backend for Frontend (BFF): a dedicated API layer tailored to a specific consumer (e.g. a mobile app, a portal). Instead of the consumer calling 5 microservices, a BFF aggregates them into a single call.

#+begin_src mermaid
flowchart LR
    MA[Mobile App]
    BFF[BFF: Mobile API]
    VS[Vehicle Service]
    JS[Journey Service]
    DS[Driver Service]

    MA --> BFF
    BFF --> VS
    BFF --> JS
    BFF --> DS
#+end_src

APIM policies can implement lightweight aggregation, but for complex cases a dedicated BFF service is cleaner.

** 8.3 Service Mesh (Complementary to API Gateway)

A service mesh (e.g. Istio, Linkerd) manages *internal* service-to-service communication within OpenShift/Kubernetes:

- mTLS between pods automatically.
- Traffic policies (retries, circuit breaking) at the network level.
- Observability (traces, metrics) without code changes.

The API Gateway handles North-South (external) traffic; the service mesh handles East-West (internal). They are complementary, not competing.

* 9. API Design Patterns

** 9.1 Pagination

Never return unbounded collections. Standard patterns:

- *Offset/limit*: ~GET /journeys?offset=0&limit=50~. Simple but inefficient at high offsets.
- *Cursor-based*: ~GET /journeys?cursor=eyJpZCI6MTAwfQ==~. Efficient for large datasets; the cursor encodes the last seen position.
- *Page-based*: ~GET /journeys?page=3&pageSize=50~. User-friendly but shares offset's inefficiency.

Response should include metadata:
#+BEGIN_SRC json
{
  "data": [...],
  "pagination": {
    "total": 1420,
    "limit": 50,
    "nextCursor": "eyJpZCI6MTUwfQ=="
  }
}
#+END_SRC

** 9.2 Filtering, Sorting, and Field Selection

- Filtering: ~GET /vehicles?status=active&driverType=HGV~
- Sorting: ~GET /journeys?sort=-startedAt~ (prefix ~-~ for descending)
- Field selection (sparse fieldsets): ~GET /vehicles?fields=id,registration,status~ - reduces payload size.

** 9.3 Idempotency

A request is idempotent if making it multiple times produces the same result as making it once. Crucial for retry logic.

| Method | Idempotent? | Safe (no side effects)? |
|--------+-------------+-------------------------|
| GET    | Yes         | Yes                     |
| PUT    | Yes         | No                      |
| DELETE | Yes         | No                      |
| POST   | No          | No                      |
| PATCH  | No*         | No                      |

*PATCH can be designed to be idempotent but isn't by definition.

For non-idempotent operations (POST), use an ~Idempotency-Key~ header. The server stores the result keyed to that value; duplicate requests return the cached result rather than processing again.

** 9.4 HATEOAS

Hypermedia as the Engine of Application State - responses include links to related actions:

#+BEGIN_SRC json
{
  "id": "v-123",
  "registration": "AB12 CDE",
  "_links": {
    "self": { "href": "/vehicles/v-123" },
    "journeys": { "href": "/vehicles/v-123/journeys" },
    "driver": { "href": "/drivers/d-456" }
  }
}
#+END_SRC

Rarely implemented fully in practice but worth understanding as the most complete expression of REST.

* 10. OpenAPI Specification Deep Dive

Since OAS is central to the APIOps pipeline, understanding its structure is practical knowledge.

#+BEGIN_SRC yaml
openapi: "3.1.0"
info:
  title: Vehicle Service API
  version: "2.0.0"
  description: Manages vehicle records for the Microlise platform.

servers:
  - url: https://api.microlise.com/v2
    description: Production

paths:
  /vehicles/{vehicleId}:
    get:
      summary: Get a vehicle by ID
      operationId: getVehicleById          # Unique identifier used in code gen
      tags: [Vehicles]
      parameters:
        - name: vehicleId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Vehicle found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Vehicle"
        "404":
          $ref: "#/components/responses/NotFound"

components:
  schemas:
    Vehicle:
      type: object
      required: [id, registration]
      properties:
        id:
          type: string
          format: uuid
        registration:
          type: string
          example: "AB12 CDE"
        status:
          type: string
          enum: [active, inactive, maintenance]

  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"

  securitySchemes:
    oauth2:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://auth.microlise.com/oauth2/token
          scopes:
            vehicles:read: Read vehicle data
            vehicles:write: Create and update vehicles

security:
  - oauth2: [vehicles:read]
#+END_SRC

Key OAS concepts:
- ~operationId~: Used by code generators and APIM to reference operations in policies.
- ~$ref~: DRY principle - define schemas and responses once, reuse everywhere.
- ~components~: The library section - schemas, parameters, responses, security schemes.
- ~tags~: Grouping for the developer portal - consumers see organised API docs.
- ~security~: Applied globally here; can be overridden per-operation.

* 11. Connecting the Dots: Microlise Architecture Map

Mapping the APIM notes to the architectural concepts above:

| APIM Note Item                             | Architectural Concept                                 |
|--------------------------------------------+-------------------------------------------------------|
| Separate layer between customers & APIs    | API Gateway Pattern (§3)                           |
| OpenAPI spec / swagger build               | Contract-First Design (§4.1), OAS (§10)         |
| APIOps pipeline, spec in git               | APIOps / GitOps for APIs (§6.2)                    |
| API Governance Council review              | API Governance (§6.1), Breaking Changes (§4.3)  |
| Linting scripts (~AgentScripts~)           | Automated governance enforcement                      |
| Gated + main pipeline                      | CI/CD gates for quality and security                  |
| Two-way sync, publish pipeline             | Declarative state management (APIOps)                 |
| Dev  ->  Cert  ->  Prod with approval gate | Environment promotion pattern (§6.2)               |
| Gen cluster vs Prod cluster                | Environment isolation, blast radius reduction         |
| Reverse proxy pipeline                     | Reverse Proxy vs API Gateway (§3.2)                |
| Quay registry, OpenShift containers        | Microservices deployment, East-West traffic (§8.1) |
| Swagger/OAS spec as PR artefact            | Spec-as-code, version-controlled contracts            |

* 12. Further Reading & Reference

** Recommended (from APIM notes)

- Quick Start Kubernetes - Nigel Poulton ([[https://microliseuk.sharepoint.com/sites/StorageCompute/ContainerPlatformUsers/SitePages/How-is-OpenShift-different-from-Kubernetes.aspx][OpenShift vs K8s]])

** Additional Architecture Resources

- [[https://spec.openapis.org/oas/v3.1.0][OpenAPI Specification 3.1.0 (official)]]
- [[https://www.asyncapi.com/docs][AsyncAPI Documentation]]
- [[https://grpc.io/docs/][gRPC Official Docs]]
- [[https://learn.microsoft.com/en-us/azure/api-management/][Azure API Management Docs]]
- [[https://microservices.io/patterns/apigateway.html][Microservices.io - API Gateway Pattern]]
- [[https://oauth.net/2/][OAuth 2.0 (oauth.net)]]
- [[https://swagger.io/specification/][Swagger / OAS Reference]]

** Key Terms Glossary

| Term           | Definition                                                                   |
|----------------+------------------------------------------------------------------------------|
| OAS / Swagger  | OpenAPI Specification - a standard format for describing REST APIs           |
| APIOps         | Applying GitOps principles to API management (spec-as-code, pipeline-driven) |
| APIM           | API Management - the platform/layer that governs API lifecycle               |
| Gateway        | Intermediary that enforces policy (auth, rate limiting, routing) for APIs    |
| Spec           | Short for specification - the OAS JSON/YAML document describing an API       |
| Idempotency    | Property where repeating a request has the same effect as making it once     |
| mTLS           | Mutual TLS - both parties in a connection authenticate with certificates     |
| BFF            | Backend for Frontend - an API tailored to a specific consumer's needs        |
| Service Mesh   | Infrastructure layer managing internal service-to-service communication      |
| Breaking Change| An API change that requires existing consumers to update their integration   |
