docs: add MotorSignal cloud architecture
This commit is contained in:
parent
8288229d78
commit
6bc15fb179
|
|
@ -0,0 +1,639 @@
|
|||
# MotorSignal Cloud Architecture
|
||||
|
||||
**Status:** Planning baseline
|
||||
**Date:** 2026-09-01
|
||||
**Backend direction:** Firebase Authentication + Firebase cloud services, integrated with the existing local-first Flutter application
|
||||
|
||||
## Purpose
|
||||
|
||||
Define the cloud architecture required for MotorSignal accounts, ride backup, synchronization, profiles, friends, planned rides, trips, and group rides.
|
||||
|
||||
This document intentionally favors an **offline-first** architecture. Local data remains the immediate source for recording and ride operation; cloud services provide identity, backup, synchronization, collaboration, and cross-device continuity.
|
||||
|
||||
---
|
||||
|
||||
# Architectural Goals
|
||||
|
||||
1. Recording must work without internet access.
|
||||
2. Cloud synchronization must be resilient to intermittent connectivity.
|
||||
3. A user's data must be isolated from other users by security rules.
|
||||
4. Synchronization must be idempotent and safe to retry.
|
||||
5. Reinstalling the app must not require recreating a user's ride history.
|
||||
6. Cloud functionality must support both Free and Pro entitlements.
|
||||
7. The data model must support future social/group-riding features without a major rewrite.
|
||||
8. Sensitive location information must receive stronger privacy treatment than ordinary profile information.
|
||||
9. Account deletion must have a defined, testable data-deletion path.
|
||||
10. Raw high-volume telemetry should not automatically be treated like ordinary profile documents.
|
||||
|
||||
---
|
||||
|
||||
# High-Level Architecture
|
||||
|
||||
```text
|
||||
MotorSignal Flutter App
|
||||
│
|
||||
┌────────────────────┴────────────────────┐
|
||||
│ │
|
||||
Local Database Cloud Services
|
||||
/ Local Files / Firebase
|
||||
│ │
|
||||
│ ┌──────────┼──────────┐
|
||||
│ │ │ │
|
||||
│ Auth Firestore Storage
|
||||
│ │ │ │
|
||||
└──────────────┬───────────────┴──────────┴──────────┘
|
||||
│
|
||||
Sync Engine
|
||||
│
|
||||
Entitlement
|
||||
│
|
||||
Pro / Free
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Identity
|
||||
|
||||
Firebase Authentication provides the account identity.
|
||||
|
||||
The internal account identifier should remain stable even if a user changes:
|
||||
|
||||
- Display name.
|
||||
- Username.
|
||||
- Profile photo.
|
||||
- Optional public location.
|
||||
|
||||
Google sign-in should remain compatible with the existing application authentication flow.
|
||||
|
||||
The Firebase user ID should be treated as an internal identifier, not a public username.
|
||||
|
||||
---
|
||||
|
||||
# Proposed Data Model
|
||||
|
||||
The exact collection structure can be refined during implementation, but the logical model should include:
|
||||
|
||||
```text
|
||||
users/{userId}
|
||||
|
||||
users/{userId}/motorcycles/{motorcycleId}
|
||||
users/{userId}/rides/{rideId}
|
||||
users/{userId}/trips/{tripId}
|
||||
users/{userId}/events/{eventId}
|
||||
users/{userId}/friends/{friendId}
|
||||
```
|
||||
|
||||
Potential future top-level collaborative resources:
|
||||
|
||||
```text
|
||||
groupRides/{groupRideId}
|
||||
publicRoutes/{routeId}
|
||||
```
|
||||
|
||||
These should only be introduced when query patterns and security requirements justify them.
|
||||
|
||||
---
|
||||
|
||||
# User Document
|
||||
|
||||
Conceptual fields:
|
||||
|
||||
```text
|
||||
id
|
||||
username
|
||||
displayName
|
||||
profilePhotoUrl
|
||||
bio
|
||||
location
|
||||
createdAt
|
||||
updatedAt
|
||||
privacySettings
|
||||
preferences
|
||||
```
|
||||
|
||||
Do not store sensitive secrets in the user document.
|
||||
|
||||
Avoid storing a complete ride history or large telemetry payload directly inside the user document.
|
||||
|
||||
---
|
||||
|
||||
# Motorcycle Document
|
||||
|
||||
Conceptual fields:
|
||||
|
||||
```text
|
||||
id
|
||||
ownerId
|
||||
year
|
||||
make
|
||||
model
|
||||
nickname
|
||||
photoUrl
|
||||
photoSource
|
||||
photoAttribution
|
||||
createdAt
|
||||
updatedAt
|
||||
privacy
|
||||
```
|
||||
|
||||
The motorcycle should have a stable ID independent of its displayed name.
|
||||
|
||||
---
|
||||
|
||||
# Ride Document
|
||||
|
||||
Conceptual metadata:
|
||||
|
||||
```text
|
||||
id
|
||||
ownerId
|
||||
motorcycleId
|
||||
tripId?
|
||||
groupRideId?
|
||||
startedAt
|
||||
endedAt
|
||||
duration
|
||||
distance
|
||||
summaryStatistics
|
||||
routeReference?
|
||||
telemetryReference?
|
||||
createdAt
|
||||
updatedAt
|
||||
syncState
|
||||
visibility
|
||||
```
|
||||
|
||||
High-volume telemetry should not be duplicated unnecessarily across multiple Firestore documents.
|
||||
|
||||
Where appropriate, use compressed/structured files in Firebase Storage and keep searchable summary metadata in Firestore.
|
||||
|
||||
---
|
||||
|
||||
# Trip Document
|
||||
|
||||
Conceptual fields:
|
||||
|
||||
```text
|
||||
id
|
||||
ownerId
|
||||
name
|
||||
startDate
|
||||
endDate
|
||||
description
|
||||
routeReferences
|
||||
waypoints
|
||||
participants
|
||||
createdAt
|
||||
updatedAt
|
||||
visibility
|
||||
```
|
||||
|
||||
Trips may contain multiple daily routes and completed rides.
|
||||
|
||||
---
|
||||
|
||||
# Event Document
|
||||
|
||||
For meetups and planned events:
|
||||
|
||||
```text
|
||||
id
|
||||
ownerId
|
||||
name
|
||||
description
|
||||
startAt
|
||||
location
|
||||
attendees
|
||||
linkedGroupRideId?
|
||||
createdAt
|
||||
updatedAt
|
||||
visibility
|
||||
```
|
||||
|
||||
An event does not automatically imply live location sharing.
|
||||
|
||||
---
|
||||
|
||||
# Friend Relationship
|
||||
|
||||
Friend relationships should have explicit state.
|
||||
|
||||
```text
|
||||
PENDING
|
||||
ACCEPTED
|
||||
DECLINED
|
||||
BLOCKED
|
||||
```
|
||||
|
||||
Friendship should not automatically grant access to private ride data.
|
||||
|
||||
Visibility remains controlled by the owner's privacy settings.
|
||||
|
||||
---
|
||||
|
||||
# Group Ride
|
||||
|
||||
A group ride should be a shared activity context while each rider retains ownership of their own ride record.
|
||||
|
||||
Conceptually:
|
||||
|
||||
```text
|
||||
Group Ride
|
||||
├── Organizer
|
||||
├── Participants
|
||||
├── Route
|
||||
├── Meeting Point
|
||||
├── Schedule
|
||||
├── Live Ride State
|
||||
└── Completed Ride References
|
||||
```
|
||||
|
||||
A rider's individual telemetry remains associated with that rider and motorcycle.
|
||||
|
||||
---
|
||||
|
||||
# Local-First Sync
|
||||
|
||||
The app should never require a successful cloud write before considering a ride locally saved.
|
||||
|
||||
### Write flow
|
||||
|
||||
```text
|
||||
User records ride
|
||||
↓
|
||||
Save locally
|
||||
↓
|
||||
Mark PENDING_UPLOAD
|
||||
↓
|
||||
Continue normal app operation
|
||||
↓
|
||||
Connectivity available
|
||||
↓
|
||||
Sync engine uploads
|
||||
↓
|
||||
Server acknowledgement
|
||||
↓
|
||||
Mark SYNCED
|
||||
```
|
||||
|
||||
### Offline flow
|
||||
|
||||
```text
|
||||
No internet
|
||||
↓
|
||||
Record normally
|
||||
↓
|
||||
Queue cloud operation
|
||||
↓
|
||||
Internet returns
|
||||
↓
|
||||
Retry automatically
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Sync States
|
||||
|
||||
Recommended common state model:
|
||||
|
||||
```text
|
||||
LOCAL_ONLY
|
||||
PENDING_UPLOAD
|
||||
SYNCED
|
||||
PENDING_DELETE
|
||||
CONFLICT
|
||||
SYNC_FAILED
|
||||
```
|
||||
|
||||
The implementation may combine or rename states, but synchronization must be observable and debuggable.
|
||||
|
||||
---
|
||||
|
||||
# Idempotency
|
||||
|
||||
Cloud writes must be safe to retry.
|
||||
|
||||
Do not create a new ride document every time an upload request is retried.
|
||||
|
||||
Use stable application-generated IDs so:
|
||||
|
||||
```text
|
||||
same ride ID + repeated upload = same logical ride
|
||||
```
|
||||
|
||||
not:
|
||||
|
||||
```text
|
||||
same ride + repeated upload = duplicate rides
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Conflict Handling
|
||||
|
||||
Conflicts should be rare but must be explicitly designed.
|
||||
|
||||
Potential strategy:
|
||||
|
||||
- Server metadata determines whether an object changed.
|
||||
- Immutable ride telemetry should generally not be merged field-by-field.
|
||||
- User-editable metadata can use last-write-wins or explicit conflict resolution where appropriate.
|
||||
- Never silently overwrite a user's ride data without a defined policy.
|
||||
|
||||
For example, if two devices edit a motorcycle nickname, last-write-wins may be acceptable.
|
||||
|
||||
If two devices produce different telemetry for the same ride ID, preserve enough information to diagnose the conflict rather than silently destroying one copy.
|
||||
|
||||
---
|
||||
|
||||
# Cloud Storage
|
||||
|
||||
Use Firebase Storage for large binary objects where appropriate:
|
||||
|
||||
- Ride telemetry files.
|
||||
- Exported reports if cloud retention is required.
|
||||
- Profile photos.
|
||||
- Motorcycle photos where user-uploaded.
|
||||
- Future ride media.
|
||||
|
||||
Keep Firestore documents focused on metadata, indexes, relationships, and summaries.
|
||||
|
||||
---
|
||||
|
||||
# Security Model
|
||||
|
||||
Every user-owned resource must enforce ownership server-side.
|
||||
|
||||
Conceptually:
|
||||
|
||||
```text
|
||||
request.auth.uid == resource.ownerId
|
||||
```
|
||||
|
||||
or an equivalent rule based on the final data model.
|
||||
|
||||
Never rely solely on Flutter UI restrictions to protect data.
|
||||
|
||||
A hidden button is not a security boundary.
|
||||
|
||||
---
|
||||
|
||||
# Social Security
|
||||
|
||||
Friendship and group participation must not automatically permit access to all account data.
|
||||
|
||||
Examples:
|
||||
|
||||
- Being a friend does not expose private ride history.
|
||||
- Joining a group ride does not expose a user's historical rides.
|
||||
- Live location is only available during explicitly authorized group activity.
|
||||
- Public profile data is distinct from private account data.
|
||||
|
||||
---
|
||||
|
||||
# Location Privacy
|
||||
|
||||
Ride telemetry and GPS tracks can reveal a user's home, workplace, and routine.
|
||||
|
||||
The platform should support:
|
||||
|
||||
- Private rides.
|
||||
- Friends-only rides.
|
||||
- Public rides.
|
||||
- Optional location sharing.
|
||||
- Home/start/end location obfuscation for shared rides.
|
||||
- No continuous background social tracking merely because a user has an account.
|
||||
|
||||
Live group location must require explicit participation/consent and should automatically stop at the end of the group ride.
|
||||
|
||||
---
|
||||
|
||||
# Cloud Entitlements
|
||||
|
||||
Cloud usage must be controlled through the centralized entitlement system.
|
||||
|
||||
Example capabilities:
|
||||
|
||||
```text
|
||||
canCloudBackup
|
||||
canUnlimitedCloudHistory
|
||||
canMultiDeviceRestore
|
||||
```
|
||||
|
||||
The client may use these capabilities for UX, but backend rules must also enforce limits and ownership where practical.
|
||||
|
||||
Do not trust a client-provided `isPro=true` field as proof of payment.
|
||||
|
||||
---
|
||||
|
||||
# Subscription / Account Separation
|
||||
|
||||
A user account should continue to exist even if a Pro subscription expires.
|
||||
|
||||
Conceptually:
|
||||
|
||||
```text
|
||||
Firebase Account
|
||||
│
|
||||
├── Profile
|
||||
├── Garage
|
||||
├── Rides
|
||||
└── Entitlement
|
||||
│
|
||||
┌──────┴──────┐
|
||||
│ │
|
||||
Free Pro Monthly/Lifetime
|
||||
```
|
||||
|
||||
Billing provider state determines the paid entitlement. The account owns the data.
|
||||
|
||||
---
|
||||
|
||||
# Expired Pro Data Policy
|
||||
|
||||
The final product policy must be defined before launch.
|
||||
|
||||
Recommended direction:
|
||||
|
||||
- Do not silently delete historical rides when Pro expires.
|
||||
- Preserve account and existing data.
|
||||
- Restrict creation/synchronization of additional premium-only data if the user exceeds Free limits.
|
||||
- Allow upgrade to restore full Pro functionality.
|
||||
|
||||
The exact storage allowance and retention policy must be finalized after Firebase cost modeling and legal review.
|
||||
|
||||
---
|
||||
|
||||
# Backup / Restore
|
||||
|
||||
Backup should be automatic where the user's entitlement allows it.
|
||||
|
||||
Restore should be explicit and understandable.
|
||||
|
||||
New device flow:
|
||||
|
||||
```text
|
||||
Install MotorSignal
|
||||
↓
|
||||
Sign in
|
||||
↓
|
||||
Authenticate
|
||||
↓
|
||||
Discover cloud account
|
||||
↓
|
||||
Download motorcycle/profile metadata
|
||||
↓
|
||||
Download ride metadata
|
||||
↓
|
||||
Download required ride data
|
||||
↓
|
||||
Rebuild local database
|
||||
↓
|
||||
Ready
|
||||
```
|
||||
|
||||
The restore process should be resumable if interrupted.
|
||||
|
||||
---
|
||||
|
||||
# Account Deletion
|
||||
|
||||
Account deletion must be a real backend workflow.
|
||||
|
||||
It should address:
|
||||
|
||||
- Firebase Authentication identity.
|
||||
- User profile.
|
||||
- Motorcycles.
|
||||
- Rides.
|
||||
- Telemetry files.
|
||||
- Profile photos.
|
||||
- Social relationships.
|
||||
- Trips/events owned by the user.
|
||||
- Group ride participation data where legally/technically appropriate.
|
||||
|
||||
Do not simply delete the authentication record while leaving user data orphaned indefinitely.
|
||||
|
||||
---
|
||||
|
||||
# Data Export
|
||||
|
||||
Provide an account-level export capability where required/appropriate.
|
||||
|
||||
Potential export contents:
|
||||
|
||||
- Profile information.
|
||||
- Motorcycle data.
|
||||
- Ride metadata.
|
||||
- Ride telemetry.
|
||||
- Routes.
|
||||
- Trip data.
|
||||
|
||||
The existing CSV/PDF ride exports are not necessarily sufficient to satisfy an account-level data export requirement.
|
||||
|
||||
---
|
||||
|
||||
# Observability
|
||||
|
||||
Cloud synchronization should provide enough logging to diagnose:
|
||||
|
||||
- Authentication failures.
|
||||
- Upload failures.
|
||||
- Download failures.
|
||||
- Permission-denied errors.
|
||||
- Duplicate prevention failures.
|
||||
- Conflict events.
|
||||
- Storage failures.
|
||||
- Billing/entitlement synchronization problems.
|
||||
|
||||
Do not log raw sensitive telemetry or credentials unnecessarily.
|
||||
|
||||
---
|
||||
|
||||
# Testing Matrix
|
||||
|
||||
### Authentication
|
||||
|
||||
- [ ] Fresh account.
|
||||
- [ ] Existing account.
|
||||
- [ ] Sign out.
|
||||
- [ ] Sign in again.
|
||||
- [ ] Multiple devices.
|
||||
- [ ] Account deletion.
|
||||
|
||||
### Offline
|
||||
|
||||
- [ ] Record without internet.
|
||||
- [ ] Save ride locally.
|
||||
- [ ] Restore connectivity.
|
||||
- [ ] Automatic upload.
|
||||
- [ ] Retry after failure.
|
||||
|
||||
### Sync
|
||||
|
||||
- [ ] New motorcycle sync.
|
||||
- [ ] Ride sync.
|
||||
- [ ] Ride update.
|
||||
- [ ] Ride deletion.
|
||||
- [ ] Duplicate upload prevention.
|
||||
- [ ] Conflict behavior.
|
||||
|
||||
### Restore
|
||||
|
||||
- [ ] Reinstall.
|
||||
- [ ] New device.
|
||||
- [ ] Large ride history.
|
||||
- [ ] Interrupted restore.
|
||||
- [ ] Partial network failure.
|
||||
|
||||
### Security
|
||||
|
||||
- [ ] User A cannot read User B's private rides.
|
||||
- [ ] User A cannot write User B's data.
|
||||
- [ ] Friend access respects privacy.
|
||||
- [ ] Group membership does not grant historical access.
|
||||
- [ ] Client cannot self-grant Pro entitlement.
|
||||
|
||||
---
|
||||
|
||||
# Implementation Order
|
||||
|
||||
1. Audit existing Firebase/auth implementation.
|
||||
2. Document current local Room/database models.
|
||||
3. Define cloud DTOs and stable IDs.
|
||||
4. Define Firestore/Storage collections.
|
||||
5. Write security rules before exposing new data.
|
||||
6. Implement profile document.
|
||||
7. Implement motorcycle synchronization.
|
||||
8. Implement ride metadata synchronization.
|
||||
9. Implement telemetry/blob synchronization.
|
||||
10. Implement retry queue.
|
||||
11. Implement conflict/error handling.
|
||||
12. Implement backup status UI.
|
||||
13. Implement restore.
|
||||
14. Add account deletion/data export.
|
||||
15. Connect cloud capability checks to entitlement manager.
|
||||
16. Test Free/Pro storage behavior.
|
||||
17. Add friends/social resources.
|
||||
18. Add planned rides/trips.
|
||||
19. Add group rides and explicit live-location sessions.
|
||||
|
||||
---
|
||||
|
||||
# Design Rule
|
||||
|
||||
The cloud system must make MotorSignal more reliable, not more fragile.
|
||||
|
||||
The critical riding path should remain:
|
||||
|
||||
```text
|
||||
Sensors → Local Recording → Local Database → Ride Processing
|
||||
```
|
||||
|
||||
Cloud synchronization should be downstream:
|
||||
|
||||
```text
|
||||
Local Ride → Sync Queue → Cloud Backup
|
||||
```
|
||||
|
||||
A cloud outage must not prevent a rider from recording a ride.
|
||||
Loading…
Reference in New Issue