StayNest
StayNest is a production hospitality reservation REST API built on Java 21 and Spring Boot 3 — 90+ source files, 25+ tests, layered architecture, and a Decorator-pattern pricing calculator for surge, occupancy and holiday rules that keeps business logic composable and unit-testable.
Concurrency is guarded by pessimistic row locks on room inventory to eliminate double-booking anomalies, paired with a 5-minute scheduled jobthat expires unpaid reservations. Stripe checkout uses signature-verified webhooks and transactional refund handlers that persist state to PostgreSQLbefore invoking the gateway — preventing financial discrepancies on retries.
Three Spring AI integrations run on top: dynamic pricing adjustments, semantic property search, and a pgvector-backed review Q&A chatbot — each wrapped with a fallback handler. Sessions are hardened with HttpOnly JWT cookies, BCrypt hashing, role-based access control and token versioning for global sign-out on password reset.
Every diagram, animated live.
Database schema
CREATE TABLE hotels (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
city TEXT,
manager_id INT REFERENCES users(id)
);
CREATE TABLE rooms (
id SERIAL PRIMARY KEY,
hotel_id INT REFERENCES hotels(id) ON DELETE CASCADE,
type TEXT,
base_price DECIMAL(10,2)
);
CREATE TABLE inventory (
room_id INT REFERENCES rooms(id) ON DELETE CASCADE,
day DATE NOT NULL,
count INT NOT NULL,
PRIMARY KEY (room_id, day)
);
CREATE TABLE bookings (
id SERIAL PRIMARY KEY,
guest_id INT REFERENCES users(id),
hotel_id INT REFERENCES hotels(id),
room_id INT REFERENCES rooms(id),
start_date DATE,
end_date DATE,
status TEXT,
payment_status TEXT
);
CREATE TABLE payments (
id SERIAL PRIMARY KEY,
booking_id INT REFERENCES bookings(id),
stripe_id TEXT UNIQUE,
amount DECIMAL(10,2),
status TEXT
);Deep dive & lessons
- 01Pessimistic locks on the inventory table prevent double-booking under concurrent checkout without needing a distributed lock service.
- 02Stripe webhook handler is idempotent: stripe_id is unique on payments so retries fold safely, and refunds check current status before mutating.
- 03The pricing decorator chain keeps rules composable — new campaigns become one class instead of a spider of if-branches.
- 04A scheduler expires unpaid bookings after 15 minutes, releasing inventory and closing the checkout window cleanly.
- 05Semantic review search + a fallback answer path keeps the AI chatbot useful even when the LLM is unavailable.