Case study · Hotel management system for Villa Lanka
Ayura
A multi-tenant property management system for two hotels in Sri Lanka. It replaced an Excel-based workflow and runs the full booking lifecycle, from inquiry to invoice. I designed, built and operate it end-to-end.
- Sole developer: architecture, backend, frontend, operations
- Role
- First version 2023 Rebuild live since April 2026
- Timeline
- Java 21, Spring Boot 3.5, Angular 20, PostgreSQL, Docker, GitLab CI
- Stack
- In daily use at both hotels
- Status

The problem
Both hotels managed bookings, availability, agency commissions and invoices in Excel. Prices depend on the season, the room occupancy and the travel agency a guest books through, and every booking needs several documents. Keeping all of that consistent by hand was slow and error-prone.
From v1 to a rebuild
The first version went into use in 2023. To handle two hotels, seasonal prices, agency contracts and local tax rules properly, I rebuilt Ayura from scratch in 2025 and 2026, with multi-tenancy, a clear domain model, automated tests and a deployment pipeline built in from the start. The new version has been live since April 2026.
What I built
Booking lifecycle
Requested, confirmed, cancelled. Bookings are editable only while requested and never deleted, so the history stays intact. Rooms can be changed mid-stay with automatic repricing.
Pricing engine
Package prices per night by season and occupancy, plus supplements, room-type surcharges, discounts and deposits.
Travel-agency contracts
Net-rate and commission models per agency, applied automatically to the right booking lines.
Documents
Offers, confirmations, bills and accounting bills as PDFs, in German or English depending on the guest, with unique numbering per hotel and year.
Tax & accounting rules
Configurable per hotel, including Sri Lankan VAT, tourism levy and service charge.
Review sync
A nightly Google Places sync with an admin curation step feeds selected reviews to the hotel website.
Architecture
Engineering decisions
Tenant isolation in every query
Each request resolves the current hotel first and only uses hotel-scoped queries. Dedicated integration tests check that one hotel can never see another hotel's data.
Modular monolith with ports
Booking is a vertical slice. Dashboard, calendar and documents read it through narrow ports instead of sharing its repositories.
One pipeline for every change
Creating and editing a booking run through the same steps: validation, seasonal pricing, agency contracts, taxes, discounts, commission. All invariants live in one place.
Money and time done right
All amounts use BigDecimal with explicit rounding. Time comes from an injected clock, so date logic is tested deterministically.
Safe document numbering
Invoice numbers are generated under a database lock per hotel, document type and year, so parallel confirmations never collide.
Delivery by commit
Every release is pinned to a commit SHA on the server. Rolling back means switching the tag, no rebuild needed.
Results
- Excel retired
- Both hotels run their daily booking work in Ayura.
- Consistent numbers
- Prices, commissions, taxes and accounting splits are calculated the same way every time.
- Documents on confirm
- Confirming a booking generates the confirmation, bill and accounting bill automatically.
What's next
The staff client is being rebuilt in Angular 20 with a new design system and a stricter TypeScript setup. After that comes a full architecture review, and the question of whether Ayura can serve other Ayurveda hotels as a SaaS product.
Screenshots

Demo data 
Demo data 
Demo data · German confirmation 
Demo data · English confirmation