This document details the technical and architectural decisions made to enable robust, scalable, and secure multi-device push notifications in ClarityBox. The goal: seamless experience for users across all their devices, with real-time sync, strong data integrity, and future-proof extensibility.
1. Firestore Data Model & Device-Centric Notification Storage
1.1. User Document Schema
All notification-related data is centralized in the /users/{userId}
document. Each user document contains:
pushSubscriptions
: an object mappingdeviceId
to the push subscription payload for that device.reminderSettings
: the user’s daily reminder configuration (time, enabled, timezone).
Firestore Schema Example:
{
"pushSubscriptions": {
"device-abc123": {
"endpoint": "https://fcm.googleapis.com/fcm/send/xxxx...",
"keys": { "p256dh": "...", "auth": "..." }
},
"device-def456": { /* ... */ }
},
"reminderSettings": {
"time": "18:00",
"enabled": true,
"timezone": "Europe/Paris"
}
}
- DeviceId: Each device generates a unique, persistent
deviceId
(stored in localStorage) to allow independent management of subscriptions per device. - Why this model?
- Enables true multi-device support: users can subscribe/unsubscribe on any device without affecting others.
- Avoids race conditions and data loss from array-based or flat-list models.
- Scales to any number of devices per user.
1.2. Firestore Write Patterns & Merge
- All updates to
pushSubscriptions
use Firestore’s{ merge: true }
to avoid overwriting other device entries. - Example (pseudo):
await setDoc(doc(db, 'users', userId), {
pushSubscriptions: {
[deviceId]: subscriptionData
}
}, { merge: true })
- This ensures atomic, device-scoped updates and prevents accidental data loss.
2. Real-Time Sync, DeviceId Management & Client Architecture
2.1. DeviceId Generation & Storage
- On first load, each device generates a UUIDv4 and stores it in localStorage as
ClarityBox-device-id
. - This ID is used as the key in
pushSubscriptions
and is never exposed to other users.
2.2. Real-Time Firestore Sync
- The client uses a centralized helper (
subscribeToUserReminderSettings
) that wraps Firestore’sonSnapshot
for real-time updates. - All Firestore access is abstracted via ergonomic helpers (
users-extended.ts
,firestore.ts
), never direct in components. - Example:
unsubscribeRef.current = subscribeToUserReminderSettings(user.uid, (reminderSettings) => {
// ...update local state
})
2.3. Reminder Time Slot Optimization
- The UI offers a grouped select (morning/afternoon/evening) with 15-minute slots, generated programmatically.
- This design:
- Reduces Firestore write frequency (users rarely change reminder time, and only in 15min increments).
- Simplifies backend scheduling logic (all reminders align to 15min boundaries).
- Example (slot generation):
Array.from({ length: 7 * 4 }, (_, i) => {
const h = 5 + Math.floor(i / 4);
const m = (i % 4) * 15;
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
})
3. Backend, Security & API Design
3.1. Secure Firestore Access
- All server-side logic (Next.js API routes) uses Firestore Admin SDK helpers only.
- No sensitive data (push keys, endpoints) is ever exposed to the client or logs.
- Example (sensitive data masked):
await setDoc(doc(db, 'users', userId), {
pushSubscriptions: {
[deviceId]: {
endpoint: 'https://.../send/xxxx',
keys: { p256dh: '***', auth: '***' }
}
}
}, { merge: true })
3.2. Robustness & Error Handling
- All Firestore operations are wrapped in try/catch with detailed logging (server-side only).
- The backend validates all input and enforces device/user isolation.
4. Modern UX, Internationalization & Extensibility
4.1. Modern, Mobile-First UX
- The
NotificationScheduler
component provides a grouped, accessible time selector and clear status feedback. - DeviceId is shown for debugging, and offline/permission states are handled gracefully.
4.2. Full Internationalization
- All UI strings are managed via a
useDictionary
hook and translation files (en.json
,fr.json
). - The system supports dynamic language switching and is ready for further locales.
4.3. Extensibility & Future-Proofing
- The architecture supports:
- Advanced scheduling (multiple reminders, per-device settings)
- Analytics (per-device delivery, engagement tracking)
- Easy migration to other notification providers
This architecture enables ClarityBox to deliver reliable, secure, and scalable multi-device notifications. Centralized data, ergonomic helpers, and a modern UX ensure maintainability and a seamless user experience, ready for future growth.