Skip to main content

Data Model

Nexa's data model is case-centric: a small number of aggregates hang off the case, and every aggregate is timestamped, URN-addressable, and audit-backed.

URNs

Every top-level entity has a URN of the form:

urn:nexa:<entity>:<shortid>

URNs are stable, opaque, and safe to log. They never contain PII. All inter-service references use URNs rather than internal database identifiers.

Core aggregates

The diagram above uses UML composition (*--) — every aggregate is owned by the case and shares its lifecycle. References between siblings (e.g. a reservation's groupId, an allocation's hotelUrn) are URN pointers, not embedded objects.

Case

The root aggregate.

FieldDescription
urnCase URN
externalEventIdAirline-supplied event ID used for idempotency
statusState from the case lifecycle
flightOriginal disrupted flight (IATA, schedule, delay reason)
nextFlightReaccommodation details (used to calculate stay plan)
passengerGroups[]Grouped passengers — see below
policyUrnPolicy active at allocation time
airportAnchor airport for the case
createdAt, updatedAtStandard timestamps

Passenger Group

Passengers are always handled at the group (PNR) level. Groups never split across hotels unless explicitly overridden.

FieldDescription
groupIdStable within the case
pnrBooking reference
tierECONOMY · PREMIUM_ECONOMY · BUSINESS · FIRST · CREW
stayPlan{ checkIn, checkOut, nights, buffers }
passengers[]referenceId, type (ADT/CHD/INF), specialRequirements[]
operationalStatusUNASSIGNED · ASSIGNED · CHECKED_IN · CHECKED_OUT · LOCAL_RESIDENT

Policy

Versioned and activated explicitly.

FieldDescription
urnPolicy URN
versionMonotonic; a new activation freezes the previous version
context{ airport, airline, country } — used for matching
tiersWhich tiers this policy applies to
constraintsminStars, maxDistanceKm, maxNightlyRate, amenities, excluded chains
bufferslateCheckoutMinutes, earlyCheckInMinutes
modeBATCH (schedule waves) or ON_DEMAND (run immediately)
autoApproveDemandBoolean + maxAutomaticRoomsPerWave cap
incrementalRequiresApprovalIf true, incremental demand is never auto-approved

Hotel (normalized offer)

Providers return wildly different shapes; Nexa normalizes them into one:

FieldDescription
hotelUrnInternal URN (stable across providers)
providerOfferIdProvider-specific reference (required for booking)
providerAMADEUS · HOTELBEDS · CONTRACT
name, address, location (lat/lon), starsBasic attributes
distanceKmFrom the anchor airport
nightlyRate, totalPrice, currencyPricing
amenities[]Normalized amenity tags
acceptedPaymentsPayment methods the hotel will honor
cachedAt, ttlInventory freshness
savingsVsMarketIf this is a direct contract, vs cheapest GDS

Reservation

FieldDescription
urnReservation URN
caseUrn, waveUrn, groupIdTraceability
providerReservationIdProvider booking ID
confirmationCodeShown to the passenger
statusREQUESTED · CONFIRMED · CANCELLED · FAILED
checkIn, checkOut, rooms[]Booked stay
voucherRendered HTML and text payloads
paymentInstructionsCorporate guarantee or card reference

Audit

Append-only. One document per action:

FieldDescription
entitycase, policy, demand, reservation, etc.
entityUrnTarget URN
actorUser URN or system actor
actione.g. approve, rebook, override
reasonFree-text reason (required on manual actions)
before, afterSnapshot of changed fields
atImmutable timestamp
Was this helpful?