Design shared data models for native platforms
- Define core entities: FeedItem, FeedSubscription, SearchHistoryItem - Document entity relationships with diagram - Create database schema with SQLite and FTS5 - Provide type mapping guide for Swift/Kotlin/C - Define value types vs reference types - Document enumeration types for all platforms - Specify serialization/deserialization requirements - Outline indexing strategy for performance - Include memory considerations and future extensibility
This commit is contained in:
@@ -0,0 +1,506 @@
|
|||||||
|
# 02. Design shared data models for all platforms
|
||||||
|
|
||||||
|
meta:
|
||||||
|
id: native-business-logic-migration-02
|
||||||
|
feature: native-business-logic-migration
|
||||||
|
priority: P0
|
||||||
|
depends_on: [native-business-logic-migration-01]
|
||||||
|
tags: [design, data-models]
|
||||||
|
|
||||||
|
objective:
|
||||||
|
- Design platform-agnostic data models that can be implemented natively on each platform
|
||||||
|
|
||||||
|
deliverables:
|
||||||
|
- Data model specification document
|
||||||
|
- Entity relationship diagram
|
||||||
|
- Type mapping guide (TypeScript → Swift/Kotlin/C)
|
||||||
|
- Database schema design
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- Review TypeScript types from src/types/feed.ts
|
||||||
|
- Identify core entities: Feed, FeedItem, FeedSubscription, SearchResult
|
||||||
|
- Design normalized database schema
|
||||||
|
- Create type mapping documentation
|
||||||
|
- Define value types vs reference types
|
||||||
|
- Design enumeration types for each platform
|
||||||
|
- Document serialization/deserialization requirements
|
||||||
|
|
||||||
|
acceptance_criteria:
|
||||||
|
- All data models documented with properties and types
|
||||||
|
- Entity relationships clearly defined
|
||||||
|
- Type mapping guide complete for all three platforms
|
||||||
|
- Database schema designed with proper indexing
|
||||||
|
- Value types identified (Date, UUID, Enums)
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- Use UUID for identifiers across all platforms
|
||||||
|
- Date handling needs platform-specific implementation
|
||||||
|
- Consider memory footprint for large feed items
|
||||||
|
- Plan for future extensibility
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Shared Data Models Design
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document defines platform-agnostic data models for RSSuper, enabling native implementations on iOS (Swift), Android (Kotlin), and Linux (C). The models are designed for SQLite persistence with full-text search support.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Core Entities
|
||||||
|
|
||||||
|
### 1.1 FeedItem
|
||||||
|
|
||||||
|
Represents a single article or episode from a feed.
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
|----------|------|----------|-------------|
|
||||||
|
| id | UUID | Yes | Unique identifier |
|
||||||
|
| title | String | Yes | Item title |
|
||||||
|
| link | String | No | URL to full article |
|
||||||
|
| description | String | No | Short summary |
|
||||||
|
| content | String | No | Full content (may be large) |
|
||||||
|
| author | String | No | Author name |
|
||||||
|
| published | DateTime | No | Publication date |
|
||||||
|
| updated | DateTime | No | Last update date |
|
||||||
|
| categories | String[] | No | Category tags |
|
||||||
|
| enclosure | Enclosure | No | Media attachment |
|
||||||
|
| guid | String | No | Global unique identifier from feed |
|
||||||
|
| subscriptionId | UUID | Yes | Parent subscription |
|
||||||
|
| subscriptionTitle | String | No | Denormalized for display |
|
||||||
|
|
||||||
|
#### Enclosure (nested)
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
|----------|------|----------|-------------|
|
||||||
|
| url | String | Yes | Media URL |
|
||||||
|
| type | String | Yes | MIME type (audio/mpeg, image/jpeg, etc.) |
|
||||||
|
| length | Int64 | No | File size in bytes |
|
||||||
|
|
||||||
|
### 1.2 FeedSubscription
|
||||||
|
|
||||||
|
Represents a feed subscription with configuration.
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
|----------|------|----------|-------------|
|
||||||
|
| id | UUID | Yes | Unique identifier |
|
||||||
|
| url | String | Yes | Feed URL (unique) |
|
||||||
|
| title | String | Yes | Display title |
|
||||||
|
| category | String | No | User-defined category |
|
||||||
|
| enabled | Boolean | Yes | Active/inactive |
|
||||||
|
| fetchInterval | Int32 | Yes | Fetch interval in minutes |
|
||||||
|
| createdAt | DateTime | Yes | Creation timestamp |
|
||||||
|
| updatedAt | DateTime | Yes | Last modification |
|
||||||
|
| lastFetchedAt | DateTime | No | Last successful fetch |
|
||||||
|
| nextFetchAt | DateTime | No | Next scheduled fetch |
|
||||||
|
| error | String | No | Last error message |
|
||||||
|
| httpAuth | HttpAuth | No | HTTP authentication |
|
||||||
|
|
||||||
|
#### HttpAuth (nested)
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
|----------|------|----------|-------------|
|
||||||
|
| username | String | Yes | Username |
|
||||||
|
| password | String | Yes | Password (should be encrypted) |
|
||||||
|
|
||||||
|
### 1.3 SearchHistoryItem
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
|----------|------|----------|-------------|
|
||||||
|
| id | UUID | Yes | Unique identifier |
|
||||||
|
| query | String | Yes | Search query text |
|
||||||
|
| timestamp | DateTime | Yes | When query was executed |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Entity Relationship Diagram
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────┐ ┌─────────────────────┐
|
||||||
|
│ FeedSubscription │ │ SearchHistory │
|
||||||
|
├─────────────────────┤ ├─────────────────────┤
|
||||||
|
│ id (PK) │ │ id (PK) │
|
||||||
|
│ url (UNIQUE) │ │ query │
|
||||||
|
│ title │ │ timestamp │
|
||||||
|
│ category │ └─────────────────────┘
|
||||||
|
│ enabled │
|
||||||
|
│ fetch_interval │ ┌─────────────────────┐
|
||||||
|
│ created_at │ │ FeedItem │
|
||||||
|
│ updated_at │ ├─────────────────────┤
|
||||||
|
│ last_fetched_at │ │ id (PK) │
|
||||||
|
│ next_fetch_at │──────┐ │ subscription_id(FK)│◄──┐
|
||||||
|
│ error │ │ │ title │ │
|
||||||
|
│ http_auth_username │ │ │ link │ │
|
||||||
|
│ http_auth_password │ │ │ description │ │
|
||||||
|
└─────────────────────┘ │ │ content │ │
|
||||||
|
│ │ author │ │
|
||||||
|
│ │ published │ │
|
||||||
|
│ │ updated │ │
|
||||||
|
│ │ guid (UNIQUE) │ │
|
||||||
|
│ │ enclosure_url │ │
|
||||||
|
│ │ enclosure_type │ │
|
||||||
|
│ │ enclosure_length │ │
|
||||||
|
│ │ categories (JSON) │ │
|
||||||
|
│ │ created_at │ │
|
||||||
|
│ └─────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Database Schema Design
|
||||||
|
|
||||||
|
### 3.1 subscriptions
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE subscriptions (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
url TEXT UNIQUE NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
category TEXT,
|
||||||
|
enabled INTEGER NOT NULL DEFAULT 1,
|
||||||
|
fetch_interval INTEGER NOT NULL DEFAULT 60,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
updated_at INTEGER NOT NULL,
|
||||||
|
last_fetched_at INTEGER,
|
||||||
|
next_fetch_at INTEGER,
|
||||||
|
error TEXT,
|
||||||
|
http_auth_username TEXT,
|
||||||
|
http_auth_password TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_subscriptions_url ON subscriptions(url);
|
||||||
|
CREATE INDEX idx_subscriptions_category ON subscriptions(category);
|
||||||
|
CREATE INDEX idx_subscriptions_next_fetch ON subscriptions(next_fetch_at) WHERE enabled = 1;
|
||||||
|
CREATE INDEX idx_subscriptions_enabled ON subscriptions(enabled);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 feed_items
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE feed_items (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
subscription_id TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
link TEXT,
|
||||||
|
description TEXT,
|
||||||
|
content TEXT,
|
||||||
|
author TEXT,
|
||||||
|
published INTEGER,
|
||||||
|
updated INTEGER,
|
||||||
|
guid TEXT,
|
||||||
|
enclosure_url TEXT,
|
||||||
|
enclosure_type TEXT,
|
||||||
|
enclosure_length INTEGER,
|
||||||
|
categories TEXT,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX idx_feed_items_guid ON feed_items(guid) WHERE guid IS NOT NULL;
|
||||||
|
CREATE INDEX idx_feed_items_subscription ON feed_items(subscription_id);
|
||||||
|
CREATE INDEX idx_feed_items_published ON feed_items(published DESC);
|
||||||
|
CREATE INDEX idx_feed_items_created ON feed_items(created_at DESC);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 search_history
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE search_history (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
query TEXT NOT NULL,
|
||||||
|
timestamp INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_search_history_timestamp ON search_history(timestamp DESC);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 Full-Text Search (FTS5)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE VIRTUAL TABLE feed_items_fts USING fts5(
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
content,
|
||||||
|
author,
|
||||||
|
categories,
|
||||||
|
content='feed_items',
|
||||||
|
content_rowid='rowid'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIRTUAL TABLE subscriptions_fts USING fts5(
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
category,
|
||||||
|
content='subscriptions',
|
||||||
|
content_rowid='rowid'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Type Mapping Guide
|
||||||
|
|
||||||
|
### 4.1 Primitive Types
|
||||||
|
|
||||||
|
| TypeScript | Swift (Kotlin/C) | SQLite | Notes |
|
||||||
|
|------------|------------------|--------|-------|
|
||||||
|
| `string` | `String` (`String`/`char*`) | TEXT | UTF-8 encoded |
|
||||||
|
| `number` | `Int`/`Double` (`Int`/`Double`/`int64_t`) | INTEGER/REAL | Use REAL for decimals |
|
||||||
|
| `boolean` | `Bool` (`Boolean`/`int`) | INTEGER | 0 or 1 |
|
||||||
|
| `Date` | `Date` (`LocalDateTime`/`time_t`) | INTEGER | Unix timestamp (milliseconds) |
|
||||||
|
| `UUID` | `UUID` (`UUID`/`uuid_t`) | TEXT | String format: "550e8400-e29b-41d4-a716-446655440000" |
|
||||||
|
| `string[]` | `[String]` (`List<String>`/`char**`) | TEXT | JSON array |
|
||||||
|
|
||||||
|
### 4.2 Core Model Mapping
|
||||||
|
|
||||||
|
#### FeedItem
|
||||||
|
|
||||||
|
| Property | TypeScript | Swift | Kotlin | C (struct) |
|
||||||
|
|----------|------------|-------|--------|------------|
|
||||||
|
| id | `string` | `String` | `String` | `char id[37]` |
|
||||||
|
| title | `string` | `String` | `String` | `char title[512]` |
|
||||||
|
| link | `string?` | `String?` | `String?` | `char* link` |
|
||||||
|
| description | `string?` | `String?` | `String?` | `char* description` |
|
||||||
|
| content | `string?` | `String?` | `String?` | `char* content` |
|
||||||
|
| author | `string?` | `String?` | `String?` | `char* author` |
|
||||||
|
| published | `Date?` | `Date?` | `Long?` | `int64_t published` |
|
||||||
|
| updated | `Date?` | `Date?` | `Long?` | `int64_t updated` |
|
||||||
|
| categories | `string[]?` | `[String]?` | `String?` | `char* categories` |
|
||||||
|
| enclosure | `Enclosure?` | `Enclosure?` | `Enclosure?` | `Enclosure* enclosure` |
|
||||||
|
| guid | `string?` | `String?` | `String?` | `char* guid` |
|
||||||
|
| subscriptionId | `string` | `String` | `String` | `char subscription_id[37]` |
|
||||||
|
|
||||||
|
#### FeedSubscription
|
||||||
|
|
||||||
|
| Property | TypeScript | Swift | Kotlin | C (struct) |
|
||||||
|
|----------|------------|-------|--------|------------|
|
||||||
|
| id | `string` | `String` | `String` | `char id[37]` |
|
||||||
|
| url | `string` | `String` | `String` | `char url[2048]` |
|
||||||
|
| title | `string` | `String` | `String` | `char title[512]` |
|
||||||
|
| category | `string?` | `String?` | `String?` | `char* category` |
|
||||||
|
| enabled | `boolean` | `Bool` | `Boolean` | `int enabled` |
|
||||||
|
| fetchInterval | `number` | `Int` | `Int` | `int fetch_interval` |
|
||||||
|
| createdAt | `Date` | `Date` | `Long` | `int64_t created_at` |
|
||||||
|
| updatedAt | `Date` | `Date` | `Long` | `int64_t updated_at` |
|
||||||
|
| lastFetchedAt | `Date?` | `Date?` | `Long?` | `int64_t last_fetched_at` |
|
||||||
|
| nextFetchAt | `Date?` | `Date?` | `Long?` | `int64_t next_fetch_at` |
|
||||||
|
| error | `string?` | `String?` | `String?` | `char* error` |
|
||||||
|
| httpAuth | `HttpAuth?` | `HttpAuth?` | `HttpAuth?` | `HttpAuth* http_auth` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Value Types vs Reference Types
|
||||||
|
|
||||||
|
### 5.1 Value Types (Copy on assign)
|
||||||
|
|
||||||
|
All primitive types should be treated as **value types**:
|
||||||
|
- UUID (string representation)
|
||||||
|
- Date (Unix timestamp)
|
||||||
|
- Boolean
|
||||||
|
- Integer
|
||||||
|
- Float
|
||||||
|
|
||||||
|
### 5.2 Reference Types (Pointer/Heap)
|
||||||
|
|
||||||
|
Complex types should be **reference types**:
|
||||||
|
- FeedItem (stored by ID reference)
|
||||||
|
- FeedSubscription (stored by ID reference)
|
||||||
|
- Enclosure (inline in FeedItem)
|
||||||
|
- HttpAuth (inline in FeedSubscription)
|
||||||
|
|
||||||
|
### 5.3 Implementation Notes
|
||||||
|
|
||||||
|
**Swift**: Use `struct` for value types, `class` for reference types
|
||||||
|
**Kotlin**: Use `data class` for all models, pass by value
|
||||||
|
**C**: Use struct with explicit memory management
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Enumeration Types
|
||||||
|
|
||||||
|
### 6.1 ContentType
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type ContentType = 'article' | 'audio' | 'video';
|
||||||
|
```
|
||||||
|
|
||||||
|
| TypeScript | Swift | Kotlin | C |
|
||||||
|
|------------|-------|--------|---|
|
||||||
|
| `'article'` | `ContentType.article` | `ContentType.ARTICLE` | `CONTENT_TYPE_ARTICLE` |
|
||||||
|
| `'audio'` | `ContentType.audio` | `ContentType.AUDIO` | `CONTENT_TYPE_AUDIO` |
|
||||||
|
| `'video'` | `ContentType.video` | `ContentType.VIDEO` | `CONTENT_TYPE_VIDEO` |
|
||||||
|
|
||||||
|
### 6.2 Theme
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type Theme = 'system' | 'light' | 'dark';
|
||||||
|
```
|
||||||
|
|
||||||
|
| TypeScript | Swift | Kotlin | C |
|
||||||
|
|------------|-------|--------|---|
|
||||||
|
| `'system'` | `Theme.system` | `Theme.SYSTEM` | `THEME_SYSTEM` |
|
||||||
|
| `'light'` | `Theme.light` | `Theme.LIGHT` | `THEME_LIGHT` |
|
||||||
|
| `'dark'` | `Theme.dark` | `Theme.DARK` | `THEME_DARK` |
|
||||||
|
|
||||||
|
### 6.3 Privacy
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type Privacy = 'public' | 'private' | 'anonymous';
|
||||||
|
```
|
||||||
|
|
||||||
|
| TypeScript | Swift | Kotlin | C |
|
||||||
|
|------------|-------|--------|---|
|
||||||
|
| `'public'` | `Privacy.public` | `Privacy.PUBLIC` | `PRIVACY_PUBLIC` |
|
||||||
|
| `'private'` | `Privacy.private` | `Privacy.PRIVATE` | `PRIVACY_PRIVATE` |
|
||||||
|
| `'anonymous'` | `Privacy.anonymous` | `Privacy.ANONYMOUS` | `PRIVACY_ANONYMOUS` |
|
||||||
|
|
||||||
|
### 6.4 NotificationType
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
enum NotificationType {
|
||||||
|
NEW_ARTICLE = 'NEW_ARTICLE',
|
||||||
|
EPISODE_RELEASE = 'EPISODE_RELEASE',
|
||||||
|
CUSTOM_ALERT = 'CUSTOM_ALERT',
|
||||||
|
UPGRADE_PROMO = 'UPGRADE_PROMO'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| TypeScript | Swift | Kotlin | C |
|
||||||
|
|------------|-------|--------|---|
|
||||||
|
| `NEW_ARTICLE` | `NotificationType.newArticle` | `NotificationType.NEW_ARTICLE` | `NOTIFICATION_TYPE_NEW_ARTICLE` |
|
||||||
|
| `EPISODE_RELEASE` | `NotificationType.episodeRelease` | `NotificationType.EPISODE_RELEASE` | `NOTIFICATION_TYPE_EPISODE_RELEASE` |
|
||||||
|
| `CUSTOM_ALERT` | `NotificationType.customAlert` | `NotificationType.CUSTOM_ALERT` | `NOTIFICATION_TYPE_CUSTOM_ALERT` |
|
||||||
|
| `UPGRADE_PROMO` | `NotificationType.upgradePromo` | `NotificationType.UPGRADE_PROMO` | `NOTIFICATION_TYPE_UPGRADE_PROMO` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Serialization/Deserialization
|
||||||
|
|
||||||
|
### 7.1 Date Handling
|
||||||
|
|
||||||
|
All dates stored as **Unix timestamp (milliseconds)** in SQLite. Convert to/from platform-specific types:
|
||||||
|
|
||||||
|
| Platform | Conversion |
|
||||||
|
|----------|------------|
|
||||||
|
| Swift | `Date(timeIntervalSince1970: timestamp / 1000)` |
|
||||||
|
| Kotlin | `Date(timestamp)` |
|
||||||
|
| C | `time_t` with `timestamp / 1000` |
|
||||||
|
|
||||||
|
### 7.2 JSON Fields
|
||||||
|
|
||||||
|
Arrays and nested objects stored as JSON strings:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Categories stored as JSON array
|
||||||
|
categories = '["tech", "programming", "rust"]'
|
||||||
|
|
||||||
|
-- Enclosure stored as JSON object
|
||||||
|
enclosure = '{"url": "...", "type": "audio/mpeg", "length": 1234567}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 UUID Handling
|
||||||
|
|
||||||
|
Store UUIDs as strings in canonical format:
|
||||||
|
```
|
||||||
|
550e8400-e29b-41d4-a716-446655440000
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate using platform UUID libraries:
|
||||||
|
- Swift: `UUID().uuidString`
|
||||||
|
- Kotlin: `UUID.randomUUID().toString()`
|
||||||
|
- C: Use `uuid_generate()` from libuuid
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Indexing Strategy
|
||||||
|
|
||||||
|
### 8.1 Primary Indexes
|
||||||
|
- `subscriptions.id` (auto-created for PRIMARY KEY)
|
||||||
|
- `feed_items.id` (auto-created for PRIMARY KEY)
|
||||||
|
|
||||||
|
### 8.2 Secondary Indexes
|
||||||
|
| Table | Index | Purpose |
|
||||||
|
|-------|-------|---------|
|
||||||
|
| subscriptions | `idx_subscriptions_url` | URL lookups |
|
||||||
|
| subscriptions | `idx_subscriptions_category` | Category filtering |
|
||||||
|
| subscriptions | `idx_subscriptions_next_fetch` | Sync scheduling |
|
||||||
|
| feed_items | `idx_feed_items_guid` | Deduplication |
|
||||||
|
| feed_items | `idx_feed_items_subscription` | Feed-specific items |
|
||||||
|
| feed_items | `idx_feed_items_published` | Sorting by date |
|
||||||
|
| search_history | `idx_search_history_timestamp` | History sorting |
|
||||||
|
|
||||||
|
### 8.3 FTS Indexes
|
||||||
|
| Virtual Table | Purpose |
|
||||||
|
|---------------|---------|
|
||||||
|
| `feed_items_fts` | Full-text search on articles |
|
||||||
|
| `subscriptions_fts` | Full-text search on subscriptions |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Memory Considerations
|
||||||
|
|
||||||
|
### 9.1 Large Content Handling
|
||||||
|
|
||||||
|
- `content` field can be very large (full article HTML)
|
||||||
|
- Use lazy loading for content display
|
||||||
|
- Consider compressing content with zlib for storage
|
||||||
|
- Limit in-memory cache to N most recent items
|
||||||
|
|
||||||
|
### 9.2 Streaming for Large Result Sets
|
||||||
|
|
||||||
|
Use cursor-based pagination:
|
||||||
|
```sql
|
||||||
|
-- Instead of OFFSET
|
||||||
|
SELECT * FROM feed_items
|
||||||
|
WHERE subscription_id = ?
|
||||||
|
ORDER BY published DESC
|
||||||
|
LIMIT 20;
|
||||||
|
-- Use last_published as cursor for next page
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Future Extensibility
|
||||||
|
|
||||||
|
### 10.1 Schema Versioning
|
||||||
|
|
||||||
|
Add version table for migrations:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE schema_version (
|
||||||
|
version INTEGER PRIMARY KEY,
|
||||||
|
applied_at INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 Adding New Fields
|
||||||
|
|
||||||
|
- New optional fields: Add column with NULL default
|
||||||
|
- New required fields: Use migration with DEFAULT values
|
||||||
|
- Breaking changes: New table with shared ID
|
||||||
|
|
||||||
|
### 10.3 Feed Extensions
|
||||||
|
|
||||||
|
Support for future feed types:
|
||||||
|
- Podcast: Already supported via enclosure
|
||||||
|
- Video feeds: Use content type detection
|
||||||
|
- Media RSS: Extend enclosure model
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Implementation Checklist
|
||||||
|
|
||||||
|
- [ ] Define UUID type alias for each platform
|
||||||
|
- [ ] Create Date conversion utilities
|
||||||
|
- [ ] Implement FeedItem model (struct/class)
|
||||||
|
- [ ] Implement FeedSubscription model
|
||||||
|
- [ ] Implement SearchHistoryItem model
|
||||||
|
- [ ] Create SQLite table definitions
|
||||||
|
- [ ] Implement FTS5 triggers for feed_items
|
||||||
|
- [ ] Implement FTS5 triggers for subscriptions
|
||||||
|
- [ ] Add database indexes
|
||||||
|
- [ ] Write serialization/deserialization code
|
||||||
|
- [ ] Implement pagination helpers
|
||||||
Reference in New Issue
Block a user