מבוא
תארו לעצמכם לקוח שמבצע הזמנה באתר שלכם, משלם בכרטיס אשראי, אבל לא מקבל עדכון סטטוס. הוא מתחיל לדאוג, מתקשר לתמיכה — העסק שלכם מפסיד כסף ומוניטין. איך בונים מערכת התראות סובלנית לתקלות שמספקת הודעות דרך אימייל, SMS או טלגרם ללא קשר לכשלי ערוצים? אנחנו מפרקים את הארכיטקטורה, הקוד והתשתית שנבדקו על פני יותר מ-50 פרויקטים במסחר אלקטרוני, פינטק ו-SaaS. אנחנו מבטיחים שאף אירוע לא יאבד — המערכת שלנו משתמשת בתור עם המתנה אקספוננציאלית וניטור.
במשך יותר מ-5 שנים, מעולם לא נתקלנו בהתראה שאבדה עקב טעות ארכיטקטונית. כל הבעיות נפתרו בשלב התכנון.
בעיות שאנחנו פותרים
- אובדן הודעות — ערוץ נופל או שה-API מחזיר שגיאה (לדוגמה, Twilio מחזיר 500). אנחנו משתמשים בתור (Bull Queue) עם ניסיונות חוזרים ורישום לוגים. במקרה של כשל, ההודעה נשלחת שוב לאחר 2, 4, 8 שניות (המתנה אקספוננציאלית). לאחר 3 כשלים, היא נכנסת לתור הודעות מתות (Dead Letter Queue) לניתוח ידני.
- בלבול ערוצים — משתמש רוצה התראות טלגרם אבל מקבל SMS. אנחנו מיישמים אזור אישי עם הגדרות העדפות: בחירת ערוץ לכל סוג אירוע, שעות שקט ואזור זמן.
- מלכודות ספאם — התראות טרנזקציוניות ושיווקיות מופרדות. שיווק משתמש בערוצים נפרדים (אימייל) עם אפשרות ביטול הרשמה. שעות שקט (22:00–08:00) חוסמות שליחה בשעות לא פעילות.
הרחב דוגמת קוד לשירות ההתראות
// notification.service.ts
interface NotificationRequest {
userId: string;
type: NotificationType;
data: Record<string, unknown>;
channels?: Channel[];
priority?: 'high' | 'normal' | 'low';
}
type NotificationType =
| 'order.placed'
| 'order.shipped'
| 'payment.failed'
| 'password.reset'
| 'promo.discount';
class NotificationService {
async send(request: NotificationRequest): Promise<void> {
const prefs = await this.userPrefsRepo.findByUserId(request.userId);
const channels = request.channels ?? this.resolveChannels(request.type, prefs);
await Promise.allSettled(
channels.map(channel => this.sendViaChannel(channel, request, prefs))
);
}
private resolveChannels(type: NotificationType, prefs: UserPrefs): Channel[] {
const channelMap: Record<NotificationType, Channel[]> = {
'order.placed': ['email', 'telegram'],
'order.shipped': ['email', 'sms', 'telegram'],
'payment.failed': ['email', 'sms'],
'password.reset': ['email'],
'promo.discount': prefs.marketingChannels
};
return channelMap[type] ?? ['email'];
}
} אימייל דרך Resend
להתראות אימייל, אנחנו משתמשים ב-Resend. להלן דוגמת קוד:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
async function sendEmailNotification(
user: User,
type: NotificationType,
data: Record<string, unknown>
) {
const template = emailTemplates[type];
await resend.emails.send({
from: '[email protected]',
to: user.email,
subject: template.subject(data),
react: template.component({ user, ...data })
});
}
// Шаблон order.shipped
const orderShippedTemplate = {
subject: (data) => `Ваш заказ #${data.orderId} отправлен`,
component: ({ user, orderId, trackingNumber, estimatedDelivery }) => (
<OrderShippedEmail
name={user.firstName}
orderId={orderId}
trackingNumber={trackingNumber}
trackingUrl={`https://example.com/track/${trackingNumber}`}
estimatedDelivery={estimatedDelivery}
/>
)
}; SMS דרך Twilio
להתראות SMS, אנחנו משתמשים ב-Twilio. דוגמה:
import twilio from 'twilio';
const client = twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN);
async function sendSmsNotification(
user: User,
type: NotificationType,
data: Record<string, unknown>
) {
if (!user.phone || !user.phoneVerified) return;
const templates: Record<NotificationType, (data: Record<string, unknown>) => string> = {
'order.shipped': (d) => `Заказ #${d.orderId} отправлен. Трек: ${d.trackingNumber}. Ожидайте к ${d.date}`,
'payment.failed': (d) => `Оплата заказа #${d.orderId} не прошла. Обновите данные карты: ${d.retryUrl}`
};
const text = templates[type]?.(data);
if (!text) return;
await client.messages.create({
to: user.phone,
from: process.env.TWILIO_PHONE,
body: text
});
} טלגרם דרך Bot API
להתראות טלגרם, אנחנו משתמשים בבוט טלגרם. דוגמה:
import TelegramBot from 'node-telegram-bot-api';
const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN);
async function sendTelegramNotification(
user: User,
type: NotificationType,
data: Record<string, unknown>
) {
if (!user.telegramChatId) return;
const messages: Record<string, (d: Record<string, unknown>) => string> = {
'order.shipped': (d) => `📦 *Заказ #${d.orderId} отправлен*\n\nТрек-номер: \`${d.trackingNumber}\`\nОжидаемая доставка: ${d.date}`,
'payment.failed': (d) => `⚠️ *Ошибка оплаты*\n\nЗаказ #${d.orderId} не оплачен. [Повторить оплату](${d.retryUrl})`
};
const text = messages[type]?.(data);
if (!text) return;
await bot.sendMessage(user.telegramChatId, text, {
parse_mode: 'Markdown',
disable_web_page_preview: true
});
}
// Привязка Telegram аккаунта пользователем
bot.onText(/\/start (.+)/, async (msg, match) => {
const linkToken = match[1];
const userId = await verifyLinkToken(linkToken);
if (userId) {
await userRepo.updateTelegramChatId(userId, msg.chat.id.toString());
bot.sendMessage(msg.chat.id, '✅ Telegram успешно подключён! Вы будете получать уведомления.');
}
});
ניהול העדפות
// Таблица user_notification_prefs
interface UserNotificationPrefs {
userId: string;
emailEnabled: boolean;
smsEnabled: boolean;
telegramEnabled: boolean;
marketingEmailEnabled: boolean;
marketingSmsEnabled: boolean;
quietHoursStart: string;
quietHoursEnd: string;
timezone: string;
} איך למנוע אובדן התראות?
תור התראות עם ניסיונות חוזרים הוא המרכיב המרכזי. במקרה של כשל בשליחה (לדוגמה, Twilio מחזיר 500), ההודעה נשלחת שוב לאחר 2, 4, 8 שניות. לאחר 3 כשלים, היא נכנסת ל-DLQ (תור הודעות מתות) לניתוח ידני. אמינות מסירה ללא תור היא כ-95%; עם תור, היא 99.9%. זה פי 50 פחות הודעות שאבדו. לדוגמה, עם 10,000 התראות ביום, ללא תור אתם מאבדים 500; עם תור, רק 10.
למה אתם צריכים תור עם ניסיונות חוזרים?
שירותים לא יציבים: Resend עלול לעכב אימיילים, לטלגרם יש מגבלות קצב. תור עם המתנה אקספוננציאלית מבטיח שההודעה תימסר ללא עומס. אנחנו משתמשים ב-Bull Queue עם Redis. בנוסף, אנחנו מגדירים ניטור: אם מספר השגיאות חורג מסף מסוים, אנחנו שולחים התראה לצוות בטלגרם.
השוואת ערוצים
| ערוץ | מהירות מסירה | אמינות | מתאים במיוחד ל | עלות ליחידה |
|---|---|---|---|---|
| אימייל | דקות | גבוהה | שיווק, טרנזקציות | $0.001 לאימייל (Resend) |
| SMS | שניות | גבוהה | אירועים דחופים (תשלום) | $0.05 להודעה (Twilio) |
| טלגרם | מיידי | בינונית | התראות push | חינם |
SMS נמסר פי 60 מהר יותר מאימייל, וזה קריטי להתראות תשלום. למרות שהעלות ליחידה של SMS גבוהה יותר, זה מוצדק לאירועים דחופים.
איך להגדיר התראות: מדריך שלב-אחר-שלב להגדרת התראות
- הגדירו טריגרים. אילו אירועים באפליקציה שלכם דורשים התראות? סט טיפוסי: הזמנה בוצעה, תשלום נכשל, איפוס סיסמה.
- בחרו ערוצים. לכל טריגר, קבעו את הערוץ המועדף. שכפלו אירועים קריטיים לשני ערוצים (אימייל + SMS).
- הגדירו את תור ההתראות. שלבו Bull Queue עם Redis לעיבוד הודעות עם ניסיונות חוזרים ורישום לוגים.
- צרו תבניות. פתחו תבניות HTML לאימייל, תבניות טקסט ל-SMS וטלגרם עם התאמה אישית.
- בדקו תרחישים. בדקו כשל בכל ערוץ, חריגה ממגבלת קצב, וטיפול נכון בשגיאות.
תהליך
| שלב | משך | תוצאה |
|---|---|---|
| ניתוח | 1–2 ימים | דיאגרמת זרימה וטריגרים |
| עיצוב | 2–3 ימים | ארכיטקטורה ובחירת ערוצים |
| יישום | 5–7 ימים | אינטגרציה וקוד |
| בדיקות | 2 ימים | דוח בדיקות |
| פריסה | יום אחד | מערכת עובדת |
מה כלול
- עיצוב ארכיטקטורת התראות
- אינטגרציה לאימייל (Resend/SendGrid), SMS (Twilio) וטלגרם (Bot API)
- הגדרת תור וניסיונות חוזרים
- אזור אישי לניהול העדפות
- בדיקות תרחישים (עומס, כשלי ערוצים)
- תיעוד והדרכת צוות
ציר זמן
תצורה בסיסית (3 ערוצים, 5 טריגרים, תור התראות) — משבוע עד שבועיים. ציר הזמן תלוי במורכבות הלוגיקה העסקית. צרו קשר — נבחן את הפרויקט שלכם ביום אחד. קבלו ייעוץ — נבחר את הערוצים האופטימליים לעסק ולתעבורה שלכם.
עלויות התראות
אנחנו משתמשים בספקים עם תעריפים נמוכים: Resend מ-$0.001 לאימייל, Twilio מ-$0.05 ל-SMS. טלגרם חינם. אתם משלמים רק על שימוש בפועל, ללא עמלות נסתרות. אופטימיזציה של בחירת ערוצים יכולה לחסוך עד 40% מתקציב ההתראות שלכם. לדוגמה, שליחת 10,000 אימיילים טרנזקציוניים עולה $10, בעוד 10,000 SMS עולים $500; מעבר לטלגרם להתראות לא דחופות יכול לחסוך אלפים בשנה.







