מקרה בוחן · מובייל ושרת · ⁦2025–2026⁩

אפליקציית עזרה קהילתית שבה כל בקשה מגיעה לשכנים הנכונים

הובלתי את הבנייה מחדש של אפליקציית עזרה הדדית עבור מפעיל ישראלי של קהילת עזרה. משתמשים מפרסמים בקשה (הסעה לבית חולים, כלב שאבד, כיסא גלגלים לשבוע), ושכנים בסביבה שבחרו לקבל התראות מקבלים אותה ועונים בצ׳אט, בוואטסאפ, ב־⁠SMS או בשיחה. הגרסה החדשה החליפה אפליקציה ותיקה באותם דפי חנות, שמרה על כל המשתמשים והנתונים, ומחליטה בשאילתה אחת למסד הנתונים מי צריך לשמוע על כל בקשה.

⁦.NET 8⁩ Web APIEF Core 8SQL ServerReact Native · ExpoTypeScriptRedux ToolkitPush notificationsGoogle Maps / Places
לקוח
אפליקציית עזרה הדדית קהילתית בישראל (חסוי)
התפקיד שלי
ראש צוות, ארכיטקט ומפתח בפועל
לוח זמנים
יולי 2025 – אוקטובר 2026 · באוויר ב־⁠iOS וב־⁠Android
סטטוס
באוויר
במבט אחד
1שאילתת SQL אחת בוחרת את קהל היעד של כל בקשה לפי קטגוריה, מילת מפתח, מרחק ועיר
0קריאות פוש בתוך קריאת ה־⁠API של ”יצירת בקשה”. ההפצה רצה בתור ברקע.
N עד 1רענוני טוקן כשכמה קריאות במקביל נתקלות בסשן שפג
6 עד 31טבלאות ותיקות הועברו, קודם בהרצת ניסיון, לסכמה של 31 טבלאות
4 × 341שפות × מחרוזות ממשק, כולל עברית וערבית מימין לשמאל
55נקודות קצה ב־⁠API של המובייל, ב־⁠6 קונטרולרים
30מסכים באפליקציה, בסיס קוד אחד ל־⁠iOS ול־⁠Android
15 חודשיםמהקומיט הראשון ב־⁠backend ועד גרסת החנות האחרונה, יותר מ־⁠2,000 קומיטים
הבעיה

בקשת עזרה שווה משהו רק אם האנשים הנכונים רואים אותה, ומהר

למפעיל כבר הייתה אפליקציה עם משתמשים אמיתיים ושנים של בקשות במסד MySQL. היה קשה להרחיב אותה: לא היו בה קבוצות, צ׳אט בתוך האפליקציה או הגדרות התראה אישיות, ולכן אפשר היה לצמצם את ההתראות רק באופן גס. המשימה הייתה לבנות מחדש את השרת ואת האפליקציה בלי לאבד אף משתמש, ולתת לכל אחד לבחור בדיוק אילו בקשות יעירו לו את הטלפון: אילו קטגוריות, אילו מילות מפתח, באיזה מרחק, מאילו ערים ובאילו שעות.

מה הובלתי

מארכיטקטורה ועד שחרור לחנויות

  • תכננתי את הפתרון: API בשכבות ב־⁠⁦.NET 8⁩ (קונטרולרים, שירותים, אחסון), סכמת SQL Server ומבנה אפליקציית ה־⁠Expo.
  • כתבתי בעצמי את רוב הקוד, גם בשרת וגם באפליקציה, והובלתי את הצוות דרך code review ומוסכמות עבודה (commitlint,‏ ESLint,‏ Prettier,‏ Husky).
  • בניתי את מנגנון התאמת ההתראות: מודל ההגדרות, שאילתת קהל היעד, שעות שקט, תיבת התראות באפליקציה ופוש.
  • תכננתי וכתבתי את הסבת הנתונים הוותיקים: מ־⁠MySQL לסביבת staging ומשם לסכמה החדשה, עם מצב הרצת ניסיון.
  • הייתי אחראי על מסלול השחרור: ענפי סביבה (dev,‏ QA,‏ staging,‏ production), בניות EAS, והמעבר לזהויות החנות הקיימות של האפליקציה הוותיקה.
ארכיטקטורה

אפליקציה רזה, API בשכבות, וסינון שרץ בתוך מסד הנתונים

התרשים רחב מהמסך: החליקו הצידה לצפייה בכולו →

האפליקציה מדברת גם ישירות עם שירות הצ׳אט דרך ה־⁠SDK שלו, עם טוקן משתמש שה־⁠API מנפיק. תמונות נשמרות באחסון אובייקטים, וב־⁠SQL נשמרות רק הכתובות שלהן. ההתחברות היא במספר טלפון וקוד SMS חד־פעמי. משתמש חסום מקבל מהשרת קוד סטטוס ייעודי, והאפליקציה מעבירה אותו למסך ”הגישה נחסמה”.

החלטות תכנון

לסנן איפה שהנתונים נמצאים

קהל היעד בשאילתה אחת למסד

כללי הקטגוריה, מילת המפתח, המרחק והעיר מתורגמים לשאילתת LINQ אחת. המרחק מחושב בפונקציית Haversine ב־⁠SQL שממופה ל־⁠EF Core, והרדיוס האפקטיבי הוא הקטן מבין הטווח שהמשתמש בחר והרדיוס של הבקשה. אף רשימת משתמשים לא נטענת לזיכרון רק כדי לזרוק את רובה. המחיר: פונקציית מרחק סקלרית לא משתמשת באינדקס, וזה בסדר בהיקף הנוכחי. אם בסיס המשתמשים יגדל מאוד, הצעד הבא הוא אינדקס מרחבי.

תיבה לכולם, פוש רק כשזה מתאים

כל משתמש שמתאים מקבל שורה בתיבת ההתראות, גם בלי טוקן מכשיר תקין, כך ששום דבר לא הולך לאיבוד אחרי התקנה מחדש או הפעלה מחדש של ההתראות. פוש נשלח רק למי שלא נמצא בשעות השקט שלו, ושעות השקט יכולות לחצות חצות (⁦22:00–06:00⁩). המחיר: יותר שורות בטבלת ההתראות, ולכן התיבה מוצגת בדפים של 10.

ההפצה מחוץ לקריאת ה־⁠API

יצירת בקשה שומרת אותה ומכניסה אותה לתור. worker ברקע בוחר את קהל היעד, כותב לתיבה ושולח בקשת פוש מקובצת אחת. מי שמפרסם לא מחכה למאות טלפונים. המחיר: התור רץ בתוך התהליך, ופריטים שממתינים בזמן restart יכולים ללכת לאיבוד. תהליך worker נפרד כבר הוקם כשלד, כדי להוציא את התור החוצה.

לבנות מחדש ולהסב, לא להטליא

במקום להרחיב את אפליקציית ה־⁠MySQL הישנה, הנתונים עברו בהעתקה מרוכזת (bulk copy) למסד staging. משם סקריפט T-SQL עם מתג @DRY_RUN, בדיקות הגנה וטיפול בכפילויות העביר אותם לסכמה החדשה. האפליקציה החדשה שוחררה תחת זהויות החנות הישנות, כך שמכשירים קיימים פשוט קיבלו עדכון. המחיר: סכמה נוספת וכלי חד־פעמי שצריך לתחזק עד המעבר.

יומן הנדסי

מה נשבר, ואיך תיקנתי

מתוך היסטוריית הקומיטים של שני המאגרים. מבחר:

יולי 2025

כל קריאה שילמה על מעקב שינויים

רוב נקודות הקצה רק קוראות נתונים, אבל EF Core עקב אחרי כל ישות שנטענה.

הגדרתי no-tracking כברירת מחדל לכל ה־⁠context, ומעקב מופעל במפורש רק במסלולי העדכון. בנוסף הוסרו מהשאילתה של הפוסטים includes שלא היה בהם צורך.

אוקטובר–נובמבר 2025

הנתונים הישנים לא התאימו למודל החדש

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

בניתי הסבה בשני שלבים: העתקה מרוכזת מ־⁠MySQL לסכמת staging, ואחריה מיפוי ב־⁠T-SQL עם זמנים שמוגנים מגלישה, איחוד דיווחים כפולים, THROW כשחסרים נתוני ייחוס, ומצב הרצת ניסיון שמדווח ספירות ועושה rollback.

אוקטובר 2025

פרסום בקשה חיכה להתראות

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

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

אוקטובר 2025

פוסטים של קבוצה נראו מחוץ לקבוצה

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

הפיד וההתראות מסוננים לפי חברות בקבוצה, נוספה אפשרות מפורשת ל”פוסט ציבורי בקבוצה”, ובבדיקת אבטחה בפברואר 2026 נסגרו פערי גישה נוספים ב־⁠API של הפוסטים.

דצמבר 2025

התראות במרחק הלא נכון ובשעה הלא נכונה

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

הרדיוס האפקטיבי הוא עכשיו min(reader range, request radius), ובדיקת שעות השקט מטפלת בטווחים שחוצים חצות.

פברואר 2026

התראות לפי איפה שהיית, לא איפה שאתה

ההתאמה הסתמכה על המיקום ששמור בפרופיל, והוא מתיישן אצל מי שזז הרבה.

האפליקציה שולחת את המיקום הנוכחי בפתיחה וכל 5 דקות, ולא מרעננת את הפיד כשהמיקום לא השתנה. משתמשים יכולים גם לעקוב אחרי כמה ערים.

מרץ–אפריל 2026

ימין־לשמאל שספריות התעלמו ממנו

עברית וערבית הן שפות מלאות באפליקציה, אבל חלק מרכיבי צד שלישי התעלמו מכיוון התצוגה (הסליידר, ההשלמה האוטומטית של מקומות, כותרת הצ׳אט ב־⁠iOS).

כפינו את הכיוון דרך שכבת ה־⁠i18n, תיקנתי את רכיב המקומות עם patch-package, שדרגנו את ספריית הסליידר, ועברנו על יישור ה־⁠RTL מסך אחרי מסך.

אפריל–מאי 2026

מקרי קצה בסשן

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

ה־⁠interceptor של Axios משתף עכשיו רענון אחד פעיל בין כל הקוראים. משתמש חסום מקבל קוד סטטוס ייעודי שמעביר אותו למסך ”הגישה נחסמה”.

יוני–יולי 2026

להחליף את האפליקציה הישנה, לא להוסיף חדשה

שחרור כדף חנות חדש היה מחייב לבקש מכל משתמש קיים להתקין מחדש.

הקונפיגורציה של iOS ו־⁠Android עברה לזהויות החבילה הקיימות של האפליקציה הוותיקה, וקודי הגרסה יושרו, כך שהחנויות מקבלות אותה כעדכון.

נסו בעצמכם

מי מקבל התראה?

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

קטגוריה
רדיוס הבקשה
שעת פרסום
מופיעה המילה ”wheelchair”
תוצאה

איפה זה נחת

תחום
לפני
אחרי
פלטפורמה
אפליקציה ותיקה על MySQL, קשה להרחבה
⁦.NET 8⁩ ו־⁠SQL Server ‏(31 טבלאות), אפליקציית Expo ל־⁠iOS ול־⁠Android, כל המשתמשים והנתונים הוסבו
התאמת התראות
התראות גסות, אחת לכולם
5 הגדרות אישיות (קטגוריה, מילת מפתח, מרחק, ערים, שעות שקט), שנבדקות בשאילתת SQL אחת
פרסום בקשה
הפצת הפוש בתוך קריאת ה־⁠API
נשמר ונכנס לתור. קהל היעד, התיבה ובקשת פוש מקובצת אחת רצים ברקע.
התראות שפוספסו
בלי טוקן או עם התראות כבויות, לא נשאר תיעוד
כל התאמה נכנסת לתיבת התראות בתוך האפליקציה, עם דפדוף. הפוש מכבד שעות שקט.
יצירת קשר עם המבקש
מספר טלפון בלבד
צ׳אט לכל בקשה, וגם וואטסאפ, SMS או שיחה, כולל פוסטים אנונימיים ופוסטים ”בשם מישהו אחר”
שפות
ממשק שבנוי סביב עברית
עברית, ערבית, אנגלית ורוסית, 341 מחרוזות בכל שפה, עם תצוגת RTL ו־⁠LTR
התחברות
הקלדה ידנית של קוד ה־⁠SMS
הקוד מתמלא לבד (SMS consent ב־⁠Android,‏ autofill ב־⁠iOS). טוקני גישה של 15 דקות עם רענון משותף.
מסלול השדרוג
אפליקציה חדשה הייתה מחייבת התקנה חדשה
שוחררה תחת זהויות החנות הקיימות, כך שמכשירים קיימים מתעדכנים במקום
טכנולוגיות

טכנולוגיות

Backend
⁦.NET 8⁩ Web API,‏ ⁦C#⁩,‏ AutoMapper,‏ JWT bearer, שירותי רקע (hosted services),‏ NLog
נתונים
SQL Server,‏ EF Core 8 עם NetTopologySuite, פונקציות ופרוצדורות T-SQL
מובייל
React Native 0.79,‏ Expo 53,‏ Expo Router,‏ TypeScript,‏ Redux Toolkit,‏ React Hook Form,‏ Reanimated
אינטגרציות
פוש (Expo,‏ FCM,‏ APNs), שער SMS,‏ SDK לצ׳אט מנוהל,‏ Google Maps ו־⁠Places, אחסון אובייקטים
רב־לשוניות
i18next עם 4 שפות, RTL ו־⁠LTR
שחרור
בניות EAS, ענפים ל־⁠dev,‏ QA,‏ staging ו־⁠production,‏ commitlint,‏ ESLint,‏ Prettier,‏ Husky

שיחת אבחון חינם · 30 דקות · ללא התחייבות

הביאו תהליך אחד. קבלו תוכנית ומספר לשפר.

כתבו בכמה שורות: איזה תהליך או מערכת, מה כואב היום (זמן, טעויות, עלויות או פיילוט AI שנתקע) ומי עובד עם זה. אחרי שיחה קצרה מקבלים תוך כ־3 ימים תוכנית כתובה והצעת מחיר סגורה.

לשיחת אבחון חינםafif.mansour@gmail.com

נפתח מייל מוכן מראש · מענה תוך יום עסקים

ה־AI קורא. הקוד סופר.

כל הפרויקטים