Athena — roomy-mobile/archive/changes/2026-01-05-implement-notifications/design.md

Context

The Roomy app currently has no push notification infrastructure. Users have no way of knowing when household members add or complete shopping list items without manually checking the app. The existing lib/inbox/ feature has UI scaffolding and a MessageDto but no backend integration.

Stakeholders:

  • End users: Receive timely notifications about household activity
  • Household members: Stay coordinated on shopping tasks
  • Developers: Maintain clean architecture patterns

Constraints:

  • Must follow existing MVVM architecture and service patterns
  • Must integrate with existing MessageDto and inbox UI
  • Must support both iOS and Android platforms
  • Must handle FCM configuration via Playwright automation
  • Must batch notifications within 30-second windows to reduce noise

Goals / Non-Goals

Goals:

  • Implement FCM push notifications for shopping list item add/complete events
  • Store notifications in Firestore for in-app inbox viewing
  • Provide per-category notification preferences (Shopping, Cleaning, etc.)
  • Support deep linking from notification tap to relevant shopping list
  • Track read/unread state with visual indicators

Non-Goals:

  • Email notifications (existing email infrastructure is for marketing only)
  • Quiet hours / do-not-disturb scheduling (future enhancement)
  • Rich media notifications (images, actions beyond deep link)
  • Web push notifications

Decisions

1. FCM Token Storage Strategy

Decision: Store FCM tokens as a subcollection under users (users/{userId}/fcmTokens/{tokenId}) Alternatives considered:

  • Array field on user document: Limited to 1MB document size, no token metadata
  • Separate top-level collection: Requires additional security rules complexity Rationale: Subcollection allows multiple devices per user, automatic cleanup via TTL, and simple security rules (user owns their tokens).

2. Notification Dispatch Architecture

Decision: Use Firestore onCreate/onUpdate triggers on shoppingListItems collection with Cloud Task-based batching Alternatives considered:

  • Direct FCM send from Flutter app: Requires service account on client, security risk
  • Scheduled Cloud Function polling: Higher latency, more complex state management Rationale: Triggers provide real-time response. Cloud Tasks enable 30-second batching window without blocking the trigger function.

3. Notification Document Structure

Decision: Extend existing MessageDto structure for notifications, add householdId and notificationType fields Alternatives considered:

  • Create separate NotificationDto: Duplicates existing message structure
  • Store only in FCM data payload: No persistence for inbox viewing Rationale: MessageDto already has notificationTitle, notificationMessage, readAt, and recipientId fields. Minor extension maintains consistency.

4. Batching Implementation

Decision: Use Cloud Tasks with 30-second delay. Batch key: {householdId}:{actorUserId}:{action}:{timestamp_bucket} Alternatives considered:

  • In-memory aggregation in Cloud Function: Stateless functions lose state on cold starts
  • Firestore-based aggregation: Additional reads/writes, complex cleanup Rationale: Cloud Tasks guarantee at-least-once delivery. Failed batches can be retried. Timestamp buckets (30s) ensure deterministic batch grouping.

5. Notification Preferences Schema

Decision: Store as subcollection users/{userId}/settings/notifications with category-based toggles Alternatives considered:

  • Add to existing SettingsDto: Increases document size, couples concerns
  • Top-level collection: Complicates security rules Rationale: Subcollection under settings follows existing pattern (users/{userId}/settings). Easy to extend with additional categories.

6. Deep Linking Strategy

Decision: Use FCM data payload with route and params fields. Flutter app parses and navigates via go_router. Alternatives considered:

  • Firebase Dynamic Links: Being deprecated by Google
  • Custom URL scheme: Requires additional platform configuration Rationale: FCM data payloads are reliable and already supported. go_router can handle programmatic navigation.

Data Models

Firestore Collections

users/{userId}/fcmTokens/{tokenId}
  - token: string
  - platform: 'ios' | 'android'
  - createdAt: timestamp
  - lastActiveAt: timestamp

users/{userId}/settings/notifications
  - shoppingEnabled: boolean (default: true)
  - cleaningEnabled: boolean (default: true)
  - globalEnabled: boolean (default: true)
  - updatedAt: timestamp

notifications/{notificationId}
  - id: string
  - householdId: string
  - recipientId: string
  - senderId: string
  - notificationType: 'shoppingItemAdded' | 'shoppingItemCompleted' | ...
  - title: string
  - message: string
  - created: timestamp
  - readAt: timestamp | null
  - data: { shoppingListId: string, itemCount: number, ... }

Cloud Function Flow

1. shoppingListItems.onCreate/onUpdate trigger fires
2. Check if action is add or complete
3. Fetch household members (excluding actor)
4. For each member: check notification preferences
5. Queue Cloud Task with 30s delay, batch key
6. Cloud Task handler:
   a. Aggregate items in batch window
   b. Create notification document per recipient
   c. Fetch recipient FCM tokens
   d. Send FCM multicast

Risks / Trade-offs

| Risk | Mitigation | |------|------------| | FCM token stale/invalid | Implement token refresh on app launch, handle FCM error codes to remove invalid tokens | | Cloud Task batch race conditions | Use idempotent batch keys, deduplicate notifications in handler | | Notification spam | 30-second batching, per-category preferences, future quiet hours | | APNs certificate expiry | Document renewal process, add monitoring/alerting | | Deep link to deleted item | Handle gracefully in Flutter - show error toast, navigate to shopping list |

Migration Plan

Phase 1: Infrastructure (no user impact)

  1. Add FCM dependencies to Flutter app
  2. Create Cloud Functions structure
  3. Configure FCM in Firebase Console (Playwright)
  4. Configure APNs in Apple Developer Portal (Playwright)

Phase 2: Backend (no user impact)

  1. Deploy Cloud Functions (disabled triggers)
  2. Create Firestore security rules for new collections
  3. Implement FCM token registration in Flutter

Phase 3: Feature rollout

  1. Enable Cloud Function triggers
  2. Deploy Flutter notification handling
  3. Add notification preferences to settings
  4. Enhance inbox UI with notification display

Rollback:

  • Disable Cloud Function triggers to stop new notifications
  • FCM tokens persist but are unused
  • Existing inbox functionality unchanged

Open Questions

  1. Resolved: Should notifications be batched? → Yes, 30-second window
  2. Resolved: Per-category or global preferences? → Per-category (Shopping, Cleaning, etc.)
  3. Future: Should we add quiet hours? → Not in initial scope, can be added later
  4. Future: Should we support notification actions (mark complete from notification)? → Not in initial scope

Reacties

Nog geen reacties