FieldForce Mobile App Design
FieldForce becomes a real offline-first Flutter mobile app backed by PowerSync, with the Go backend retaining all identity, write authority, conflict resolution, feature flags, and business rules. The grill pass tightened the design around Task as the canonical sync entity, mobile JWT/session source, Phase 1 Shift backend work, PowerSync Cloud Free for development, explicit uploadData endpoint mapping, rejected Task upload UX, and Crashlytics debug context.
FieldForce mobile is a Flutter, offline-first client over the existing Go backend. PowerSync owns interactive local data and sync; Go remains authoritative for identity, writes, conflict resolution, feature flags, location intake, and business rules.
Grill Outcome
Canonical entity
resolvedTask is the MVP sync entity. "Job" is informal copy only and Work Order is not modeled until the backend introduces it.
Security gate
resolvedUse the existing SolidStart / Better Auth source for mobile access: valid backend-issued session, isActive = true, and platform = "mobile", enforced by a centralized go_router guard.
Location authority
backend neededA server-side ff_shifts or equivalent Shift entity is required before production background location can be enabled.
Write bridge
resolveduploadData() is Dart-side bridge code that drains PowerSync queued writes into existing or entity-specific Go API endpoints.
Architecture
flowchart TD UI["Flutter presentation\nwidgets + Riverpod"] Domain["Pure Dart domain\nentities + use cases"] Data["Data repositories\nPowerSync + API datasources"] SQLite["Local SQLite\nPowerSync-managed"] Upload["PowerSync upload queue\nuploadData() Dart bridge"] Location["flutter_background_geolocation\nown offline queue"] PowerSync["Self-hosted PowerSync"] Go["Go API\nexisting endpoints"] LocAPI["Dedicated Go location endpoint"] PG["Postgres\nsingle source of truth"] Push["NATS JetStream / FCM + APNs"] UI --> Domain --> Data Data --> SQLite Data --> Upload --> Go Data --> Location --> LocAPI SQLite <--> PowerSync PowerSync <--> PG Go --> PG LocAPI --> PG PG --> Push Push --> UI
Core Decisions
Flutter + Melos for the mobile platform
Build FieldForce as a Flutter app in a Melos workspace.
The app needs offline-first work, push, background location, secure storage, and future multi-app package sharing. Flutter gives native mobile capability without hand-splitting iOS and Android.
Rejected: WebView/mobile web for native background constraints; separate native apps for scope and duplication.
PowerSync for interactive offline data
Use PowerSync-managed SQLite for Tasks and other interactive entities.
PowerSync gives durable local reads/writes, upload replay, and reactive queries while keeping Postgres as the source of truth.
Rejected: hand-built sync, conflict resolution, or local schema migration machinery.
Separate location telemetry path
Route background location through flutter_background_geolocation to a dedicated Go endpoint.
Location pings are high-volume append-only telemetry and should not bloat PowerSync buckets.
Rejected: syncing location through PowerSync buckets.
Security, Sync, And Conflicts
| Concern | Decision | Consequence |
|---|---|---|
| Verified access | SolidStart / Better Auth JWT source with platform = "mobile" | Device never asserts verification locally; valid session + isActive = true gates access. |
| Offline writes | uploadData() maps queued operations to Go endpoints | No generic PowerSync write endpoint; endpoint map is a Phase 1 artifact. |
| Task status conflict | ADR-0013 state machine wins | Rejected queued events show the reason, latest server version, failed local submit, and review/resubmit path. |
| PowerSync deployment | Cloud Free for development | Production hosting and bucket storage are decided later after real usage and compliance needs are known. |
| Background location | Three gates: feature flags, permission, active Shift | Production location remains off until Shift exists and is synced. |
| Crash debugging | Crashlytics plus custom non-PII keys | Record flavor, build, platform, route, org, hashed user ID, active Task/Shift, PowerSync state, queue count, last sync, location gates, and feature flags. |
Roadmap
- Phase 1
Offline-first Task cycle, mobile JWT gate,
ff_shiftsbackend work, brand UI, crash reporting, and location behind flags. - Phase 2
FCM/APNs hardening, silent pre-sync, deep links, assignment and approver notifications.
- Phase 3
Location maturity: OEM battery handling, shift-scoped consent, geofencing and activity tuning.
- Phase 4+
Digital Worker package reuse, OTA evaluation, richer field capabilities, and advanced sync scoping.
Open Questions
- PowerSync deployment: use PowerSync Cloud Free for development; decide production hosting and bucket storage later.
- JWT details: use the existing SolidStart / Better Auth auth source, with standard/custom JWT claims and
platform = "mobile". - Shift: implement
ff_shiftsor equivalent as explicit Phase 1 backend scope before enabling production location. - Endpoint map: fully define each
uploadData()operation-to-endpoint mapping before implementation. - Rejected Task uploads: show clear reason, latest server version, failed local submit state, and review/resubmit path; never silently delete the action.
- Sync Rules: MVP only requires users see allowed records; deeper RBAC partitioning is deferred.
- Licensing: confirm paid
flutter_background_geolocationproduction license. - Crashlytics context: set custom non-PII keys and verify no raw PII, tokens, or precise location are sent.