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?