Architecture

Overview

FlowDice is a Ruby on Rails web application that ships as a PWA first, with Hotwire Native shells added in Phase 3. The dice engine is factored as a standalone Ruby module to enable reuse across the web UI, API, MCP Server, and Discord Bot.

Rails App

  • Framework: Rails 8+ with Hotwire (Turbo Drive + Stimulus)
  • Frontend: Mobile-first responsive HTML/CSS, Stimulus controllers for calculator interactivity
  • Offline: Service worker caches all pages and assets for full offline PWA support
  • Navigation: Standard Turbo Drive full-page navigation (not heavy Turbo Frames) — this is critical for Hotwire Native compatibility
  • RNG: Cryptographically secure random number generation via SecureRandom

Key Components

Dice Engine (standalone Ruby module)

The notation parser and evaluator. Takes a Notation Spec string, parses it into an AST, and evaluates it to produce results with individual die values. Factored as a standalone module/gem so it can be consumed by multiple interfaces.

FlowDice::Engine (core Ruby module)
├── Web UI (Rails controllers + Stimulus)
├── JSON API (for native apps)
├── MCP Server (tool definitions + handlers)
├── Discord Bot (via MCP client)
└── Future: CLI, etc.

Core responsibilities:

Calculator UI

Stimulus controller managing the button-based input. Builds notation strings from button presses, validates input state (disabling invalid buttons), and displays live notation + explanation.

MCP Server

Exposes the dice engine as MCP tools for AI assistants, VTTs, bots, and other integrations. Thin wrapper over the same engine — see MCP Server for tool definitions.

Discord Bot

Lightweight Node.js interface using discord.js, connecting to the FlowDice instance via MCP Server. Provides slash commands (/roll), manages Discord interactions, and renders results as embeds with interactive parameter buttons.

Favorites System

Named, saved notation strings with optional Named Parameters. Single-tap to roll, long-press for parameter toggles. Stored locally for offline support; synced to cloud in Phase 3 with Characters.

Roll History

Chronological log of all rolls with notation, results, and individual die values. Tap to re-roll.

Data Model

ModelFieldsNotes
Rollnotation, result, individual_dice, rolled_atEvery roll executed
Favoritename, notation, last_result, positionUser-saved rolls
Charactername, system, stats, weapons, spells, class_featuresPhase 3; synced to cloud

Phase 1 stores everything client-side (localStorage/IndexedDB) for offline-first. Server-side persistence is a Phase 2 consideration if sync or accounts are added. Phase 3 requires server-side character storage for multi-device sync.

Interfaces

FlowDice engine has 7 interfaces:

  1. Web UI — Rails app + PWA
  2. PWA — offline-capable web app
  3. Hotwire Native iOS — Swift/Xcode shell (Phase 3)
  4. Hotwire Native Android — Kotlin/Android Studio shell (Phase 3)
  5. JSON API — for any client (native, web, third-party)
  6. MCP Server — for AI assistants, VTTs, integrations
  7. Discord Bot — slash commands via Discord Bot
  8. Future: Android Auto (Phase 3 stretch), CLI, etc.

All routes feed to the same FlowDice::Engine, so behavior is identical everywhere.

Platform Progression

  1. Phase 1: Rails app → browser (desktop + mobile) + PWA
  2. Phase 1 Stretch: MCP Server (basic roll + parse tools)
  3. Phase 2: Full MCP Server + Template Library + Discord Bot + JSON API
  4. Phase 3: Hotwire Native iOS shell + Android shell + Characters + cloud sync
  5. Stretch: Android Auto, CLI, etc.

Hotwire Native Readiness

Building the Rails app with these conventions ensures smooth native wrapping later:

  • Mobile-first responsive CSS from day one
  • URL-based navigation (not heavy Turbo Frames for primary flows)
  • turbo_native_app? helper available for conditional native UI
  • Clean URL structure maps to native navigation stacks
  • Service worker enables offline support in both web and native contexts

See also: FlowDice, Notation Spec, Hotwire Native, MCP Server, Discord Bot