Time and identity are not what you think (building a resort PMS, part 2/3)
This is part 2 of a three-part series on building a resort PMS solo. Part 1 covered coordinating unreliable systems with sagas and outbox/inbox. This part is about the two things the hotel modeled in ways that quietly rewired how I think about every system: time, and identity.
Business date is not wall-clock time — the idea that rewired me
This is the most domain-specific concept I hit, and the one that most changed how I think about time in any system. A hotel 'day' isn't midnight to midnight. It's the span between last night's end-of-day audit and the next one, and it can close at 3 AM. So a minibar charge posted at 2:30 AM belongs to yesterday's business date until the audit runs. Revenue reports key on business date; the audit trail keys on wall-clock time; the two are deliberately different fields on every posting.
'When did this happen' and 'which day does it count for' are two different questions. Most night-audit bugs — and plenty of analytics bugs in my day job — come from systems that assume they're the same question.
I now see business-date-versus-event-time everywhere in my day-job pipelines: the difference between when an event arrived and which reporting period it belongs to is the same distinction, and getting it wrong is how you end up with numbers that don't reconcile at month boundaries.
Anchor to the stable entity — folios, not room numbers
A guest who moves from room 312 to 415 at 8:45 PM should still get the bar tab posted at 9 PM. So charges, locks, and messages attach to the folio and reservation — the stable identity — never to the room number, which is incidental and can change under you. I've written this exact bug in data work: keying on a short, human-friendly identifier that turns out not to be stable or unique, and watching records get stitched to the wrong owner. Anchor to the thing that doesn't move. The room number is a label; the folio is the identity.
Immutable snapshots — don't reconstruct history from live state
The nightly rate, the tax, the cancellation policy: all captured as an immutable snapshot at the moment of booking. The live rate plan can change tomorrow; the guest's reservation must not silently change with it. If I ever need to know what a guest agreed to, I read the snapshot, not the current global config.
This is the same lesson a nasty production bug once beat into me on the day job: never reconstruct a historical fact by reading mutable current state, because that state has moved on. A reservation's price is a point-in-time fact. So is an order's line-item detail. Snapshot the fact when it happens; don't recompute it later from a world that has changed.
So: anchor money to the stable identity, and freeze facts when they happen. Coordination (part 1) and truth-over-time (this part) are the machinery. Part 3 is about the two things that machinery ultimately serves — money and the humans touching it — and the bigger realization about why any of these patterns transferred from food-tech to hospitality at all.
Built something similar, or want to argue with an approach here?
contact@singhpratap.dev