Offline-First Architecture, ארכיטקטורה ראשון-אופליין

📚 פיתוח Mobile ⏱️ 14 דק׳ 🎓 מתקדם ✓ חינם לגמרי
Offline-First Architecture, ארכיטקטורה ראשון-אופליין

למה כל app מובייל חייב להיות Offline-First ב-2025

תרחיש אמיתי: אפליקציית שירות שדה ישראלית, טכנאי של חברת תקשורת נוסע לבדיקה בנגב. ה-app נפתח בבוקר עם מלא רשת, ממלא טפסים, מצלם ממצאים, ואז נכנס לאזור ללא כיסוי. הapp מקרש. הנתונים אובדים. הטכנאי חוזר למשרד עם ידיים ריקות. זה לא edge case, זה המציאות של כל app שמניח שהרשת תמיד זמינה.

Offline-first לא אומר שה-app עובד ללא אינטרנט, זה אומר שה-app מניח שהרשת תיכשל. כל write נשמר locally תחילה, ורק אחר כך מסתנכרן לserver. כל read מגיע מcache, ורק אחר כך מה-network. הבדל קטן בגישה, הבדל ענק בחוויה. מחקר מ-2025 מצא שמעבר ל-local-first הוריד זמן טעינה מ-1.7 שניות ל-300ms על Android ממוצע באזורים עם קישוריות לא יציבה.

Claude עוזר לתכנן את ה-sync strategy, אבל רק אם נותנים לו את הפרטים הנכונים: scale, סוגי data, failure modes, וconflict scenarios. שיעור זה מלמד את הארכיטקטורה, ואיך לכתוב פרומפטים שמייצרים קוד production-ready.

בחירת Storage Layer, לא החלטה שמשנים אחר כך

הבחירה בstorage layer היא אחת ההחלטות הכי קשות לשנות בהמשך. הנה ההשוואה הפרקטית:

ספרייהמתי להשתמשחיסרון עיקרי
MMKVSettings, queue של pending ops, tokensלא מתאים לdata רלציוני
WatermelonDBCatalog, entities עם relations, sync מובנהדורש native build (לא Expo Go)
Expo SQLite + DrizzleStack מודרני, TypeScript, בלי native buildsync logic כותבים לבד
PowerSyncSync אוטומטי Postgres-SQLite, minimal codevendor dependency, pricing
AsyncStorageלא, לא להשתמש לdata קריטיאיטי, אין transactions, async בלבד

הכלל הפשוט: MMKV לqueue, WatermelonDB או Expo SQLite+Drizzle לentities, react-native-fs לקבצים. לsync אוטומטי ללא כתיבת sync logic, PowerSync היא הבחירה היחידה ב-2025 עם first-class offline support (ElectricSQL ו-Zero הכריזו ש-offline out of scope).

Sync Queue, הלב של Offline-First

הרכיב הכי חשוב בארכיטקטורה הוא ה-sync queue: רשימה של pending operations שחייבת לשרוד app restart. טעות נפוצה: queue ב-memory. כשה-app נסגר, הqueue נמחק, והuser מאבד את כל מה שעשה offline.

הפרומפט לClaude לייצר sync queue מלא:

כתוב לי offline support לאפליקציה ב-React Native
You are a mobile backend architect specializing in offline-first sync. Design a TypeScript SyncQueue class for a React Native field-service app (50K field technicians, iOS + Android) with: - Technicians fill forms and capture photos offline for hours - Data types: work orders (user-owned, editable), asset catalog (read-only, queryable) - Network: unreliable, 2G to no-signal in remote areas - Storage: MMKV for queue persistence (must survive app restart), react-native-fs for photos Provide: 1. Queue enqueue/dequeue with MMKV persistence 2. Exponential backoff retry (1s, 2s, 4s... max 30s, circuit breaker after 5 attempts) 3. NetInfo listener to trigger sync on reconnection 4. Status enum: idle / syncing / failed for UI badge 5. Conflict resolution for work orders: server timestamp wins if newer, else keep local TypeScript strict mode, no any.

מה שגורם לפרומפט החזק לעבוד: role framing ברור, scale מוגדר (50K technicians), failure modes ספציפיים (שעות ללא רשת), storage מוגדר, ו-expected outputs ברשימה מספורת. Claude יוצר קוד production-ready כשהוא מקבל את ה-context הזה.

Conflict Resolution, הבעיה שמגלים מאוחר מדי

User פותח app במובייל, מוסיף 3 items לcart. User פותח את אותו app בweb (אם יש), מוסיף 2 items אחרים. שניהם offline. שניהם מסתנכרנים. מה ה-cart הסופי?

כפי שנכתב ב-LogRocket Blog על WatermelonDB: "כש-server מחזיר conflict (HTTP 409), הapp צריך לmerge את ה-cart המקומי עם זה שבserver". אבל איזה merge? יש שלוש אסטרטגיות:

הגדירו conflict strategy לכל entity מהיום הראשון, זה הרבה יותר קשה להוסיף בהמשך.

WatermelonDB: Lazy Sync עם lastPulledAt

WatermelonDB פותרת את בעיית ה-initial sync: בסנכרון ראשון היא שולחת null, ומקבלת כל הdata. בסנכרונים הבאים שולחת את ה-lastPulledAt timestamp, ומקבלת רק את השינויים מאז. עם 50,000 מוצרים בcatalog, זה ההבדל בין sync של 50MB לsync של 2KB. הספרייה נבדקה ב-15 apps production עם 500K+ users ו-99.8% sync success rate.

חשוב: WatermelonDB דורשת development build, לא עובדת עם Expo Go. אם הצוות שלכם עובד עם Expo managed workflow, בחרו Expo SQLite + Drizzle עם sync logic ידני.

טעויות נפוצות

Last-Write-Wins, ה-timestamp המאוחר מנצח, הcart הישן נמחק
Additive merge, איחוד items משני הצדדים, max quantity לitem זהה
Manual resolution, מציגים למשתמש שתי גרסאות ומבקשים לבחור
לדחות writes offline עד שיש רשת
הוא מצפין את ה-data בעת sync
הוא פותר conflicts אוטומטית
הוא שולח רק שינויים מה-sync האחרון, לא את כל ה-data
הוא מונע rate limiting מהserver
שימוש ב-WatermelonDB עם Postgres backend
שמירת pending queue ב-memory במקום ב-MMKV/SQLite
שימוש ב-Expo SQLite במקום WatermelonDB
שימוש ב-exponential backoff לretries

סיכום, עקרונות שישנו את ה-App שלכם

רוצה ללמוד עם מעקב התקדמות, קוויזים ותעודה?

כל 130 השיעורים פתוחים בחינם, כולל נגן אינטראקטיבי, שמירת התקדמות ותעודה דיגיטלית בסיום.