Skip to main content

Push Notifications Architecture

System Overview

ZÈYA's push notification system is built on Expo Push Notification Service and provides reliable, real-time notifications across iOS and Android platforms. The system handles everything from user registration to delivery tracking and analytics.

Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────┐
│ ZÈYA Push Notification System │
└─────────────────────────────────────────────────────────────────────────┘

┌──────────────────┐ ┌──────────────────┐
│ Mobile Apps │ │ Backend API │
│ (iOS/Android) │ │ (Laravel) │
└────────┬─────────┘ └────────┬─────────┘
│ │
│ 1. Request Push Permissions │
│ ────────────────────────────────────────────────────▶│
│ │
│ 2. Get Expo Push Token │
│ ◀────────────────────────────────────────────────────│
│ (ExponentPushToken[xxx]) │
│ │
│ 3. Register Token with Backend │
│ ────────────────────────────────────────────────────▶│
│ POST /api/user/save-phone-token │
│ { token, device_id, platform } │
│ │
│ │
│ ┌──────────────────┴─────────────────┐
│ │ MySQL Database │
│ │ │
│ │ ┌─────────────────────────────┐ │
│ │ │ mobile_devices │ │
│ │ │ - id │ │
│ │ │ - user_id │ │
│ │ │ - token (Expo token) │ │
│ │ │ - device_id │ │
│ │ │ - platform │ │
│ │ └─────────────────────────────┘ │
│ │ │
│ └────────────────────────────────────┘
│ │
│ │
│ ┌──────────────────┴─────────────────┐
│ │ Event Trigger │
│ │ (Swap Match, Message, etc.) │
│ └──────────────────┬─────────────────┘
│ │
│ ┌──────────────────▼─────────────────┐
│ │ NotificationController │
│ │ ::sendNotification() │
│ │ │
│ │ 1. Check user settings │
│ │ 2. Validate user preferences │
│ └──────────────────┬─────────────────┘
│ │
│ ┌──────────────────▼─────────────────┐
│ │ NotificationService │
│ │ ::send() │
│ │ │
│ │ 1. Fetch user devices │
│ │ 2. Create notification records │
│ │ 3. Prepare Expo messages │
│ └──────────────────┬─────────────────┘
│ │
│ │
│ ┌──────────────────▼─────────────────┐
│ │ Expo Push Service │
│ │ (expo.dev) │
│ │ │
│ │ - Receives push request │
│ │ - Returns ticket ID │
│ │ - Queues for delivery │
│ └──────────────────┬─────────────────┘
│ │
│ │ Ticket Response
│ │
│ ┌──────────────────▼─────────────────┐
│ │ Update Notification Status │
│ │ │
│ │ ┌─────────────────────────────┐ │
│ │ │ notifications │ │
│ │ │ - notification_id │ │
│ │ │ - user_id │ │
│ │ │ - notification_content │ │
│ │ │ - is_send = true │ │
│ │ │ - ticket_id │ │
│ │ │ - is_delivered │ │
│ │ │ - is_open │ │
│ │ └─────────────────────────────┘ │
│ └────────────────────────────────────┘
│ │
│ │
│ ┌──────────────────▼─────────────────┐
│ │ Expo Delivers to Device │
│ │ │
│ │ iOS: APNs │
│ │ Android: FCM │
│ └──────────────────┬─────────────────┘
│ │
│ 4. Receive Push Notification │
│ ◀────────────────────────────────────────────────────│
│ { title, body, data } │
│ │
│ 5. Display Notification │
│ - Show banner/alert │
│ - Play sound │
│ - Update badge count │
│ │
│ │
│ 6. User Taps Notification │
│ - Open app │
│ - Navigate to content (deep link) │
│ │
│ 7. Mark Notification as Read │
│ ────────────────────────────────────────────────────▶│
│ GET /api/notification/{id}/read │
│ │
│ ┌──────────────────▼─────────────────┐
│ │ Update: is_open = true │
│ └────────────────────────────────────┘
│ │
│ │
│ ┌──────────────────▼─────────────────┐
│ │ Background: Fetch Receipts │
│ │ (ProcessExpoReceipts Job) │
│ │ │
│ │ - Query pending notifications │
│ │ - Fetch delivery receipts │
│ │ - Update is_delivered status │
│ └────────────────────────────────────┘
│ │
│ │
│ ┌──────────────────▼─────────────────┐
│ │ Analytics & Monitoring │
│ │ │
│ │ - Delivery rates │
│ │ - Open rates │
│ │ - Error tracking │
│ │ - Performance metrics │
│ └────────────────────────────────────┘
│ │

Bulk Notification Flow

For sending notifications to multiple users (e.g., promotional campaigns):

┌─────────────────────────────────────────────────────────────────────────┐
│ Bulk Notification Processing │
└─────────────────────────────────────────────────────────────────────────┘

┌──────────────────┐
│ Admin Panel │
│ or API Request │
└────────┬─────────┘

│ POST /api/admin/send-notification-all
│ { title, text, data }


┌────────────────────────────────────┐
│ SendBulkNotifications Job │
│ (Coordinator) │
│ │
│ 1. Query active users │
│ WHERE notification_enabled = 1 │
│ │
│ 2. Chunk users into batches (50) │
│ │
│ 3. Dispatch batch jobs │
└────────┬───────────────────────────┘

├───────────┬───────────┬───────────┬───────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Batch │ │ Batch │ │ Batch │ │ Batch │ │ Batch │
│ 1 │ │ 2 │ │ 3 │ │ 4 │ │ N │
│ (50) │ │ (50) │ │ (50) │ │ (50) │ │ (50) │
└───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘
│ │ │ │ │
└───────────┴───────────┴───────────┴───────────┘


┌──────────────────────────┐
│ SendNotificationBatch │
│ Job (Worker) │
│ │
│ 1. Get user devices │
│ 2. Bulk insert records │
│ 3. Send to Expo │
│ 4. Update statuses │
│ │
│ Retry on failure (3x) │
└──────────────────────────┘


┌──────────────────────────┐
│ Queue Workers │
│ │
│ Worker 1 ───┐ │
│ Worker 2 ───┼─▶ Process │
│ Worker 3 ───┘ Parallel│
│ Worker N │
└──────────────────────────┘

Data Flow

1. Token Registration

Mobile App                Backend                 Database
│ │ │
│──── Get Token ────────▶│ │
│ │ │
│◀─── Return Token ──────│ │
│ │ │
│──── Save Token ───────▶│ │
│ (POST /save-token) │ │
│ │ │
│ │──── Insert/Update ────▶│
│ │ mobile_devices │
│ │ │
│ │◀─── Success ───────────│
│ │ │
│◀─── 200 OK ────────────│ │

2. Send Notification

Trigger Event          Controller            Service              Expo               Device
│ │ │ │ │
│──── sendNotification() │ │ │
│ │ │ │ │
│ │──── send() ───────▶│ │ │
│ │ │ │ │
│ │ │──── Create ──────▶│ │
│ │ │ Records │ │
│ │ │ │ │
│ │ │──── Push ────────▶│ │
│ │ │ Message │ │
│ │ │ │ │
│ │ │◀─── Ticket ID ────│ │
│ │ │ │ │
│ │ │──── Update ──────▶│ │
│ │ │ Status │ │
│ │ │ │ │
│ │ │ │──── Deliver ─────▶│
│ │ │ │ │
│ │ │ │ │◀─── Display

3. User Interaction

Device                 Mobile App              Backend              Database
│ │ │ │
│──── Tap ────────────▶│ │ │
│ Notification │ │ │
│ │ │ │
│ │──── Mark as Read ─────▶│ │
│ │ (GET /read) │ │
│ │ │ │
│ │ │──── Update ──────▶│
│ │ │ is_open=true │
│ │ │ │
│ │ │◀─── Success ──────│
│ │ │ │
│ │◀─── 200 OK ────────────│ │
│ │ │ │
│ │──── Navigate to ──────▶│ │
│ │ Content (deep link)│ │

Components Breakdown

Backend Components

ComponentLocationPurpose
NotificationControllerapp/Http/Controllers/NotificationController.phpHandles API endpoints for notifications
NotificationServiceapp/Services/NotificationService.phpCore notification sending logic
SendBulkNotificationsapp/Jobs/SendBulkNotifications.phpCoordinator for bulk sends
SendNotificationBatchapp/Jobs/SendNotificationBatch.phpProcesses notification batches
ProcessExpoReceiptsapp/Jobs/ProcessExpoReceipts.phpFetches delivery receipts
Notification Modelapp/Models/Notification.phpDatabase model
MobileDevice Modelapp/Models/MobileDevice.phpDevice token storage

Mobile App Components

ComponentLocationPurpose
usePushNotificationssrc/share/useNotifications.jsMain notification hook
Permission ManagerBuilt into hookHandles permission requests
Token ManagerBuilt into hookManages Expo push token
Notification HandlerBuilt into hookProcesses incoming notifications
Event Emittersrc/share/emitter.jsCross-component events
Deep Link HandlerNavigation layerHandles notification taps

Database Schema

Entity Relationship Diagram

┌─────────────────────┐
│ users │
│ │
│ - user_id (PK) │
│ - username │
│ - email │
│ - name │
│ - status │
└──────────┬──────────┘
│ 1

│ N
┌──────────▼──────────┐ ┌─────────────────────┐
│ mobile_devices │ │ user_settings │
│ │ │ │
│ - id (PK) │ │ - id (PK) │
│ - user_id (FK) │ │ - user_id (FK) │
│ - token │ │ - setting_name │
│ - device_id │ │ - setting_value │
│ - platform │ └─────────────────────┘
└──────────┬──────────┘
│ 1

│ N
┌──────────▼──────────────────────────┐
│ notifications │
│ │
│ - notification_id (PK) │
│ - user_id (FK) │
│ - mobile_devices_id (FK) │
│ - notification_content (JSON) │
│ - notification_type │
│ - is_send (boolean) │
│ - is_delivered (boolean) │
│ - is_open (boolean) │
│ - ticket_id (Expo ticket) │
│ - error_message │
│ - created_at │
│ - updated_at │
│ - deleted_at │
└─────────────────────────────────────┘

Notification Lifecycle States

┌────────────────────────────────────────────────────────────┐
│ Notification State Machine │
└────────────────────────────────────────────────────────────┘

[Created]

│ API receives request
│ Record inserted in DB


[Queued]

│ Job picks up notification
│ Prepares Expo message


[Sent] ──────────────────┐
│ │
│ is_send = true │ is_send = false
│ ticket_id set │ error_message set
│ │
▼ ▼
[Pending Receipt] [Failed]
│ │
│ Waiting for │ Retry logic
│ delivery │ (max 3 attempts)
│ │
▼ └──────▶ [Permanently Failed]
[Delivered] ─────────────┐
│ │
│ is_delivered=true │ is_delivered=false
│ │ error_message set
│ │
▼ ▼
[Shown to User] [Delivery Failed]

│ User sees notification
│ on device

├────────────┬──────────┐
│ │ │
▼ ▼ ▼
[Dismissed] [Expired] [Opened]

│ User taps notification
│ App opens
│ Mark as read API called


[Read]

│ is_open = true
│ User navigated to content


[Completed]

Performance Characteristics

Throughput

  • Single notification: ~50-100ms
  • Batch (50 users): ~2-5 seconds
  • Bulk (10,000 users): ~3-5 minutes (with 5 queue workers)

Scalability

  • Queue-based architecture supports horizontal scaling
  • Can handle 100,000+ notifications per hour
  • Add more queue workers for increased throughput
  • Database indexes optimize query performance

Reliability

  • 3 retry attempts per failed notification
  • Exponential backoff (60s between retries)
  • Delivery receipt tracking
  • Error logging and monitoring

Integration Points

External Services

  1. Expo Push Service (expo.dev)

    • Sends notifications to devices
    • Returns ticket IDs for tracking
    • Provides delivery receipts
  2. Apple Push Notification Service (APNs)

    • Used by Expo for iOS devices
    • Handles iOS notification delivery
  3. Firebase Cloud Messaging (FCM)

    • Used by Expo for Android devices
    • Handles Android notification delivery
  4. Firebase Realtime Database

    • Syncs badge counts
    • Real-time notification data
    • Chat notification tracking

Internal Services

  1. Queue System (Laravel Queue)

    • Redis or Database driver
    • Job processing and retries
    • Background task management
  2. MySQL Database

    • Notification records
    • Device tokens
    • User preferences
    • Analytics data
  3. Event System

    • Triggers notifications on events
    • Swap matches, messages, etc.
    • Decoupled architecture

Security Model

Authentication & Authorization

┌─────────────────────────────────────────────┐
│ Security Layers │
└─────────────────────────────────────────────┘

1. API Authentication
├─ JWT Bearer Token
├─ Token expiration
└─ User identity verification

2. Device Token Security
├─ One-way storage (encrypted at rest)
├─ User-device association
└─ Token rotation on re-registration

3. Notification Authorization
├─ User ownership verification
├─ Notification preference checks
└─ Admin-only endpoints protected

4. Data Privacy
├─ Minimal PII in notification payload
├─ Deep links for detailed data
└─ User consent for notifications

5. Rate Limiting
├─ Per-user rate limits
├─ Per-IP rate limits
└─ Admin endpoint throttling

Monitoring & Observability

Key Metrics

  1. Delivery Metrics

    • Sent rate
    • Delivery rate
    • Open rate
    • Failure rate
  2. Performance Metrics

    • Processing time
    • Queue length
    • Worker utilization
    • API response time
  3. Error Metrics

    • Failed sends
    • Invalid tokens
    • Network errors
    • Timeout errors

Logging

[2026-01-20 10:30:45] INFO: Notification sent
{
"user_id": 123,
"notification_id": 456,
"ticket_id": "xxx-xxx",
"device_id": 789
}

[2026-01-20 10:30:50] WARNING: Notification delivery failed
{
"user_id": 123,
"notification_id": 456,
"error": "DeviceNotRegistered",
"details": "Token is no longer valid"
}

[2026-01-20 10:35:00] INFO: Bulk notification completed
{
"total_users": 10000,
"total_batches": 200,
"batch_size": 50,
"duration_seconds": 180
}