Guides · Architecture

Architecture

The aggregator is a Go monolith plus a worker. Both speak to the same Postgres, Redis, and RabbitMQ.

Components at a glance

                    ┌────────────────────────────┐
                    │   Public REST API (chi)    │
                    │   cmd/api/main.go          │
                    └──────────────┬─────────────┘
                                   │
   ┌───────────────────────────────┼───────────────────────┐
   │                               │                       │
   ▼                               ▼                       ▼
┌──────────────┐         ┌──────────────────┐      ┌─────────────┐
│ handlers/    │         │ aggregator/      │      │ webhook/    │
│  search,     │         │  fan-out         │      │  dispatcher │
│  orders,     │         │  + breakers      │      └─────┬───────┘
│  servicing   │         └────────┬─────────┘            │
└──────┬───────┘                  │                       │
       │                 ┌────────┴───────────────┐       │
       │                 │ adapters/              │       │
       │                 │  ndcbase (shared base) │       │
       │                 │  britishairways  …     │       │
       │                 └─────────────┬──────────┘       │
       │                               │                  │
       │         ┌─────────────────────┼──────────────┐   │
       │         ▼                     ▼              ▼   │
       │  ┌────────────┐      ┌─────────────┐  ┌─────────┐│
       │  │ Airline 1  │      │ Airline 2   │  │Airline N││
       │  └────────────┘      └─────────────┘  └─────────┘│
       │                                                   │
       ▼                                                   ▼
┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌─────────────┐
│ cache/Redis  │  │ repo/Postgres│  │ queue/Rabbit │  │ Agency URL  │
└──────────────┘  └──────────────┘  └──────┬───────┘  └─────────────┘
                                            │
                                    ┌───────┴──────┐
                                    │   Worker     │
                                    │ cmd/worker   │
                                    └──────────────┘

Process model

Two processes, each from cmd/:

apicmd/api/main.go

Synchronous request handler. Owns the HTTP listener, aggregator fan-out, cache, and repos. Writes events to RabbitMQ but never reads from it.

workercmd/worker/main.go

Consumes RabbitMQ events and dispatches webhooks to registered subscribers. Aggregates L2B metrics into Postgres on a schedule. Listens for airline-pushed events and republishes as order.* events.

Run multiple replicas of each behind a load balancer for HA. State lives in Postgres, Redis, and RabbitMQ — the binaries are stateless.

Layered packages

PackageOwns
internal/handlersHTTP routing, request decoding, response encoding. No business logic.
internal/handlers/middlewareAuth (API key → SHA-256 → Postgres lookup → Redis cache), rate limiting, structured request logging.
internal/aggregatorFan-out across adapters, per-connector circuit breaker, global timeout. No I/O of its own.
internal/adaptersAbstract NDCConnector interface + concrete airline packages.
internal/adapters/ndcbaseShared OAuth2 / HTTP / XML envelope plumbing. Base for all airline adapters.
internal/adapters/britishairwaysBA-specific XML types and mapper. Embeds *ndcbase.Client.
internal/normalizationVersion detector → transformers → enrichers → quality scorer.
internal/refdataIn-memory airports / airlines / aircraft tables joined into offers by enrichers.
internal/cacheTyped wrappers around Redis: cache-aside for offers, sliding-window rate limiter, API-key cache.
internal/queueRabbitMQ publisher + consumer.

Aggregator fan-out

When a search request arrives, the aggregator sends it to every configured adapter concurrently with a per-adapter timeout (default 8 s). Results from adapters that respond in time are merged into a single ranked list. Adapters that timeout or return errors are silently skipped — partial results are always better than a failed request.

Each adapter is wrapped in a circuit breaker: after 5 consecutive failures, the breaker opens for 30 s before a single probe request is allowed through. This prevents one misbehaving airline from dragging down every search.

Normalization pipeline

Every raw airline response passes through a pipeline before being returned to the caller:

  1. Version detector — infers the NDC schema version from the XML namespace.
  2. Transformers — version-specific rules that map airline XML fields to the canonical Offer model.
  3. Enrichers — join refdata (airports, aircraft types, carrier names) into each offer.
  4. Quality scorer — computes a quality score per offer based on data completeness.