Architecture: Mobayilo
Architecture: Mobayilo
Section titled “Architecture: Mobayilo”Source of truth note
Section titled “Source of truth note”For Mobayilo, the source of truth is:
- Rails 8.1 monolith
- Hotwire (Turbo/Stimulus) views
- Server-rendered pages + small JSON endpoints where needed
What this is
Section titled “What this is”Mobayilo is a mobile-first web app that lets users place international calls from their browser (WebRTC) to real phone numbers (PSTN) using Twilio, funded by a pay-as-you-go credit wallet charged via Stripe.
Stack (target)
Section titled “Stack (target)”- Rails 8.1 (monolith)
- Hotwire (Turbo + Stimulus) for SPA-like UX without a JS-heavy frontend
- Tailwind CSS for mobile-first responsive UI
- PostgreSQL for users, balances, call logs, and ledger transactions
- Twilio Programmable Voice for PSTN + browser WebRTC
- Stripe for one-time credit purchases
High-level architecture
Section titled “High-level architecture”Browser (Hotwire + Twilio Voice JS) ↕ HTTPSRails 8.1 (HTML + JSON endpoints + webhooks) ↕PostgreSQL ↕ ↕Stripe webhooks Twilio webhooks/TwiMLCore domain objects (MVP)
Section titled “Core domain objects (MVP)”User: authentication + settings.Accountwallet:cached_balance_centsis the operational balance cache;Transactionledger is the source of truth.Transaction(ledger): every balance mutation (purchase, call charge, refund, bonus).Call: one row per call attempt; stores Twilio SIDs, status, duration, computed cost.CallRate: price per minute by destination (country/prefix-based).
Key backend responsibilities
Section titled “Key backend responsibilities”- Token issuance: generate Twilio capability/access tokens for the browser SDK after checking user status + balance.
- Call orchestration: accept a “call initiate” action and hand off to Twilio with the destination number.
- TwiML generation: return TwiML that instructs Twilio how to connect calls.
- Webhook processing:
- Twilio status callbacks update
Callstate and final duration. - Stripe events create/complete a credit purchase transaction.
- Twilio status callbacks update
- Billing: compute final call cost and create a ledger entry to deduct credits exactly once (idempotent).
Mobile-first UX principles
Section titled “Mobile-first UX principles”- Dialer is the primary screen: large tap targets, bottom-aligned controls, minimal text.
- Use a bottom nav for 3–5 main areas (Dial, History, Credits, Settings).
- Optimize for flaky networks: optimistic UI + clear retry states.
- Avoid heavy client state; rely on Turbo and server truth.
Proposed routes (MVP)
Section titled “Proposed routes (MVP)”- Auth:
sessions,registrations,passwords(Devise) - Dialer:
GET /call,POST /calls(initiate) - Calls:
GET /history,GET /calls/:id - Credits:
GET /credits,POST /credits/purchase,GET /transactions - Rates:
GET /rates,GET /rates/calculate?phone_number=... - Webhooks:
POST /api/webhooks/twilio/voice(TwiML)POST /api/webhooks/twilio/status(status updates)POST /api/webhooks/stripe(payment events)
Background jobs (recommended)
Section titled “Background jobs (recommended)”- Post-process Twilio call completion to compute/charge cost (idempotent).
- Periodic sync/update of Twilio pricing (optional) and refreshing
CallRate.
MVP milestones (practical)
Section titled “MVP milestones (practical)”- Rails app skeleton (Tailwind + Hotwire) + Devise auth.
- Wallet + ledger transactions (DB + UI).
- Stripe top-ups (PaymentIntent + webhook crediting).
- Twilio Voice JS in dialer (token issuance + basic call connect/disconnect).
- Twilio status webhook → call logs → cost deduction.
- History + rates calculator UI; basic admin tools.
Decisions to confirm
Section titled “Decisions to confirm”- Billing rounding (MVP): per-started-minute.
- JS bundling:
esbuild(recommended) vs importmap. - Call status UI: Turbo Streams only vs add Action Cable for richer events.
Production blockers
Section titled “Production blockers”- Remove any dev-only SMTP CRL bypass (
SMTP_DISABLE_CRL_CHECK/VERIFY_NONE) before launch.