Desktop App Strategy & Roadmap
This document outlines the strategy for transforming the current web-based ERP system into a cross-platform desktop application (Windows, macOS, Linux).
Current Architecture Analysis
Section titled “Current Architecture Analysis”The current system (apps/webapp) is built with:
- Framework: Astro v5 + React 19
- Rendering Strategy: Server-Side Rendering (SSR) via
@astrojs/node. - Data Access: Direct database connections (
drizzle-orm) inside.astrofiles and API routes. - Queue/Background: Currently
bee-queue, moving to Temporal.- Rationale for migration: Temporal offers superior fault tolerance, reliable long-running workflow support, and better observability compared to bee-queue.
The Challenge
Section titled “The Challenge”Because the application uses SSR and connects directly to the database from the Node.js server, we cannot simply “bundle” the current code into a desktop app. A desktop app runs on the user’s machine, which:
- Does not have direct access to your production database (security/firewall).
- Cannot easily run the full Node.js server environment without significant bloat.
Proposed Strategy
Section titled “Proposed Strategy”We recommend a two-phase approach to deliver value quickly while building towards a robust architecture.
Phase 1: The “Wrapper” (Immediate Value)
Section titled “Phase 1: The “Wrapper” (Immediate Value)”Use Tauri v2 (see Technology Recommendation below) or Electron to create a lightweight native wrapper around the existing web application URL.
- How it works: The desktop app is essentially a specialized browser that points to
https://app.your-erp.com. - Pros:
- Speed: Can be built in < 1 week.
- Code Reuse: 100% reuse. No changes needed to the webapp.
- Updates: Users always see the latest version (no manual updates needed).
- Cons:
- Requires internet connection always.
- Doesn’t feel “native” (page loads, spinners).
- Limited OS integration (file system, notifications are trickier).
Phase 2: The “Thick Client” (Long Term)
Section titled “Phase 2: The “Thick Client” (Long Term)”Refactor the frontend to talk to a remote API, allowing the UI to run locally on the user’s machine.
- Architecture:
- Frontend (Desktop): React components running in Tauri/Electron (Client-Side Rendering).
- Backend (API): The existing Astro app exposes a JSON API (REST or tRPC) hosted remotely.
- Workflows: The remote API triggers Temporal workflows.
- Network Requirement: Phase 2 defaults to requiring an internet connection for writes and live data, but supports offline mode as described below. The term “Thick Client” refers to the local execution of UI logic.
API Deployment & Data Flow
Section titled “API Deployment & Data Flow”Crucially, the API remains remote and hosted, while the UI is bundled locally.
-
Hosted Remote API:
- The backend logic (database access, Temporal client, heavy computation) continues to run on your cloud servers (e.g.,
https://api.your-erp.com). - The desktop app does not contain a local Node.js server or a local copy of the full database.
- The backend logic (database access, Temporal client, heavy computation) continues to run on your cloud servers (e.g.,
-
Database Access:
- Remote DB: All writes and authoritative reads happen against the hosted production database via the API.
- Security: The desktop app never touches the DB directly. It authenticates via JWT/OAuth and makes HTTP requests to your API.
-
Temporal Workflow Execution:
- Server-Side Execution: Temporal Workers run on your backend infrastructure, not on the user’s desktop.
- Trigger Mechanism: The desktop app sends a request (e.g.,
POST /api/workflows/start-pdf-processing) to the hosted API. - API Responsibility: The API validates the request and calls
temporalClient.workflow.start(). - Status Updates: The API exposes endpoints for the desktop app to poll for status, or pushes updates via WebSockets/SSE.
- Recommendation: Use Server-Sent Events (SSE) for server-to-client workflow updates as it is simpler and more firewall-friendly than WebSockets, unless bidirectional real-time control is strictly required.
-
Static Output (
output: 'static'):- We configure Astro to build a Static Single Page Application (SPA).
- This generates HTML, CSS, and JS files that are bundled inside the Tauri/Electron binary.
- Result: The UI loads instantly from the local disk (no network latency for UI assets), but data is fetched dynamically from the remote API at runtime.
Offline Strategy (Sync)
Section titled “Offline Strategy (Sync)”⚠️ Complexity Warning: Implementing true offline capability with sync is a major undertaking that may double Phase 2 effort. Proceed only if strictly required.
If offline capability is a requirement, data entities must be strictly categorized:
1. Basic Offline (Read-Only Cache)
- Mechanism: Implement a local database (e.g., SQLite via Tauri plugin, or PouchDB in browser storage).
- Entities: Product Catalog, Customer List, Tax Rates.
- Safe to cache locally and update periodically.
2. Full Offline (Write + Sync/Outbox)
- Online-Only (Transactional):
- These entities require real-time validation against the central database.
- Entities: Inventory Counts, Pricing (tiered/contract), Orders, Payments.
- Must be fetched live from the API to prevent overselling or incorrect pricing.
- Optimistic UI & Sync Engine:
- Order Queuing: Allow users to create orders offline. The app stores them in a local “Outbox” queue.
- Sync Logic: When online, the sync engine replays the Outbox to the Remote API.
- Validation Rules: Unlike simple data, complex entities have constraints. For example, an offline order is accepted tentatively but Inventory and Pricing must be re-validated by the server during sync. If validation fails (e.g., out of stock), the user is notified to resolve the conflict.
Step-by-Step Transition to Phase 2
Section titled “Step-by-Step Transition to Phase 2”-
API-First Refactoring:
- Identify data loading in
.astrofiles (e.g.,dashboard.astrofetchingdb.select()...). - Move this logic to API endpoints (e.g.,
apps/webapp/src/pages/api/dashboard.ts). - Update the UI to fetch from these endpoints using React hooks (e.g.,
useQuery).
- Identify data loading in
-
Temporal Integration:
- Action: User clicks “Process PDF” in the Desktop App.
- Request: Desktop App POSTs to
/api/workflows/process-pdf. - Server: API Endpoint validates request -> Calls
temporalClient.workflow.start(). - Feedback: Desktop App polls/subscribes to workflow status.
-
Build Target:
- Configure Astro to output a Static Site (
output: 'static') for the desktop build. - Bundle this static site into the Tauri application.
- Configure Astro to output a Static Site (
Technology Recommendation
Section titled “Technology Recommendation”We strongly recommend Tauri v2 over Electron for this project.
Why Tauri?
- Performance: Your customers wanting a “downloaded app” likely value performance. Tauri apps start instantly.
- Security: Separation of concerns. The Rust backend handles OS interactions (File System), while the UI just renders.
- Future Proof: Fits the “modern stack” (Rust/TS) better than Electron.
Team Considerations:
- Rust Learning Curve: While the frontend remains React/TS, any backend logic (file system access, native dialogs) requires Rust. This can impact velocity if the team is unfamiliar with Rust’s ownership model and async patterns.
- Node Sidecars: While Tauri supports Node.js sidecars to run JS logic on the backend, this adds complexity and negates some performance benefits.
- Recommendation: If Rust is a blocker, build a small proof-of-concept first. If velocity is the only priority and bundle size is irrelevant, Electron remains a valid fallback.
| Feature | Tauri v2 | Electron |
|---|---|---|
| Binary Size | Tiny (~5-10 MB) | Huge (~100 MB+) |
| Memory Usage | Low (uses OS WebView) | High (bundles Chromium) |
| Security | High (Rust backend, tough sandbox) | Moderate (Node.js access) |
| Backend | Rust (can use Node sidecars) | Node.js |
| Frontend | Any (React/Astro works great) | Any |
Deployment & Updates
Section titled “Deployment & Updates”A robust deployment strategy is critical for managing both the desktop clients and the backend API.
-
API Hosting (Phase 2 Requirement):
- The API (
apps/webapprefactored) must be hosted on a cloud provider (e.g., AWS, GCP, Vercel) using containerization (Docker). - Versioning: Implement strict API versioning (e.g.,
/api/v1/orders). - Client Version Policy:
- Minimum Supported Version: Define a policy (e.g., support last 3 major versions).
- Enforcement: API Gateway should reject requests from deprecated clients with a 426 Upgrade Required.
- Sunset Policy: Announce deprecation windows (e.g., 6 months) via in-app notifications.
- Breaking Changes: Breaking changes to Temporal triggers or data schemas must be managed with deprecation windows. Feature flags should be used to roll out changes safely.
- The API (
-
Desktop Updates:
- Phase 1 (Wrapper): The wrapper points to the web URL, so updates are instant (server-side deploy). No client update needed unless the wrapper shell itself changes.
- Phase 2 (Native App): Use Tauri’s built-in Auto-Updater.
- Publish update artifacts (signatures and binaries) to an S3 bucket or GitHub Releases.
- The app checks for updates on launch and downloads/installs them in the background.
-
Observability & Error Reporting:
- Crash Reporting: Integrate a tool like Sentry into the Tauri app to capture native crashes and JS errors.
- Telemetry: Instrument key user actions to track feature usage.
- Monitoring: Correlate client versions with API error logs to identify version-specific regressions.
- Dashboard: Create a view to monitor the distribution of active client versions.
Roadmap for Code Reuse
Section titled “Roadmap for Code Reuse”-
Shared UI Library:
- Ensure
@repo/uicomponents are pure React (no server dependencies). - They can be used in both the Web (SSR) and Desktop (CSR) apps.
- Ensure
-
Authentication:
- Web: Cookies/Session.
- Desktop: Implement an OAuth flow (open browser to login -> callback to app scheme
erp-app://callback) or Device Flow. - Token Management:
- Storage: Store the Access Token (short-lived) and Refresh Token (long-lived) in the system keychain using Tauri’s secure storage plugin.
- Auto-Refresh: Intercept 401 responses or proactively refresh tokens before expiry.
- Session Persistence: Use Refresh Tokens to persist sessions across app restarts.
- Re-Auth: If the Refresh Token expires, trigger the OAuth flow again (redirect to browser).
- Security: Implement “Remember Me” toggles and Refresh Token rotation.
-
File Access (PDFs):
- Web: Standard HTML file input.
- Desktop: Native file drag-and-drop or file system dialog. The file content is read by the Rust backend and streamed securely to the remote API endpoint.
Summary
Section titled “Summary”- Start with Tauri Wrapper (Phase 1) to get an icon on desktops immediately.
- Refactor
apps/webappto expose API endpoints for data. - Move Temporal workflow triggers behind these API endpoints.
- Transition to a Native Tauri App (Phase 2) that bundles the React UI and talks to the API.
Revision History
Section titled “Revision History”- Initial Draft: Strategy for cross-platform desktop app migration.
- Phase 2 Clarification: Detailed API deployment, DB/Temporal access, and offline strategies.
- Offline Data Strategy: Explicit categorization of read-only vs. transactional entities and sync validation rules.
- Deployment & Clarifications: Added deployment section, clarified remote API dependency, and refined auth/file access details.
- Architecture Refinement: Added token management, observability, detailed offline warnings, and team considerations for Rust.