When faced with three incompatible data layers and no frontend, extracting the valuable backend logic and rebuilding with FastAPI and SQLite proved more efficient than incremental fixes.
The Task "Connect LINE Bot to our existing hotel management system." That was the brief. When I opened the codebase, I found something unexpected.
Code Audit: Three Data Layers, Zero Compatibility
Here's what was inside:
- api.py — in-memory dict for data management
- models.py — SQLite ORM demo
- demo.py — another separate in-memory dict
Three incompatible data layers in the same project. None of them talking to each other. No frontend either. Not a single screen that hotel staff could actually use.
But the backend logic had value. tasks.py (workflow), scheduler.py (shift management), inventory.py (33 types of supplies) — too good to throw away.
Decision: Extract the backend logic, redesign everything else with FastAPI + unified SQLite layer.
Redesign Principles
Unify the Data Layer
Eliminated all in-memory implementations. SQLite became the single source of truth. Dev, test, and production all go through the same code path.
Flask → FastAPI
Migrated the Flask endpoints to FastAPI for async support and automatic OpenAPI docs generation. Hotel projects keep growing — having an OpenAPI spec means the next developer (human or AI) can get up to speed quickly.
Guest / Staff Bot Separation
Separate LINE Bot channels for guests and staff. Independent webhook endpoints for each, with Rich Menus and Quick Replies controlling the UX flow.
LIFF Pages
LINE Front-end Framework (LIFF) lets you embed web apps inside LINE. I used it for form-heavy flows that Quick Reply can't handle. Two LIFF pages deployed:
| LIFF | Purpose |
|---|---|
| checkin | Check-in process |
| reservation | Reservation form |
Hosted at linebot.techsfree.com/hotel/liff/. Nginx + HTTPS, PM2 for process management.
The Bug That Bit Me: Field Name Mismatch
The LIFF form was sending guestName, but the FastAPI Pydantic model expected guest_name. Classic camelCase vs snake_case collision. Fixed by adding alias to the reservation endpoint. Simple once you know where to look, maddening before you do.
P0 Progress (≈40% complete)
Working:
- ✅ G01 Reservation (via LIFF)
- ✅ G07 WiFi password information
Still pending:
- ⚠️ G03 Check-in LIFF (API path fix needed)
- ⚠️ G08 Front desk call (Guest → Staff Push notification not implemented)
- ⚠️ G20 Check-out (Flex Message incomplete)
- ⚠️ Staff time clock and task list
What I Learned
"Preserve the existing code" is a trap. Patching three incompatible data layers incrementally would have taken longer and stayed fragile. Extracting the good parts and redesigning the rest was faster.
The final diff looks like a full rewrite — because it basically was — but decisions were cleaner and the result is maintainable.
LIFF + webhook is a great fit for hospitality, F&B, and retail. If you can assume all staff have LINE (a safe assumption in many Asian markets), you skip the native app entirely. Onboarding cost drops dramatically.
The fastest path to a working product isn't always the one that tries to save existing code.


Comments
Please log in or register to join the discussion