This article covers the technical decisions behind building this system. Each section explores a different layer of the architecture.
The Problem Space
Building a booking system sounds straightforward until you're dealing with real operational complexity. Paws Paradise isn't a simple appointment scheduler — it's a multi-service platform where lodging is booked by night, daycare by time slot, and grooming by appointment window. Each service type has distinct rules, and all three need to run simultaneously without conflict.
The system needed to handle:
GraphQL Schema Design
The foundation of the system is the GraphQL schema. Getting this right meant thinking in terms of business constraints first — not database tables.
The key insight: model the domain, not the UI. A booking isn't just a row in a database — it's a state machine with transitions, validations, and side effects.
The availableUnits query is the core of conflict detection — it accepts the service type and date range, then returns only units with no overlapping confirmed bookings.
Conflict Detection Logic
Double-booking is the worst failure mode in a lodging system. The detection logic needs to be airtight — checking not just exact matches but all possible overlap patterns.
The OR condition covers all overlap cases — a booking that starts before and ends during, or starts during the requested window. Missing either case would allow double-bookings to slip through.
The Pricing Engine
Pricing is computed from a rule graph — not hardcoded formulas. This means business owners can change rates without touching code.
The formula: base_rate × duration × size_multiplier + addons − discounts
Every transaction exposes a breakdown — line items per pet, per service, per add-on. This builds client trust and makes disputes easy to resolve.
Multi-Role Access
Three roles with meaningfully different access patterns require more than just route guards. The permission model needs to live at the data layer — not just the UI.
I implemented this as a context-aware middleware layer on the GraphQL resolvers — every query and mutation checks the caller's role before resolving. No frontend guard can substitute for this.
Key Takeaways
Schema design that reflects real business constraints is harder to build but far easier to extend and debug.
Any booking system without airtight overlap detection will eventually produce double-bookings. Test every edge case.
A transparent breakdown per line item builds client trust and reduces support overhead significantly.
UI-only access control is a liability. Enforce permissions in resolvers — treat every request as potentially untrusted.