Maliva
Maliva is a tourism guide app for Malang Raya, designed to help tourists and locals alike make the most out of their trip. Through our application, you can explore the best local attractions, learn about the culture, and plan your trip accordingly.
Overview
What was built
Cloud backend for Maliva, a mobile tourism guide app for Malang Raya, Indonesia. The cloud track built and deployed the REST API that served both the Android client and a separate ML recommendation service.
Why it was built
Capstone project for Bangkit Academy 2024 (Google, GoTo, Tokopedia, Traveloka). The constraint was real: three tracks (mobile, ML, cloud) working in parallel toward a fixed demo deadline, each team deploying independently.
Architecture & Design
Microservice-style cloud architecture on GCP. The Android app communicates with a central Express.js API on Cloud Run. The API uses a custom-built authentication and authorization system with JWT for token verification, Firestore for content data, Firebase Storage for file storage, and makes HTTP calls to a separate ML service for personalised recommendations. CI/CD is fully automated via GitHub Actions.
User-facing mobile client for Malang Raya tourism
Core REST backend: attractions, users, trip data
Recommendation model wrapped as a Cloud Run endpoint
Custom-built authentication and authorization with JWT
File and media storage
NoSQL content store: attractions, user data
Serverless container runtime for API and ML service
CI/CD pipeline: build Docker image, push to registry, deploy
Challenges & Solutions
Problem
Three teams (Android, ML, Cloud) needed to integrate at a fixed deadline with no full-time coordination. The cloud track had to build a backend that both the mobile app and the ML model could consume, while keeping deployments independent enough that one track's changes couldn't break another's.
Constraints
- Cross-functional team: mobile, ML, and cloud tracks developed in parallel
- Non-negotiable Bangkit programme deadline
- Must deploy on Google Cloud Platform, budget constrained to free/low-cost tiers
- No dedicated DevOps; each track owned its own deployment pipeline
Approach
Defined an OpenAPI-style contract document at project start and shared it across all tracks. The cloud API was built with Express.js and deployed to Cloud Run via Docker. The ML model was wrapped as an independent Cloud Run endpoint called over HTTP, keeping it loosely coupled by design. GitHub Actions automated the full build-push-deploy pipeline on every push to main, removing manual deployment entirely.
Three tracks developing in parallel needed an API contract before any code was written, otherwise each track would build to different assumptions.
Defined and shared an OpenAPI-style contract document at project kickoff. Each track implemented to the contract; integration sessions caught drift early.
Minimal integration blocking. Issues surfaced at weekly syncs rather than the day before the deadline.
Manual deployments were error-prone and took hours, which was unsustainable with frequent cross-track integration updates.
Built a GitHub Actions pipeline: push to main → build Docker image → push to Artifact Registry → deploy to Cloud Run. Zero manual steps.
Deployment time dropped from hours of manual work to under 15 minutes, fully repeatable.
Integrating the ML recommendation model built by a separate team without creating a hard dependency between services.
The ML team deployed their model as an independent Cloud Run endpoint. The API called it via HTTP with a JSON payload, treating the ML service as an external dependency rather than a shared library.
Either service could be updated or redeployed independently. Failure in the ML service degraded recommendations gracefully rather than breaking the main API.
Cloud Run over App Engine or GKE
Cloud Run scales to zero (no idle cost), accepts any Docker image, and deploys in seconds. The right balance between simplicity and capability under a student budget.
Cold starts on low-traffic endpoints. Mitigated with a minimum instance count, but that re-introduces idle cost.
ML model as a separate HTTP service, not embedded in the API
Keeping the ML model as an independent service meant the cloud and ML tracks could deploy independently. No shared codebase, no shared deployment, no coordination overhead.
Added network latency on each recommendation call. An embedded model would be faster but would couple the two teams' release cycles.
Results & Learnings
Full system delivered within the Bangkit Academy programme deadline
Deployment time reduced from hours of manual steps to under 15 minutes via automated CI/CD
Mobile, ML, and cloud tracks integrated successfully at the final demo
Key Lessons
- An API contract defined at kickoff is the single highest-leverage action in a parallel cross-functional team
- CI/CD automation pays back its setup cost within the first week by reducing deployment anxiety near deadlines
- Treating the ML model as an independent HTTP service made integration clean and failure-isolated
Future Improvements
- Add a CDN caching layer for frequently accessed attraction data
- Improve ML model accuracy with a larger, more representative local tourism dataset
- Add offline support to the Android app for areas with poor connectivity
What I'd Do Differently
- Set up CI/CD on day one, not week three; the time savings compound immediately
- Add automated integration tests covering the mobile → API → ML service flow end-to-end