Jul 30, 2026·5 min read

A hotel is a distributed system (building a resort PMS, part 1/3)

#distributed-systems#builder#architecture#series

This is part 1 of a three-part series on building a resort property-management system solo, and what a hotel taught me about my day job. This part is about coordinating unreliable systems; part 2 is about time and identity; part 3 is about money, humans, and why the patterns transfer at all.

By day I work on real-time order systems — high-volume event pipelines, analytics, the plumbing that keeps a food-tech platform honest when orders, payments, and third-party partners are all flying at once. On the side, part-time and solo, I built something that on paper has nothing to do with that: a full property-management system and direct-booking engine for a boutique resort. I went in expecting to learn about hospitality. What actually happened is that the hotel taught me my own day job, from an angle I'd never seen it from.

A hotel, it turns out, is a distributed system with money on the line and a dozen unreliable partners. Swap 'delivery aggregator' for 'travel agency' and 'payment gateway' for 'door-lock encoder', and it's the same problem I solve every day — just wearing a bathrobe.

Once I saw that, the architecture stopped being a hospitality question and became a distributed-systems question I already knew how to ask. The domain didn't need new patterns. It needed the same rigor I use for order pipelines, applied honestly to a messier, more human reality. Here are the patterns the hotel forced on me, and why each one is the same lesson my day job teaches.

Sagas, not transactions — because check-in touches five systems

Checking a guest in isn't one action. It reserves a room, encodes a key card, opens a payment pre-authorization, flips the housekeeping and front-office state, maybe provisions the in-room TV and Wi-Fi. Those live in different systems, and no database transaction spans a physical lock encoder. So check-in, room-move, refund, and group-cancel can't be transactions — they're sagas, where every forward step carries a compensating action, and a half-finished flow lands in a duty-manager queue instead of silently corrupting state.

This is exactly the cross-service order flow from my day job: place order → reserve inventory → charge → notify the kitchen → dispatch. You can't wrap that in one transaction either, and the discipline is identical — model it as a saga with explicit compensations, not a hopeful sequence of calls.

Outbox and inbox — because every partner is unreliable

The PMS never calls the lock encoder or the payment gateway inline from a business operation. It writes an outbox row in the same transaction as the business change, and a worker ships it with retries and an idempotency key until the partner acknowledges. Incoming events — a travel agency's booking webhook, a gateway callback, a lock's response — hit an inbox first, de-duplicated on (source, event id), because they arrive duplicated, out of order, and at 3 AM.

If you've ever consumed aggregator webhooks or emitted order events onto a bus, this is muscle memory: outbox for at-least-once delivery outward, inbox de-dup for at-least-once delivery inward, idempotency keys everywhere because the network will replay you. The hotel didn't teach me a new pattern here; it reminded me these patterns are non-negotiable the instant real money crosses a system boundary you don't control.

That's the coordination layer: sagas to sequence work across systems that can't share a transaction, and outbox/inbox to move messages reliably between them. But coordinating the work is only half of it. The harder, stranger half is modeling truth — what time did this happen, and who does it belong to. That's part 2.

Built something similar, or want to argue with an approach here?

contact@singhpratap.dev