How AirBoundsSearchHandler fans out to Amadeus with different Commercial Fare Families (CFFs),
merges results, and handles the anchor search for inbound legs.
Routing
Outbound fan-out
Inbound + anchor
Merge & response
flowchart TD
classDef route fill:#e3f2fd,stroke:#90caf9,color:#1565c0
classDef outbound fill:#e8f5e9,stroke:#a5d6a7,color:#2e7d32
classDef inbound fill:#f3e5f5,stroke:#ce93d8,color:#7b1fa2
classDef merge fill:#fff3e0,stroke:#ffcc80,color:#e65100
classDef amadeus fill:#fce4e8,stroke:#f48fb1,color:#e0002a
classDef nz fill:#e0f2f1,stroke:#80cbc4,color:#00695c
SN([Spotnana]):::route -->|"POST /offers/search/air-bounds"| CTRL[CommerceOffersController
resolve officeId + itinerary type]:::route
CTRL --> HANDLER{"AirBoundsSearchHandler
selectedBoundId == null?"}:::route
%% ── OUTBOUND ──
HANDLER -->|"null → outbound"| POS{"Office ID /
itinerary type?"}:::outbound
POS -->|"SYD domestic"| DOM["2 parallel calls"]:::outbound
POS -->|"SYD international"| INTL["3 parallel calls"]:::outbound
POS -->|"Ex-Row POS"| EXROW["3 parallel calls
(country CFFs from enum)"]:::outbound
POS -->|"NZ domestic"| NZDOM["1 call
NZDOM CFF"]:::nz
DOM -->|"req₁"| D1["Commercial
AUDOML"]:::outbound
DOM -->|"req₂"| D2["Classic / Rewards
ACEMECO, ACEMPRM, ACEMBUS"]:::outbound
D1 & D2 --> AMA1
INTL -->|"req₁"| I1["Eco / Pre-eco
AUINTECO"]:::outbound
INTL -->|"req₂"| I2["Business
AUINTBUS"]:::outbound
INTL -->|"req₃"| I3["First
AUINTFIR"]:::outbound
I1 & I2 & I3 --> AMA1
EXROW --> AMA1
NZDOM --> AMA1
AMA1["✈️ Amadeus DAPI
airBoundsShopping
(CompletableFuture.allOf)"]:::amadeus --> MERGE
%% ── INBOUND ──
HANDLER -->|"present → inbound"| ICFF["Apply inbound CFFs
based on user's outbound selection"]:::inbound
ICFF --> ANCHOR{"prepareAnchorSearch()
flip isRequestedBound
inverse CFFs
findMatchingAirBoundId()"}:::inbound
ANCHOR -->|"match found"| PAR["2 parallel calls"]:::inbound
ANCHOR -->|"no match"| SOLO["1 call
(inbound only)"]:::inbound
PAR -->|"anchor req"| ANC["Anchor search
(inverse CFF of selected)"]:::inbound
PAR -->|"inbound req"| IBR["Inbound search
(same CFF as selected)"]:::inbound
ANC & IBR --> AMA2
SOLO --> AMA2
AMA2["✈️ Amadeus DAPI
airBoundsShopping"]:::amadeus --> MERGE
%% ── MERGE ──
MERGE["Merge + Dedup
• group by flight-signature
• dedup by cabin + class
• filter bad CFF combos
• combine dictionaries (ConcurrentHashMap)"]:::merge
MERGE --> SORT["Sort
ranking > 0 → Amadeus rank
else → Domestic / Intl comparator"]:::merge
SORT --> RESP(["AirBoundsListReply
airBoundGroups[] + dictionaries"]):::merge
RESP --> SN2([Spotnana]):::route