Files
RSSuper/tasks/native-business-logic-migration/01-analyze-current-architecture.md
Michael Freno 5f4d5a52e8 docs: Document current Expo architecture for native migration
- Document technology stack (Expo, TypeScript, Zustand, SQLite)
- Map out all data models and types
- Document service dependencies and architecture
- Create component dependency diagrams
- Document data flow for feed sync and search
- Provide migration checklist with priorities
- Identify pure TypeScript vs native-dependent services

This analysis provides the foundation for migrating business logic
from Expo/TypeScript to native platforms (iOS, Android, Linux).
2026-03-29 11:43:46 -04:00

22 KiB

01. Analyze and document current Expo architecture

meta: id: native-business-logic-migration-01 feature: native-business-logic-migration priority: P0 depends_on: [] tags: [analysis, documentation]

objective:

  • Document the current Expo/TypeScript architecture to understand what needs to be migrated

deliverables:

  • Architecture documentation file
  • Component dependency diagram
  • Data flow diagrams
  • List of all services, stores, and utilities to migrate

steps:

  • Review all TypeScript source files in src/
  • Identify all data models and types
  • Map out service dependencies
  • Document state management flow (Zustand stores)
  • Identify database schema and queries
  • Document API endpoints and network calls
  • Create migration checklist

acceptance_criteria:

  • Architecture document exists with all components listed
  • Data flow diagrams created for key features
  • Migration checklist complete with all items identified
  • All services and their dependencies documented

notes:

  • Focus on business logic, not UI components
  • Pay attention to async operations and error handling
  • Document any platform-specific code that needs special handling

Current Architecture Analysis

Overview

RSSuper is a multi-platform RSS reader built with Expo (React Native) using TypeScript. The current architecture follows a service-oriented pattern with Zustand for state management and expo-sqlite for local persistence.

Technology Stack

Layer Technology
Framework Expo SDK 55 (canary)
Runtime React Native 0.83.4
Language TypeScript 5.9
State Management Zustand 5.x
Data Fetching TanStack Query 5.x
HTTP Client Axios
Local Database expo-sqlite
Navigation React Navigation 7.x
XML Parsing fast-xml-parser
Audio expo-av

Data Models

Core Types (src/types/feed.ts)

FeedItem
├── id: string
├── title: string
├── link?: string
├── description?: string
├── content?: string
├── author?: string
├── published?: Date
├── updated?: Date
├── categories?: string[]
├── enclosure?: { url, type, length? }
└── guid?: string

Feed
├── id: string
├── title: string
├── link?: string
├── description?: string
├── subtitle?: string
├── language?: string
├── lastBuildDate?: Date
├── updated?: Date
├── generator?: string
├── ttl?: number
├── items: FeedItem[]
├── rawUrl: string
├── lastFetchedAt?: Date
└── nextFetchAt?: Date

FeedSubscription
├── id: string
├── url: string
├── title: string
├── category?: string
├── enabled: boolean
├── fetchInterval: number (minutes)
├── createdAt: Date
├── updatedAt: Date
├── lastFetchedAt?: Date
├── nextFetchAt?: Date
├── error?: string
└── httpAuth?: { username, password }

SearchQuery / SearchFilters / SearchResult

Global Types (src/types/global.d.ts)

NotificationType (enum)
├── NEW_ARTICLE
├── EPISODE_RELEASE
├── CUSTOM_ALERT
└── UPGRADE_PROMO

NotificationConfig
AccountSettings
PushNotificationConfig

Services Architecture

1. Feed Service (src/services/feed-service.ts)

Responsibilities: RSS/Atom feed parsing and fetching

Functions:
├── parseFeed(url, data) → ParseResult
├── fetchFeed(url, auth?) → ParseResult
└── fetchFeeds(subscriptions) → Map<string, ParseResult>

Dependencies:
├── axios (HTTP)
└── xml2js (XML parsing)

Platform: Pure TypeScript - NO native dependencies

2. Database Service (src/services/database.ts)

Responsibilities: SQLite persistence layer

Tables:
├── subscriptions
│   ├── id (PRIMARY KEY)
│   ├── url (UNIQUE)
│   ├── title, category
│   ├── enabled, fetch_interval
│   ├── http_auth_username, http_auth_password
│   ├── timestamps (created_at, updated_at, last_fetched_at, next_fetch_at)
│   └── error
│
├── feeds
│   ├── id (PRIMARY KEY)
│   ├── subscription_id (FOREIGN KEY)
│   ├── title, link, description, author
│   ├── published, updated, content
│   ├── guid (UNIQUE)
│   └── created_at
│
├── feeds_fts (FTS5 virtual table)
├── subscriptions_fts (FTS5 virtual table)
└── search_history

Functions:
├── initDatabase() → void
├── saveSubscription(sub) → void
├── getSubscription(id) → FeedSubscription
├── getAllSubscriptions() → FeedSubscription[]
├── deleteSubscription(id) → void
├── saveFeedItems(subscriptionId, items) → void
├── getFeedItems(subscriptionId?, limit, offset) → FeedItem[]
├── getAllFeedItems(limit, offset) → FeedItem[]
└── search history functions

Dependencies: expo-sqlite

3. Search Service (src/services/search-service.ts)

Responsibilities: Full-text search using SQLite FTS5

Functions:
├── searchArticles(query, options) → SearchResult[]
├── searchFeeds(query, limit) → SearchResult[]
└── combinedSearch(query, options) → { articles, feeds }

Search Features:
├── FTS5 full-text search (primary)
├── LIKE fallback search
├── Date filters
├── Feed filters
├── Author filters
├── Content type filters
└── Multiple sort options

Dependencies: database service

4. Sync Service (src/services/sync-service.ts)

Responsibilities: Feed synchronization orchestration

Functions:
├── syncFeed(subscription) → { success, itemsSynced }
├── syncAllFeeds() → { success, totalItemsSynced }
├── getFeedsDueForSync() → FeedSubscription[]
├── getLocalFeedItems(...) → FeedItem[]
├── getAllLocalFeedItems(limit) → FeedItem[]
├── hasLocalData() → boolean
└── resolveConflict(options) → FeedItem

Conflict Resolution Strategies:
├── newer
├── older
├── local
└── remote

Dependencies: feed-service, database, feed-store

5. Background Sync Service (src/services/background-sync.ts)

Responsibilities: Background task management

Task: BACKGROUND_SYNC_TASK
├── Uses expo-task-manager
├── Registers background fetch task
└── Syncs all enabled feeds

Functions:
├── registerBackgroundSync() → boolean
├── unregisterBackgroundSync() → void
└── isBackgroundSyncRegistered() → boolean

Dependencies: expo-task-manager, sync-service, feed-store

6. Notification Service (src/services/notification-service.ts)

Responsibilities: Push and local notifications

Functions:
├── registerNotificationChannels() → void (Android)
├── requestNotificationPermissions() → boolean
├── getPermissionStatus() → status
├── scheduleNotification(config) → string | null
├── scheduleNotificationAtTime(config, date) → string | null
├── cancelNotification(id) → void
├── cancelAllNotifications() → void
├── getScheduledNotifications() → NotificationRequest[]
├── showNotification(config) → void
├── setBadgeCount(count) → void
├── getBadgeCount() → number
├── addNotificationListener(callback) → unsubscribe
└── addNotificationResponseListener(callback) → unsubscribe

Android Channels:
├── default
├── new-articles
├── episode-releases
└── custom-alerts

Dependencies: expo-notifications, settings-store

7. Audio Player Service (src/services/audio-player.ts)

Responsibilities: Podcast/audio playback

Class: AudioPlayerService
├── initialize() → void
├── subscribe(callback) → unsubscribe
├── play(item) → void
├── pause() → void
├── resume() → void
├── stop() → void
├── seekTo(positionMs) → void
├── setPlaybackSpeed(speed) → void
├── skipForward(seconds) → void
├── skipBackward(seconds) → void
└── getState() → PlayerState

Playback States: idle | loading | playing | paused | error
Playback Speeds: 0.5 | 0.75 | 1 | 1.25 | 1.5 | 1.75 | 2

Dependencies: expo-av

8. API Service (src/services/api.ts)

Responsibilities: HTTP client setup (currently minimal)

Setup:
├── Axios instance with baseURL
├── Request interceptor (auth token placeholder)
└── Response interceptor (error handling)

Note: Not actively used - TODO for future cloud sync

State Management (Zustand Stores)

1. Feed Store (src/stores/feed-store.ts)

State:
├── subscriptions: FeedSubscription[]
├── feedItems: FeedItem[]
├── loading: boolean
└── error: string | null

Actions:
├── addSubscription(subscription) → Promise<void>
├── updateSubscription(id, updates) → void
├── removeSubscription(id) → Promise<void>
├── loadSubscriptions() → Promise<void>
├── loadFeedItems(subscriptionId?, limit?, offset?) → Promise<void>
├── setLoading(loading) → void
├── setError(error) → void
└── clearError() → void

2. Settings Store (src/stores/settings-store.ts)

State:
├── syncInterval: SyncInterval
├── theme: 'system' | 'light' | 'dark'
├── notificationPreferences: NotificationPreferences
├── readingPreferences: ReadingPreferences
└── accountSettings: AccountSettings

Actions:
├── setSyncInterval(interval) → void
├── setTheme(theme) → void
├── setNotificationPreferences(prefs) → void
├── setReadingPreferences(prefs) → void
├── setAccountSettings(settings) → void
└── resetSettings() → void

Persistence: AsyncStorage (via zustand persist)
Storage Key: rssuper-settings

3. Bookmark Store (src/stores/bookmark-store.ts)

State:
└── bookmarkedIds: Set<string>

Actions:
├── addBookmark(articleId) → void
├── removeBookmark(articleId) → void
├── toggleBookmark(articleId) → void
├── isBookmarked(articleId) → boolean
└── getBookmarks() → string[]

Persistence: AsyncStorage (via zustand persist)
Storage Key: rssuper-bookmarks

4. Search Store (src/stores/search-store.ts)

State:
├── query: string
├── filters: SearchFilters
├── sort: SearchSortOption
├── page: number
├── pageSize: number
├── loading: boolean
├── error: string | null
└── searchHistory: SearchHistoryItem[]

Actions:
├── setQuery(query) → void
├── setFilters(filters) → void
├── setSort(sort) → void
├── setPage(page) → void
├── setLoading(loading) → void
├── setError(error) → void
├── clearSearch() → void
├── addToHistory(query) → Promise<void>
├── loadHistory() → Promise<void>
├── clearHistory() → Promise<void>
├── removeFromHistory(query) → Promise<void>
└── resetSearch() → void

Component Dependencies

┌─────────────────────────────────────────────────────────────────────────┐
│                           UI Components                                  │
│  (feed-item-card, article-card, search-filters, etc.)                  │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         React Hooks                                      │
│  use-feed-list, use-theme, use-color-scheme, use-offline               │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                        Zustand Stores                                    │
│  feed-store, settings-store, bookmark-store, search-store              │
└─────────────────────────────────────────────────────────────────────────┘
            │                   │                   │                   │
            ▼                   ▼                   ▼                   ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                          Services Layer                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌───────────┐ │
│  │feed-service  │  │search-service│  │ sync-service │  │  api.ts   │ │
│  └──────────────┘  └──────────────┘  └──────────────┘  └───────────┘ │
│  ┌──────────────────────┐  ┌──────────────────────┐                    │
│  │background-sync.ts    │  │notification-service  │                    │
│  └──────────────────────┘  └──────────────────────┘                    │
└─────────────────────────────────────────────────────────────────────────┘
            │                   │
            ▼                   ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                      Data Layer                                          │
│  ┌──────────────────────────────────────────────────────────────────┐   │
│  │                    database.ts (expo-sqlite)                      │   │
│  │  - subscriptions table                                            │   │
│  │  - feeds table                                                    │   │
│  │  - FTS5 virtual tables                                            │   │
│  │  - search_history table                                           │   │
│  └──────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                    Expo Modules (Native)                                 │
│  expo-sqlite, expo-task-manager, expo-notifications, expo-av           │
└─────────────────────────────────────────────────────────────────────────┘

Data Flow Diagrams

Feed Sync Flow

User adds subscription
        │
        ▼
┌───────────────────┐
│ feed-store        │
│ addSubscription() │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐     ┌───────────────────┐
│ database.ts       │     │ settings-store    │
│ saveSubscription()│     │ (check intervals) │
└─────────┬─────────┘     └───────────────────┘
          │
          ▼ (manual or background)
┌───────────────────┐
│ sync-service      │
│ syncFeed()        │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐     ┌───────────────────┐
│ feed-service      │     │ feed-store        │
│ fetchFeed()       │     │ updateSubscription│
│ parseFeed()       │     └─────────┬─────────┘
└─────────┬─────────┘               │
          │                         ▼
          │               ┌───────────────────┐
          │               │ database.ts       │
          │               │ saveFeedItems()   │
          │               └─────────┬─────────┘
          │                         │
          ▼                         ▼
┌───────────────────┐     ┌───────────────────┐
│ axios.get()      │     │ UI updates        │
│ (HTTP request)   │     │ via Zustand       │
└───────────────────┘     └───────────────────┘

Search Flow

User enters search query
        │
        ▼
┌───────────────────┐
│ search-store      │
│ setQuery()        │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ search-service    │
│ searchArticles()  │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐     ┌───────────────────┐
│ database.ts       │     │ FTS5 or LIKE      │
│ getDb()           │────▶│ fallback search   │
└─────────┬─────────┘     └───────────────────┘
          │
          ▼
┌───────────────────┐
│ SearchResult[]    │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ UI renders        │
│ search-results    │
└───────────────────┘

Migration Checklist

Services to Migrate (Pure TypeScript)

Service File Native Deps Priority
Feed Service feed-service.ts None P0
Search Service search-service.ts None P0
Sync Service sync-service.ts None P0
API Service api.ts None P1

Services with Native Dependencies

Service File Native Deps Priority
Database database.ts expo-sqlite P0
Background Sync background-sync.ts expo-task-manager P0
Notifications notification-service.ts expo-notifications P0
Audio Player audio-player.ts expo-av P1

Stores to Migrate

Store File Persistence Priority
Feed Store feed-store.ts None P0
Settings Store settings-store.ts AsyncStorage P0
Bookmark Store bookmark-store.ts AsyncStorage P0
Search Store search-store.ts None P0

Types to Migrate

Types File Priority
Feed Types types/feed.ts P0
Global Types types/global.d.ts P0

Key Findings

  1. Pure TypeScript Services (can be shared across all platforms):

    • Feed parsing (RSS/Atom)
    • Search logic
    • Sync orchestration
    • Conflict resolution
  2. Native Module Dependencies:

    • expo-sqlite → SQLite database
    • expo-task-manager → Background tasks
    • expo-notifications → Push/local notifications
    • expo-av → Audio playback
  3. State Management:

    • All stores use Zustand
    • Settings and bookmarks persist to AsyncStorage
    • Database serves as source of truth for feeds/subscriptions
  4. Error Handling:

    • Most services return { success: boolean; error?: string }
    • Try-catch blocks with meaningful error messages
    • Fallback strategies (e.g., FTS → LIKE search)
  5. Platform-Specific Code:

    • hooks/use-color-scheme.web.ts - Web-specific color scheme
    • components/animated-icon.web.tsx - Web-specific icon animation
    • components/app-tabs.web.tsx - Web-specific tabs

Migration Priorities

  1. Phase 1: Data models and types (shared)
  2. Phase 2: Pure TypeScript services (feed, search, sync)
  3. Phase 3: Database layer with expo-sqlite
  4. Phase 4: Background sync with expo-task-manager
  5. Phase 5: Notifications with expo-notifications
  6. Phase 6: Audio player with expo-av
  7. Phase 7: State management integration