Skip to content

Airline Microservices Platform — Architecture Blueprint (Multi‑Tenant, Multi‑Provider, Multi‑Currency)

This document consolidates everything agreed and written in the conversation into a single, deliverable blueprint:

  • True multi-tenant platform (3 airline brands) with multiple channels
  • Search → Select → Ancillaries → Book → My Booking (view + modify + post-book ancillaries)
  • Providers: start with ISO Gruppe integrator, later GDS (Amadeus) and NDC (Crazy Lama Easy Links)
  • Offers may include legs from different providers
  • One payment per purchase, single customer currency
  • Discounts/Promotions and Loyalty pricing
  • Change Orders for post-book paid changes
  • Book now / Ticket later with rule-based windows (30 min → hours)
  • Target scale: ~1000 searches/min (~16.7/sec) with provider fanout control and caching

1) Final service division (phase‑1 realistic, phase‑2 ready)

Platform / Tenant foundation

1. Tenant Config Service (owner of rules)

Owns (DB): - Tenants (airline brands) and channels - Allowed providers per tenant/channel/market - Markup/commission/rounding rules - Ticketing policies (issue now vs later windows) - PSP routing per tenant/currency/region - Feature flags (per tenant/channel)

Used by: Pricing, Search, Offer, Payment, Ticketing, BFF


2. Identity Service

Owns (DB): - Registered users, sessions, roles, auth claims

Provides: user identity for cross-channel continuity.


3. Guest Booking Access Service (PNR + last name)

Owns (DB): - Guest access grants (optional audit) - Token issuance policies

Purpose: - User enters PNR + last name - Service validates via Booking Service - Issues short-lived BookingAccessToken with scoped permissions: - read_booking - modify_booking (optional) - TTL short (15–60 minutes) and refresh requires re-validation

Do not treat “PNR+LastName” as authentication across services. Use the token.


Search & Shopping (pre‑booking)

4. Search Service (read side)

Owns (DB): - SearchSession records - Cached provider results (keyed by normalized search request + tenant/channel) - Supplier health/latency statistics (optional)

Responsibilities: - High-throughput search fanout to Provider Gateway - Caching + partial results support - Returns “shopping view” options (not guaranteed final price)

Scaling notes: - 1000 searches/min ≈ 16.7/sec; provider calls are expensive → caching + concurrency limits are mandatory.


5. Itinerary Composition Service (required because offers can span providers)

Owns (DB): - Composition rules (connect-time constraints, combinability rules) - Optional cached compositions for a SearchSession

Responsibilities: - Combine legs from multiple providers into a stable “virtual itinerary” - Produce stable itineraryId and normalized segments

This keeps Search from becoming a “monster” and centralizes multi-provider combinability.


6. Offer Service (sellable contract)

Owns (DB): - Offers (time-limited) - Provider references per leg - BookingPlan (the orchestration plan to fulfill the offer) - Revalidation requirements per provider leg - Offer locking / reservation intent state

Responsibilities: - Convert an itineraryId into a sellable Offer - Store expiry per leg/provider and overall expiry - Store the BookingPlan (multi-provider steps + compensation rules)


7. FX & Money Service (separate from Pricing)

Owns (DB): - FX rates (ingested) + provider/source metadata - Rate versions (fxRateVersionId) - Rounding rules per currency - “Rate lock” policy (how long a rate is honored)

Responsibilities: - Single source of truth for FX + rounding - Used by Pricing; referenced by Order for auditability


8. Pricing Service (multi-currency + channel + loyalty aware)

Owns (DB): - Optional pricing audit logs (recommended) - Optional caching of computed quotes keyed by (offerId, currency, customerContext, promo set)

Responsibilities: - Compute totals in single customer currency - Apply tenant/channel markups - Apply loyalty pricing adjustments - Apply promotions (via Promotions Service) - Return breakdown + priceSignature (hash) to detect changes


9. Promotions Service

Owns (DB): - Campaigns, promo codes, eligibility, stacking, budgets, usage limits, audit

Responsibilities: - Validate promo codes - Return discount components + reasons - Does not mutate money directly; Pricing consumes results


10. Ancillaries Service (pre‑book and post‑book)

Owns (DB): - Ancillary catalog (if you maintain your own) - Rules and validation logic - Optional caching of seat maps / availability

Responsibilities: - List available ancillaries for an Offer (pre-book) - List available ancillaries for a CompositeBooking (post-book) - Validate compatibility and create ancillary line items


11. Cart Service (persistent cross‑channel)

Owns (DB): - Carts, cart lines, snapshots - Latest priceSignature, expiry, promo refs

Responsibilities: - Persistent cart across devices/channels for anonymous + logged-in - Store: - Offer reference - Passengers - Ancillaries selections - Promotions references - Price snapshot + priceSignature - Detect price changes / offer expiry


Commercial & Payment (money and contracts)

12. Order Service (Initial Orders + Change Orders)

Owns (DB): - Orders (immutable commercial record) - Order line items - Totals breakdown and currency - Status transitions - References to booking and ticketing outcomes

Responsibilities: - Create Initial Order from cart snapshot - Create Change Orders for post-book paid changes - Emit OrderPaid after payment confirmed


13. Payment Service

Owns (DB): - Payment intents, transactions - Webhook inbox/outbox - Refunds, partial captures, retries - PSP routing metadata

Responsibilities: - PSP abstraction (per tenant/currency routing) - Payment lifecycle; webhooks - Refunds / chargebacks handling (future)


Travel domain (reservation + ticketing separated)

14. Booking Service (Composite Booking)

Owns (DB): - CompositeBooking records (unified view) - Mapping to ProviderBookings (PNRs) - Unified passenger list + itinerary summary - Change history/audit

Responsibilities: - Consume OrderPaid and execute Offer.bookingPlan steps - Create provider bookings (possibly multiple) through Provider Gateway - Maintain unified booking state for “My Booking” - Apply paid changes after Change Orders are paid

Status requirements (critical): - Composite booking statuses must include: - PENDING - PARTIALLY_CONFIRMED - CONFIRMED - FAILED - MANUAL_ACTION_REQUIRED

Because multi-provider booking isn’t atomic.


15. Ticketing Service (rule engine + queue)

Owns (DB): - Ticketing jobs (queue) - Attempt log + next schedule - Ticket issuance state per provider booking - Rules evaluation results

Responsibilities: - Decide issue now vs ticket later based on tenant/channel rules - Handle delay windows (30 minutes to hours) - Retries / backoff / manual queue - Ticket operations: issue, reissue, void (future)


Provider integration layer (shield the domain)

16. Provider Gateway (integration orchestrator)

Owns (DB): - Idempotency store - Correlation logs - Provider webhook inbox - Provider call audit (safe, minimal)

Responsibilities: - Normalized commands with resilience: - retries/backoff/circuit breaker - rate limiting - idempotency - correlation IDs and tracing - Webhook ingestion → normalized events


17. Provider Adapters (one per provider)

  • ISOGruppeAdapter (phase 1)
  • AmadeusAdapter (phase 2)
  • NdcEasyLinksAdapter (phase 2)

Responsibilities: - Provider-specific mapping, quirks, credentials, endpoints


Supporting services

18. Notifications Service

Consumes order/booking/ticketing events and sends: - receipts - booking confirmations - ticket issued notifications - change confirmations


19. BFFs (Backend‑for‑Frontend) per channel/tenant (API composition)

Not a core “data owner”, but critical for: - DTO shaping - orchestration across services - caching and UI-friendly endpoints

Can be a shared codebase configured per tenant/channel.


2) Key domain contracts (schemas)

A) Itinerary (output of Itinerary Composition)

{
  "itineraryId": "ITI_9f3c...",
  "legs": [
    {
      "legId": "LEG_1",
      "providerId": "ISO",
      "providerSearchRef": "ISO_SRCH_abc",
      "segments": [
        {
          "from": "TLV",
          "to": "ATH",
          "dep": "2026-02-01T08:00",
          "arr": "2026-02-01T10:10",
          "flightNo": "IZ123"
        }
      ]
    },
    {
      "legId": "LEG_2",
      "providerId": "AMADEUS",
      "providerSearchRef": "AMA_SRCH_xyz",
      "segments": [
        {
          "from": "ATH",
          "to": "MAD",
          "dep": "2026-02-01T12:00",
          "arr": "2026-02-01T14:40",
          "flightNo": "IB456"
        }
      ]
    }
  ],
  "passengers": { "adt": 1, "chd": 0, "inf": 0 }
}

B) Offer (multi-provider legs + BookingPlan)

{
  "offerId": "OFF_123",
  "tenantId": "AIRLINE_A",
  "channelId": "WEB",
  "customerCurrency": "EUR",
  "expiresAt": "2026-01-15T13:45:00Z",
  "legs": [
    {
      "legId": "LEG_1",
      "providerId": "ISO",
      "providerOfferRef": "ISO_OFFER_777",
      "legExpiresAt": "2026-01-15T13:40:00Z",
      "revalidateRequired": true
    },
    {
      "legId": "LEG_2",
      "providerId": "AMADEUS",
      "providerOfferRef": "AMA_OFFER_888",
      "legExpiresAt": "2026-01-15T13:42:00Z",
      "revalidateRequired": true
    }
  ],
  "bookingPlan": {
    "steps": [
      {
        "step": 1,
        "providerId": "ISO",
        "action": "CREATE_RESERVATION",
        "legs": ["LEG_1"],
        "timeoutSeconds": 20,
        "compensation": { "onFailure": "NONE" }
      },
      {
        "step": 2,
        "providerId": "AMADEUS",
        "action": "CREATE_RESERVATION",
        "legs": ["LEG_2"],
        "timeoutSeconds": 25,
        "compensation": { "onFailure": "CANCEL_STEP_1_IF_POSSIBLE" }
      }
    ],
    "finalize": [
      { "action": "LINK_COMPOSITE_BOOKING" },
      { "action": "SCHEDULE_TICKETING" }
    ]
  }
}

C) Price Quote (single customer currency + signature)

{
  "offerId": "OFF_123",
  "fxRateVersionId": "FXV_2026-01-15T13:00Z",
  "currency": "EUR",
  "components": [
    { "type": "BASE", "amount": 220.00 },
    { "type": "TAX", "amount": 54.30 },
    { "type": "CHANNEL_MARKUP", "amount": 9.99 },
    { "type": "LOYALTY_DISCOUNT", "amount": -12.00 },
    { "type": "PROMO", "code": "WINTER10", "amount": -10.00 }
  ],
  "total": 262.29,
  "priceSignature": "SIG_7baf..."
}

D) Composite Booking (unified “My Booking”)

{
  "compositeBookingId": "CBK_555",
  "tenantId": "AIRLINE_A",
  "status": "CONFIRMED",
  "providerBookings": [
    { "providerId": "ISO", "providerBookingId": "PNR1AB2", "status": "CONFIRMED" },
    { "providerId": "AMADEUS", "providerBookingId": "PNR9ZZ8", "status": "CONFIRMED" }
  ],
  "ticketingStatus": "PENDING",
  "passengers": [
    { "id": "P1", "lastName": "BOTO", "firstName": "MISYEA" }
  ],
  "itinerarySummary": { "...": "..." }
}

3) Event backbone (topics and responsibilities)

Each service should use an Outbox pattern when emitting events to ensure consistency between DB state and published events.

Shopping → Order/Payment topics

  • OfferCreated
  • CartUpdated
  • OrderCreated
  • PaymentIntentCreated
  • PaymentSucceeded
  • PaymentFailed

Fulfillment topics

  • OrderPaid (Order emits after PaymentSucceeded is verified/reconciled)
  • CompositeBookingCreationStarted
  • ProviderBookingCreated (per provider)
  • CompositeBookingCreated
  • TicketingScheduled
  • TicketIssued
  • TicketingFailed
  • BookingChangeRequested
  • BookingChangeApplied
  • BookingChangeFailed

Notifications consumes

  • OrderPaid
  • CompositeBookingCreated
  • TicketIssued
  • BookingChangeApplied
  • PaymentFailed (optional user messaging)

4) Minimal BFF endpoint set (phase 1)

  • POST /search
  • GET /search/{searchSessionId} (polling status + partial results)

Offers

  • POST /offers (from itineraryId)
  • GET /offers/{offerId}

Pricing

  • POST /pricing/quote

Cart

  • GET /cart
  • POST /cart/lines
  • DELETE /cart/lines/{lineId}

Order/Payment

  • POST /orders (from cart snapshot)
  • GET /orders/{orderId}

My Booking

  • POST /guest-access (pnr + last name → BookingAccessToken)
  • GET /bookings/{compositeBookingId} (or GET /bookings/by-pnr/{pnr})
  • POST /bookings/{id}/changes (initiates change order flow)

5) Reliability and scaling notes (the non‑negotiables)

Search fanout control

  • Concurrency limits per provider
  • Timeouts (return partial results)
  • Cache by normalized request key + tenant/channel
  • Track supplier health and degrade gracefully

Idempotency everywhere

  • Offer creation
  • Order creation
  • Booking plan steps (each provider call)
  • Payment webhooks

Async booking and ticketing

  • Booking and ticketing are long-running workflows:
  • return “in progress” states and allow polling
  • emit state transitions as events

Multi-provider booking is not atomic

  • Support PARTIALLY_CONFIRMED and MANUAL_ACTION_REQUIRED
  • Compensation rules per booking step:
  • cancel prior steps if possible
  • otherwise route to manual ops queue

Multi-currency auditability

  • Order stores:
  • currency (customer currency)
  • fxRateVersionId and/or explicit applied rate IDs
  • breakdown components in customer currency
  • provider settlement currency references (future)

Mermaid Diagrams (All Flows + Service Map)

Copy/paste these into Markdown files or Mermaid renderers.
Each diagram is syntactically valid Mermaid.


Diagram 1 — Service Landscape & Communication Directions

flowchart LR

  %% Clients
  subgraph clients["Clients"]
    WebA["Airline A Website"]
    WebB["Airline B Website"]
    WebC["Airline C Website"]
    Partners["Partner Channels"]
  end

  %% BFF
  subgraph bff["BFF"]
    BFFA["BFF Tenant Channel"]
  end

  %% Platform
  subgraph platform["Platform"]
    TenantCfg["Tenant Config"]
    Identity["Identity"]
    GuestAccess["Guest Booking Access"]
  end

  %% Shopping
  subgraph shopping["Shopping"]
    Search["Search Service"]
    Compose["Itinerary Composition"]
    Offer["Offer Service"]
    Pricing["Pricing Service"]
    FX["FX and Money"]
    Promo["Promotions"]
    Anc["Ancillaries"]
    Cart["Cart"]
  end

  %% Commercial
  subgraph commercial["Commercial"]
    Order["Order Service"]
    Payment["Payment Service"]
  end

  %% Travel
  subgraph travel["Travel Domain"]
    Booking["Booking Service Composite"]
    Ticket["Ticketing Service"]
  end

  %% Integration
  subgraph integration["Integration"]
    PGW["Provider Gateway"]
    ISO["ISO Adapter"]
    AMA["Amadeus Adapter"]
    NDC["NDC Easy Links Adapter"]
  end

  %% Support
  subgraph support["Support"]
    Notif["Notifications"]
  end

  %% Client entry
  WebA --> BFFA
  WebB --> BFFA
  WebC --> BFFA
  Partners --> BFFA

  %% Platform access
  BFFA --> TenantCfg
  BFFA --> Identity
  BFFA --> GuestAccess

  %% Shopping flow
  BFFA --> Search
  BFFA --> Offer
  BFFA --> Pricing
  BFFA --> Anc
  BFFA --> Cart
  BFFA --> Order

  %% Internal shopping links
  Compose --> Search
  Offer --> Compose
  Pricing --> FX
  Pricing --> Promo
  Anc --> PGW

  %% Commercial
  Order --> Payment

  %% Travel
  Booking --> PGW
  Ticket --> PGW

  %% Providers
  PGW --> ISO
  PGW --> AMA
  PGW --> NDC

  %% Notifications
  Payment --> Notif
  Order --> Notif
  Booking --> Notif
  Ticket --> Notif

  %% Config influence
  TenantCfg --> Search
  TenantCfg --> Pricing
  TenantCfg --> Payment
  TenantCfg --> Ticket

Diagram 2 — Flow 1: Search → Itinerary → Offer → Price → Cart

sequenceDiagram
  autonumber
  actor U as User
  participant BFF as BFF
  participant TC as TenantConfig
  participant S as SearchService
  participant PGW as ProviderGateway
  participant ISO as ISOAdapter
  participant COMP as ItineraryComposition
  participant OFF as OfferService
  participant PR as PricingService
  participant FX as FxMoney
  participant PROMO as Promotions
  participant CART as CartService

  U->>BFF: Search request dates pax route
  BFF->>TC: Get tenant and channel rules
  BFF->>S: Search flights tenantId channelId criteria
  S->>PGW: Search normalized request
  PGW->>ISO: Provider search phase one
  ISO-->>PGW: Results
  PGW-->>S: Normalized results with supplier status
  S->>COMP: Compose itineraries from results
  COMP-->>S: Itineraries itineraryId list
  S-->>BFF: searchSessionId and itineraries

  U->>BFF: Select itineraryId
  BFF->>OFF: Create offer itineraryId tenantId channelId currency
  OFF->>COMP: Get itinerary details by itineraryId
  COMP-->>OFF: Itinerary details
  OFF-->>BFF: Offer created offerId expiresAt

  BFF->>PR: Request pricing quote offerId customerContext currency
  PR->>FX: Get fxRateVersionId and rounding
  FX-->>PR: fxRateVersionId and rate data
  PR->>PROMO: Validate promotion eligibility
  PROMO-->>PR: Discount components
  PR-->>BFF: Price quote breakdown and priceSignature

  U->>BFF: Add to cart
  BFF->>CART: Add cart line offerId selections priceSignature
  CART-->>BFF: Cart updated

Diagram 3 — Flow 2: Checkout (One Payment) → BookingPlan (Multi‑Provider) → Ticketing Now/Later

sequenceDiagram
  autonumber
  actor U as User
  participant BFF as BFF
  participant CART as Cart Service
  participant ORD as Order Service
  participant PAY as Payment Service
  participant BK as Booking Service
  participant PGW as Provider Gateway
  participant ISO as ISO Adapter
  participant AMA as Amadeus Adapter
  participant TKT as Ticketing Service
  participant NOTIF as Notifications

  U->>BFF: Checkout
  BFF->>CART: GET /cart
  CART-->>BFF: Cart snapshot (lines + priceSignature)
  BFF->>ORD: POST /orders (cart snapshot, currency)
  ORD-->>BFF: OrderCreated (orderId, status=PendingPayment)
  ORD->>PAY: Create payment intent (orderId, amount, currency)
  PAY-->>BFF: paymentIntent info (redirect/3DS if needed)

  U->>PAY: Complete payment (PSP UI)
  PAY-->>PAY: PSP webhook received
  PAY-->>ORD: PaymentSucceeded (orderId, txnId)
  ORD-->>ORD: Mark order Paid
  ORD-->>BK: Event: OrderPaid (orderId, offerId, passenger data, etc.)

  BK-->>BK: Start BookingPlan execution
  BK->>PGW: Step 1 - CREATE_RESERVATION (ISO legs)
  PGW->>ISO: Create reservation
  ISO-->>PGW: ProviderBookingId/PNR
  PGW-->>BK: ProviderBookingCreated (ISO)

  BK->>PGW: Step 2 - CREATE_RESERVATION (AMA legs)
  PGW->>AMA: Create reservation
  AMA-->>PGW: ProviderBookingId/PNR
  PGW-->>BK: ProviderBookingCreated (AMA)

  BK-->>BK: Link CompositeBooking
  BK-->>ORD: CompositeBookingCreated (compositeBookingId, status)
  BK-->>NOTIF: Booking confirmation (optional "in progress" state)

  ORD-->>TKT: Event: TicketingScheduled (based on tenant rules)
  TKT-->>TKT: Evaluate ticketing rules (issue now vs later window)
  alt Issue Now
    TKT->>PGW: TicketIssue (ISO PNR)
    PGW->>ISO: Issue ticket
    ISO-->>PGW: Ticket numbers
    PGW-->>TKT: TicketIssued (ISO)
    TKT->>PGW: TicketIssue (AMA PNR)
    PGW->>AMA: Issue ticket
    AMA-->>PGW: Ticket numbers
    PGW-->>TKT: TicketIssued (AMA)
    TKT-->>ORD: TicketIssued (compositeBookingId)
    TKT-->>NOTIF: Tickets issued notification
  else Ticket Later
    TKT-->>TKT: Create job (notBefore + deadline)
    TKT-->>NOTIF: "Ticketing scheduled" (optional)
  end

Diagram 4 — Flow 3: My Booking (Registered or Guest) + Post‑Book Ancillaries (Change Order)

sequenceDiagram
  autonumber
  actor U as User
  participant BFF as BFF
  participant ID as Identity
  participant GA as Guest Booking Access
  participant BK as Booking Service
  participant ANC as Ancillaries Service
  participant PR as Pricing Service
  participant FX as FX & Money
  participant ORD as Order Service
  participant PAY as Payment Service
  participant PGW as Provider Gateway
  participant ISO as ISO Adapter
  participant TKT as Ticketing Service
  participant NOTIF as Notifications

  alt Registered user
    U->>BFF: Login
    BFF->>ID: Authenticate
    ID-->>BFF: Access token
  else Guest user (PNR + LastName)
    U->>BFF: Enter PNR + LastName
    BFF->>GA: POST /guest-access (pnr, lastName)
    GA->>BK: Validate booking (by PNR)
    BK-->>GA: Valid/invalid
    GA-->>BFF: BookingAccessToken (scoped, short-lived)
  end

  U->>BFF: View booking
  BFF->>BK: GET /bookings/{id} (token)
  BK-->>BFF: Composite booking details

  U->>BFF: Add post-book ancillaries
  BFF->>ANC: Get available ancillaries (compositeBookingId)
  ANC->>PGW: Provider ancillary availability (per provider booking)
  PGW->>ISO: Query ancillaries (if ISO involved)
  ISO-->>PGW: Availability
  PGW-->>ANC: Normalized availability
  ANC-->>BFF: Available ancillaries

  BFF->>PR: Price delta quote (change context)
  PR->>FX: Get fxRateVersionId
  FX-->>PR: fxRateVersionId
  PR-->>BFF: Delta PriceQuote (signature)

  U->>BFF: Confirm purchase of change
  BFF->>ORD: Create Change Order (delta lines + currency + signature)
  ORD->>PAY: Create payment intent (change order)
  PAY-->>BFF: paymentIntent info

  U->>PAY: Complete payment
  PAY-->>ORD: PaymentSucceeded (change order)
  ORD-->>BK: Event: ChangeOrderPaid (what to apply)
  BK->>PGW: Apply ancillaries to provider bookings
  PGW->>ISO: Add ancillary (if relevant)
  ISO-->>PGW: OK
  PGW-->>BK: Change applied per provider
  BK-->>ORD: BookingChangeApplied
  BK-->>NOTIF: Change confirmation

  opt Ticketing needed (EMD/reissue)
    BK-->>TKT: Event: TicketingScheduled (change context)
    TKT->>PGW: Issue EMD/Reissue
    PGW->>ISO: Ticket action
    ISO-->>PGW: OK
    PGW-->>TKT: TicketIssued
    TKT-->>NOTIF: Updated ticket notification
  end

Diagram 5 — Ticket‑Later Rule Engine (Decision + Queue)

flowchart TD
  A["TicketingScheduled event"] --> B{"Evaluate rules<br/>tenant, channel, provider<br/>payment, fare, risk"}
  B -->|Issue now| C["Create immediate issuance jobs"]
  B -->|Delay window| D["Create delayed job<br/>notBefore and deadline"]
  B -->|Manual| E["Route to manual ops queue"]

  C --> F["Attempt issuance"]
  D --> F
  F -->|Success| G["TicketIssued event"]
  F -->|Failure retryable| H["Retry with backoff<br/>until deadline"]
  H --> F
  F -->|Failure not retryable| I["TicketingFailed event"]
  I --> E

Starter Repo Blueprint (Monorepo Recommended)

This blueprint is designed for a true multi-tenant platform with many services, shared contracts, and consistent infra.

1) Repository structure

airline-platform/
  README.md
  docs/
    architecture/
      00-overview.md
      01-services.md
      02-events.md
      03-flows.md
      04-diagrams.md
    mermaid/
      flow-1-search-to-cart.mmd
      flow-2-checkout-book-ticket.mmd
      flow-3-mybooking-changeorder.mmd
      landscape-services.mmd
      ticketing-rules.mmd

  contracts/
    AirlinePlatform.Contracts/
      src/
        Events/
        Dtos/
        Common/
      AirlinePlatform.Contracts.csproj
      README.md

  shared/
    AirlinePlatform.SharedKernel/
      src/
        Money/
        Tenant/
        Time/
        Idempotency/
        Validation/
      AirlinePlatform.SharedKernel.csproj
    AirlinePlatform.Observability/
      src/
        Logging/
        Tracing/
        Metrics/
      AirlinePlatform.Observability.csproj
    AirlinePlatform.Http/
      src/
        Resilience/
        Auth/
      AirlinePlatform.Http.csproj

  services/
    bff/
      Airline.Bff/
        src/
        tests/
        Airline.Bff.csproj

    tenant-config/
      TenantConfig.Api/
        src/
        tests/
      TenantConfig.Domain/
      TenantConfig.Infrastructure/

    identity/
      Identity.Api/
      Identity.Domain/
      Identity.Infrastructure/

    guest-access/
      GuestAccess.Api/
      GuestAccess.Domain/
      GuestAccess.Infrastructure/

    provider-gateway/
      ProviderGateway.Api/
      ProviderGateway.Domain/
      ProviderGateway.Infrastructure/
      adapters/
        IsoGruppe.Adapter/
        Amadeus.Adapter/        # phase 2
        NdcEasyLinks.Adapter/   # phase 2

    search/
      Search.Api/
      Search.Domain/
      Search.Infrastructure/

    itinerary-composition/
      Composition.Api/
      Composition.Domain/
      Composition.Infrastructure/

    offer/
      Offer.Api/
      Offer.Domain/
      Offer.Infrastructure/

    fx-money/
      FxMoney.Api/
      FxMoney.Domain/
      FxMoney.Infrastructure/

    pricing/
      Pricing.Api/
      Pricing.Domain/
      Pricing.Infrastructure/

    promotions/
      Promotions.Api/
      Promotions.Domain/
      Promotions.Infrastructure/

    ancillaries/
      Ancillaries.Api/
      Ancillaries.Domain/
      Ancillaries.Infrastructure/

    cart/
      Cart.Api/
      Cart.Domain/
      Cart.Infrastructure/

    order/
      Order.Api/
      Order.Domain/
      Order.Infrastructure/

    payment/
      Payment.Api/
      Payment.Domain/
      Payment.Infrastructure/

    booking/
      Booking.Api/
      Booking.Domain/
      Booking.Infrastructure/

    ticketing/
      Ticketing.Api/
      Ticketing.Domain/
      Ticketing.Infrastructure/

    notifications/
      Notifications.Worker/
      Notifications.Infrastructure/

  infra/
    docker/
      docker-compose.local.yml
    k8s/
      base/
        namespaces.yaml
        configmaps.yaml
      services/
        search.yaml
        offer.yaml
        pricing.yaml
        cart.yaml
        order.yaml
        payment.yaml
        booking.yaml
        ticketing.yaml
        provider-gateway.yaml
        tenant-config.yaml
        identity.yaml
        guest-access.yaml
        ancillaries.yaml
        promotions.yaml
        fx-money.yaml
        notifications.yaml
        bff.yaml
      gateways/
        envoy-gateway/
          gateway.yaml
          httproutes.yaml
          tls.yaml
    observability/
      otel-collector.yaml
      grafana-dashboards/
      loki/
      tempo/
    migrations/
      postgresql/
        README.md

  build/
    Directory.Build.props
    Directory.Packages.props
    nuget.config

  .github/
    workflows/
      ci-contracts.yml
      ci-shared.yml
      ci-services.yml
      deploy-k8s.yml

  tools/
    scripts/
      dev-bootstrap.ps1
      dev-bootstrap.sh
      generate-contracts.ps1
      generate-contracts.sh

A) One solution file per service + one root “meta” solution

  • services/search/Search.sln
  • services/offer/Offer.sln
  • plus airline-platform.sln at root for developer convenience

B) Each service uses Clean Architecture-ish layering

  • *.Api (controllers, endpoints, auth, DTO mapping)
  • *.Domain (entities, value objects, state machines)
  • *.Infrastructure (EF Core, messaging, external calls)

C) Shared packages

  • contracts is versioned together in monorepo (or published as NuGet internally)
  • sharedkernel contains:
  • Tenant context (tenantId/channelId)
  • Money types (currency, amount, rounding policy)
  • Idempotency helper
  • Validation utilities

3) Shared Contracts (Events + DTOs)

Event design rules

  • Events are immutable facts (past tense)
  • Include correlation IDs
  • Include tenantId + channelId always
  • Prefer small payloads with stable IDs referencing internal storage

Example event envelope:

{
  "eventId": "evt_...",
  "eventType": "OrderPaid",
  "occurredAt": "2026-01-15T13:20:00Z",
  "correlationId": "corr_...",
  "tenantId": "AIRLINE_A",
  "channelId": "WEB",
  "data": {
    "orderId": "ORD_123",
    "offerId": "OFF_123",
    "currency": "EUR",
    "total": 262.29
  }
}


4) Data ownership (DB per service)

Recommended: database-per-service (PostgreSQL schemas per service are acceptable early if you must, but isolate strongly).

  • TenantConfig DB
  • Identity DB
  • GuestAccess DB (or reuse Identity DB with strict separation)
  • ProviderGateway DB (idempotency + webhook inbox)
  • Search DB (sessions/cache)
  • Composition DB (optional)
  • Offer DB
  • FX DB
  • Pricing DB (optional)
  • Promotions DB
  • Ancillaries DB
  • Cart DB
  • Order DB
  • Payment DB
  • Booking DB
  • Ticketing DB
  • Notifications DB (optional)

5) Local development blueprint (docker compose)

Include local dependencies: - PostgreSQL (multiple DBs or schemas) - Redis (cache/session) - RabbitMQ or Kafka (event bus) - OTel Collector + Grafana/Loki/Tempo (optional)


6) Messaging and workflow blueprint

A) Event bus

  • RabbitMQ (topic exchanges) or Kafka (topics)
  • Standardize:
  • exchange/topic naming
  • retry + DLQ strategy
  • schema versioning rules

B) Sagas / workflows

  • Booking is a saga driven by OrderPaid
  • Ticketing is a saga driven by TicketingScheduled

C) Outbox pattern

  • Each service that emits events writes them to an outbox table transactionally
  • A publisher worker reliably publishes and marks sent

7) Steps checklist (end‑to‑end, no missing steps)

  1. Search:
  2. BFF → Search (tenant/channel context)
  3. Search → ProviderGateway → ISO adapter (phase 1)
  4. Search stores SearchSession and returns partial results if needed

  5. Composition:

  6. Composition creates itineraryId objects, possibly combining providers

  7. Offer:

  8. Offer Service creates Offer for itineraryId
  9. Offer stores multi-provider legs + BookingPlan + expiries

  10. Quote:

  11. Pricing calls FX for fxRateVersionId
  12. Pricing calls Promotions + Loyalty source (Identity/Profile)
  13. Pricing returns breakdown + signature

  14. Cart:

  15. Cart stores offer + selections + signature snapshot
  16. Cart detects expiry/price changes

  17. Checkout:

  18. Order created from cart snapshot
  19. Payment intent created (single payment)

  20. Payment:

  21. PSP completes
  22. Payment webhooks processed and deduped
  23. Order marks paid and emits OrderPaid

  24. Booking saga:

  25. Booking consumes OrderPaid
  26. Executes Offer.bookingPlan steps through ProviderGateway
  27. Handles compensation / partial bookings / manual action
  28. Emits CompositeBookingCreated

  29. Ticketing saga:

  30. Ticketing evaluates rules and either:
    • issues now
    • schedules job window (30 min to hours)
    • routes to manual queue
  31. Emits ticket events

  32. My Booking:

  33. Registered user uses Identity token
  34. Guest user uses BookingAccessToken from GuestAccess

  35. Post-book ancillaries:

  36. Ancillaries returns available post-book add-ons
  37. Pricing returns delta in same customer currency
  38. Change Order created
  39. Payment taken
  40. Booking applies changes, ticketing reissues if required
  41. Notifications sent

Appendix: Notes on future provider additions (Amadeus/NDC)

  • Keep provider-specific semantics inside adapters.
  • ProviderGateway normalized API must remain stable as new providers arrive.
  • Offer and BookingPlan should keep per-leg provider IDs and references.