This platform was commissioned by a Singapore corporate client. Development is currently on temporary hold while the client resolves Singapore regulatory and legal licensing requirements. System architecture, product UX, and live Flutter interfaces are showcased here with full client permission.
The High-Trust Hurdle in Domestic Services
In ride-hailing (e.g. Grab or Uber), passengers treat the driver as a fungible commodity: you request a ride, any nearby 4-star driver arrives, and you complete your journey. Home cleaning operates on a completely different psychological plane: a homeowner is granting an unfamiliar person access to their private sanctuary, family, and possessions.
In Singapore, homeowners are routinely trapped between two substandard models: exploitative domestic employment agencies requiring rigid multi-month contracts and high upfront agency fees, or unregulated WhatsApp message boards where availability is chaotic, cleaners cancel without notice, and quality is unpredictable.
I engineered Helpful (initially code-named InstantMaid) as a 3-sided marketplace that merges the velocity of on-demand ride dispatch with the rigor of verified domestic trust:
- Transparent Tiered Pricing: Standardized, upfront hourly rates ($15/hr for standard maintenance cleaning, $20/hr for deep cleaning) without hidden agency commission markups.
- Instant Postal Code Matching: Customers enter their 6-digit Singapore postal code to view live provider density, nearby helpers, and realistic arrival windows.
- Two Distinct Operational Apps: Rather than forcing cleaners and homeowners into an awkward single app with role toggles, each audience has a bespoke native Flutter client designed around their specific daily workflow.
De-Risking the Living Room: Verified Helper Credentials
Marketplace conversion hinges entirely on defusing customer anxiety before the first booking. If a customer cannot immediately verify a helper's background and experience, they abandon the funnel.
I architected a multidimensional provider trust system:
- Identity & Background Verification: Cleaners must complete identity screening (NRIC/Work Permit verification) and background checks before they can go online. This earns a prominent verified badge on their public profile.
- Operational Track Record: Profiles display verifiable platform metrics: completed job count (e.g. 312 Jobs done), years of experience, aggregate customer star rating, and real-time response rate (e.g. 98% response).
- Language & Specialty Badging: In Singapore's multilingual society, communication barriers cause job friction. Provider cards explicitly display verified spoken languages (English, Tamil, Bahasa Malaysia, Mandarin) and specialized care competencies (medical assistance, companionship, deep sanitization).
The Flutter Monorepo with Shared Freezed Packages
The platform requires three distinct user interfaces:
- Customer App (
apps/user_app): Flutter iOS/Android client styled in clean Material 3 Indigo, optimized for browsing, scheduling, and live tracking. - Cleaner App (
apps/provider_app): Flutter iOS/Android client styled in high-contrast Dark/Emerald Green, optimized for outdoor daylight legibility, one-tap online/offline toggling, and urgent dispatch acceptance. - Operations Portal (
helpful-admin): Next.js web application for booking monitoring, provider onboarding, and geofenced dispatch oversight.
To eliminate model duplication across these apps, I structured the repository as a shared Dart workspace:
- Shared Immutable Models (
packages/maid_models): Built with Freezed andjson_serializable. Models forBooking,Profile,Review, andProviderJobCardare strictly immutable, generating type-safecopyWithmethods and serialization logic. A single update immediately cascades across both mobile clients. - Shared API Layer (
packages/maid_api): Pure Dart package encapsulatingSupabaseServiceand Riverpod providers. Manages asynchronous data pipelines withAsyncValueand handles background connectivity recovery. - Database-Level Row Level Security (RLS): Because three apps and serverless edge functions share a single PostgreSQL database, access control cannot rely on client-side rules. Strict RLS policies at the PostgreSQL level ensure customers can only read their own bookings, cleaners can only access dispatched jobs, and sensitive contact information remains masked until booking confirmation.
PostGIS 15km Radial Matching & Singapore Geocoding
Effective dispatch requires spatial precision. Matching a homeowner in Jurong West with a cleaner in Changi wastes transit time and triggers cancellations.
I engineered a localized spatial dispatch pipeline tailored to Singapore:
- OneMap Singapore Postal Resolution: Built direct integration with the Singapore Land Authority's official OneMap API. Entering a 6-digit postal code (e.g.
238823for Orchard Road) immediately resolves building coordinates, block numbers, and street names without requiring messy freeform address typing. - PostGIS Radial Matching: Provider locations are stored as PostgreSQL
geography(Point, 4326)coordinates. The custom RPCfind_nearest_cleaners()uses spatial index queries (ST_DWithin) to calculate exact linear distance, identifying all vetted, online providers within a 15-kilometer radius. - OpenRouteService Real-Time Driving ETAs: While PostGIS handles radial filtering, actual driving times on Singapore expressways (PIE, AYE, CTE) dictate real arrival estimates. OpenRouteService computes live driving durations so homeowners see realistic arrival windows (e.g. "Arrives in ~15 min").
Atomic Serverless Dispatch & Slide-to-Confirm Ergonomics
The booking lifecycle is governed by an explicit 9-state finite state machine:
draft → pending_dispatch → searching → confirmed → in_progress → completed [or: cancelled / rejected]
In two-sided dispatch platforms, client-side race conditions are lethal: if two cleaners tap "Accept" simultaneously, both arrive at the customer's doorstep.
- Server-Side Dispatch Orchestration: When payment authorization succeeds, the client does not query cleaners. Instead, a Supabase Deno Edge Function (
dispatch-booking) triggers. It queriesfind_nearest_cleaners(), inserts atomic records intodispatch_requests, and broadcasts an alert overlay to cleaners via Supabase Realtime channels. - Atomic First-to-Accept Locking: When a cleaner accepts, an atomic PostgreSQL transaction binds the cleaner to the booking and immediately cancels all other outstanding dispatch requests, preventing duplicate assignments.
- Slide-to-Confirm Ergonomics: Accidental taps on a "Book Now" button trigger unwanted dispatch fees and driver frustration. Borrowing from mission-critical transport UX, I designed a swipeable "Slide to confirm" slider widget that requires deliberate physical intent to lock in the reservation.
- Stripe Payment Authorization: Customers are never charged upfront for unfulfilled jobs. A Deno Edge Function creates a Stripe PaymentIntent with manual capture; the card is only captured when the cleaner taps "Job Complete" on-site.
Key Learnings from Marketplace Systems Engineering
- Domain nuances dictate interaction patterns: Ride-hailing prioritizes instant 3-minute pickups with random drivers; domestic services prioritize scheduling flexibility, background verification, and language compatibility. Tailoring the UI around trust metrics eliminated the primary point of customer friction.
- Enforce security at the database boundary: Relying on mobile app code to guard customer addresses and cleaner phone numbers invites data breaches. Enforcing Row Level Security (RLS) directly in PostgreSQL ensures that even if client code is reverse-engineered, user data remains untouchable.
- Monorepos eliminate sync drift: Maintaining separate repos for customer and provider apps inevitably leads to broken schemas and out-of-sync API contracts. A single Flutter monorepo with Freezed code generation ensures compile-time safety across all clients.