← Index Case Study · N° 03 Hotel SaaS

Guesperia ORMS

A hotel guest-request platform: guests scan an in-room QR to ask for anything, and staff work every request against a server-authoritative SLA clock.
ClientSincap Agency
RoleBackend & platform
StatusIn development
StackJava 21 · Spring Boot · Redis

01 — The brief

Guesperia ORMS — an Order & Request Management System for hotels — replaces the phone calls and paper chits guests use to ask for towels, a technician, or anything else. A guest scans a QR in their room, submits a request, and it lands with the right department. Staff work a live queue; managers see whether the hotel is hitting its service targets.

Two things made it more than a to-do list. Requests are governed by SLAs — every one has a deadline the system, not a person, computes and enforces. And it is multi-tenant from the first table: several hotels share one deployment, and no request, guest or metric may ever leak across a tenant boundary.

02 — Approach

The backend is a Spring Boot service in a hexagonal, ports-and-adapters shape: a framework-free domain at the centre, use cases and ports around it, and adapters — persistence, Redis messaging, SSE, SLA, Clerk, email, QR — at the edge. Multi-tenancy is enforced at three layers at once: a request-scoped tenant context, a Hibernate filter on every query, and a database CHECK that a tenant id is never null.

The SLA clock is the heart of it. Deadlines are computed on the server, and a scheduled ticker — guarded by ShedLock so only one instance runs it across the cluster — sweeps open requests every fifteen to thirty seconds, crossing five-, two- and zero-minute thresholds and raising escalations. Real-time updates ride Redis pub/sub out to two separate SSE channels: a guest stream scoped to their own submission, and a department-scoped staff stream — and the DTO boundary guarantees a guest is never sent SLA data meant for staff.

  • Core Java 21 · Spring Boot · hexagonal ports & adapters
  • Tenancy TenantContext + Hibernate @Filter + NOT NULL tenant CHECK
  • SLA engine Server deadlines · ShedLock ticker (15–30s) · threshold escalations
  • Real-time Redis pub/sub → separate guest & staff SSE streams
  • Auth Clerk org auth · JWT resource server · app-layer PermissionService
  • Frontend Next.js 16 · React 19 · next-intl (tr/en) · Turborepo

None of this was free. Each choice bought something and charged for it somewhere else — the work was in choosing where to pay.

  1. Trade-off 01

    Hexagonal core over a framework-coupled service

    Keeping the domain free of Spring means the request and SLA rules can be tested and reasoned about without a database or a web server — the core compiles with zero framework on the classpath. The cost is more moving parts: ports, adapters and mappers that a straight layered service would not need.

  2. Trade-off 02

    Server-authoritative SLA over client timers

    Deadlines and threshold crossings are computed on the server and swept by one lock-guarded ticker, so every device shows the same clock and nothing depends on a phone being awake. The cost is a scheduled job that must be tenant-scoped, idempotent and safe to run on exactly one node — which is what ShedLock buys.

  3. Trade-off 03

    Two SSE channels over one shared stream

    Guests and staff get separate real-time streams, and the DTO boundary means SLA and internal data can never reach a guest by accident. The cost is maintaining two channels and two projections of the same event instead of broadcasting one.

  4. Trade-off 04

    Multi-tenant from day one over retrofitting later

    Enforcing tenant isolation in the context, the ORM filter and a database constraint at once makes a cross-tenant leak a hard error, not a code-review hope. The cost is that every query, migration and test has to carry the tenant dimension from the very start.

The clock belongs to the server — every screen only reads it.

03 — Results

Guesperia’s backend is feature-complete for its MVP — the guest and staff flows, the SLA engine, escalations, reports and Clerk-based access all pass a suite of ninety-plus tests across sixteen migrations. It runs multi-tenant for a two-hotel pilot, with the Next.js guest and staff apps coming up behind it.

92
Backend tests passing across 16 migrations
3
Layers of tenant isolation: context · ORM filter · DB check
2
Hotels sharing one multi-tenant deployment

04 — What’s next

The backend is ahead of the clients, so the near-term work is finishing the guest and staff web apps, wiring the Clerk webhook and a real email provider in production, and hardening the platform for more than the pilot’s two hotels.