1) ما هو هذا المشروع أصلاً؟
تخيّل أن لديك عدة حملات إعلانية تعمل على Meta، وربما لاحقاً على TikTok أو Google. بدلاً من الدخول إلى كل منصة بشكل منفصل لمتابعة الأداء، يقوم هذا المشروع ببناء مركز قيادة موحد يجلب الأرقام، يحللها، يكتشف الخلل، ويرسل تنبيهات أو ينفذ إجراءات تلقائياً عند الحاجة.
ما الذي يفعله النظام؟
- يربط الحساب الإعلاني بالمستخدم.
- يجلب الإحصاءات من Meta Graph API.
- يوحد البيانات ويحللها.
- يستخدم AI لإعطاء توصيات مفهومة.
- ينفذ قواعد أتمتة مثل إيقاف الحملات الخاسرة.
- يرسل تنبيهات إلى الهاتف أو تيليجرام أو Slack.
لماذا هذا النوع من المشاريع مهم؟
- يمثل مشروع SaaS حقيقي يمكن بيعه باشتراك شهري.
- يجمع بين Backend Architecture و Integrations و AI.
- يعلمك كيف تُبنى الأنظمة الكبيرة بشكل منظم.
- يفتح الباب لإضافة TikTok أو Google Ads لاحقاً بسهولة.
2) الأساس الذي بنينا عليه المشروع (Tech Stack)
اعتمد المشروع على مجموعة تقنيات تكمل بعضها البعض. الفهم هنا مهم، لأن كل تقنية لها دور محدد داخل النظام.
| التقنية | الدور داخل المشروع | لماذا اختيرت؟ |
|---|---|---|
| Node.js | محرك تشغيل JavaScript على السيرفر | سريع ومناسب لـ I/O وطلبات APIs |
| TypeScript | إضافة أنواع واضحة للكود | تقليل الأخطاء وتحسين القراءة والتنظيم |
| NestJS | Framework منظم لبناء Back-end | معمارية قوية تشبه الأنظمة الكبيرة |
| TypeORM / PostgreSQL | التعامل مع البيانات والجداول | هيكلة واضحة وقاعدة بيانات قوية |
| Axios / HttpService | التواصل مع خدمات خارجية | إرسال طلبات HTTP إلى Meta وTelegram وغيرها |
المفاهيم الأساسية في NestJS
- Module: صندوق يجمع الوظائف المتقاربة في مكان واحد.
- Controller: يستقبل الطلب القادم من الواجهة أو التطبيق.
- Service: ينفذ المنطق الفعلي في الخلفية.
- Entity: يصف شكل الجدول داخل قاعدة البيانات.
- Guard: يفحص هل للمستخدم الحق في الوصول أم لا.
- Filter: يتعامل مع الأخطاء ويحوّلها إلى رد نظيف ومنظم.
3) جولة في النواة (The Root)
src/main.ts — الشرارة الأولى
هذا هو أول ملف يُنفذ عند تشغيل المشروع. دوره هو إنشاء التطبيق، تجهيز الإعدادات العامة، وإخبار السيرفر أن يبدأ الاستماع للطلبات.
async function bootstrap() {
// إنشاء التطبيق بناءً على الموديول الرئيسي
const app = await NestFactory.create(AppModule);
// كل الروابط ستبدأ بـ /api
app.setGlobalPrefix('api');
// إعداد Swagger لتوثيق الـ API
const config = new DocumentBuilder()
.setTitle('SaaS Meta')
.build();
// تشغيل السيرفر على المنفذ 3001
await app.listen(3001);
}
main.ts فلن يعمل المشروع كله، لأنه نقطة الإقلاع الأساسية.
src/app.module.ts — الدماغ المركزي
هذا الملف يربط كل موديولات المشروع ببعضها. أي جزء غير مسجل هنا غالباً لن يكون معروفاً للتطبيق.
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRoot({ /* database config */ }),
MetaAdsModule,
AutomationModule,
AuthModule,
UsersModule,
PaymentsModule,
],
})
export class AppModule {}
configuration.ts — الخزنة
هذا الملف يقرأ القيم السرية من .env، مثل مفاتيح Meta أو بيانات قاعدة البيانات. الهدف هو فصل الأسرار عن الكود.
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=secret
META_TOKEN=your_meta_token
GEMINI_API_KEY=your_gemini_key
4) موديول Meta Ads
هنا يبدأ المشروع بالتعامل المباشر مع Meta. هذه الطبقة مسؤولة عن الاتصال بـ Graph API، جلب البيانات، وتنظيفها لتصبح قابلة للفهم داخل النظام.
MetaAdsService — المفاوض
هذا الجزء يرسل الطلب إلى Meta ويسترجع الإحصاءات الأساسية للحملات.
async fetchInsights(adAccountId: string) {
const url = `https://graph.facebook.com/v19.0/${adAccountId}/insights`;
const response = await this.httpService.get(url, {
params: {
access_token: this.token,
fields: 'campaign_name,impressions,clicks,spend,ctr'
}
});
return response.data;
}
- يبني رابط API الخاص بالحساب الإعلاني.
- يرسل
access_tokenحتى تسمح Meta بإرجاع البيانات. - يحدد الحقول المطلوبة فقط لتقليل الحمل غير الضروري.
MetaNormalizer — عامل النظافة
بيانات Meta غالباً تعود بصيغة خام وغير موحدة. لذلك نحتاج طبقة تنظفها وتحولها إلى شكل موحد يعتمد عليه باقي المشروع.
static normalize(raw: any) {
return {
campaignId: raw.campaign_id,
campaignName: raw.campaign_name || 'اسم غير معروف',
impressions: parseInt(raw.impressions || '0'),
spend: parseFloat(raw.spend || '0'),
source: 'meta'
};
}
5) موديول التحليلات والذكاء الاصطناعي
بعد جلب البيانات، لا يكفي عرض الأرقام كما هي. هنا يبدأ دور التحليل: هل الحملة جيدة أم سيئة؟ هل الصورة ضعيفة؟ هل تكلفة النقرة مرتفعة؟ هل هناك شيء يستدعي تنبيهاً فورياً؟
AnalyticsService — المحلل
analyzeCampaign(data: any) {
const insights = [];
if (data.ctr < 1.0) {
insights.push({
type: 'warning',
code: 'LOW_CTR',
message: 'نسبة النقر ضعيفة! الجمهور يرى الإعلان ولكن لا ينقر عليه.',
recommendation: 'جرب تغيير صورة الإعلان أو العنوان لجذب الانتباه أكثر.'
});
}
if (data.cpc > 2.0) {
insights.push({
type: 'danger',
message: 'تكلفة النقرة غالية جداً!',
recommendation: 'راجع استهداف الجمهور، ربما تستهدف جمهوراً منافساً بشدة.'
});
}
return insights;
}
هذه التحليلات هي منطق برمجي صريح. أي أنها لا تعتمد على AI فقط، بل على قواعد واضحة يمكن الوثوق بها وتعديلها بسهولة.
AiService — الخبير الذكي
هنا يدخل الذكاء الاصطناعي ليحوّل الأرقام إلى شرح بشري قابل للفهم. يمكنه مثلاً أن يقرأ الأداء ويكتب توصية بلغة واضحة، أو حتى يحلل صورة الإعلان إذا كان لدينا Vision model.
استخدامات AI داخل المشروع
- شرح أداء الحملة بلغة مفهومة.
- اقتراح تحسينات على الجمهور أو الكرياتيف.
- تحليل صورة الإعلان من ناحية الجاذبية والوضوح.
- اكتشاف الأنماط التي قد لا يلتقطها الشرط البرمجي البسيط.
كيف تتكامل الخدمة مع النظام؟
- تستقبل البيانات المنظفة من النظام.
- يتم بناء Prompt واضح ومنضبط.
- يعيد النموذج توصية أو ملخصاً أو تفسيراً.
- يُعرض الناتج للمستخدم أو يستخدم ضمن التنبيهات.
6) محرك الأتمتة (Automation Engine)
هذا الجزء هو الذي يجعل المشروع أكثر من مجرد لوحة تحكم. هنا ننتقل من “المراقبة” إلى “التنفيذ الذكي”. المستخدم يضع قاعدة، والنظام ينفذها أوتوماتيكياً دون أن ينتظر تدخله اليدوي.
RuleEngineService — المراقب
async evaluateRule(rule: Rule) {
// 1) جلب البيانات الحالية من Meta
const currentData = await this.metaService.fetchInsights(rule.targetId);
// 2) فحص الشرط
const shouldTrigger = this.checkConditions(rule, currentData);
if (shouldTrigger) {
// 3) تنفيذ الإجراء المطلوب
if (rule.action === 'PAUSE') {
await this.metaService.updateStatus(rule.targetId, 'PAUSED');
this.logger.log(`تم إيقاف الحملة ${rule.targetId} بطلب من الأتمتة.`);
}
}
}
الفكرة العملية للقواعد
- إذا تجاوز الصرف 100 دولار بدون مبيعات → أوقف الحملة.
- إذا انخفض CTR عن حد معين → أرسل تحذيراً.
- إذا ارتفع CPA كثيراً → أبلغ المستخدم فوراً.
Cron Jobs — التوقيت
لكي تعمل الأتمتة تلقائياً، لا بد من وجود آلية زمنية تكرر الفحص كل فترة. هنا يأتي دور المهام المجدولة.
@Cron('0 * * * *')
handleHourlyChecks() {
// كل ساعة يتم فحص القواعد المسجلة
}
7) التنبيهات
عندما يكتشف النظام مشكلة أو ينفذ إجراءً مهماً، يجب أن يبلغ المستخدم فوراً. التنبيه ليس مجرد ميزة إضافية؛ بل جزء أساسي من قيمة المنتج.
NotificationService — ساعي البريد
async send(message: string, channels: string[]) {
for (const channel of channels) {
if (channel === 'telegram') {
await this.http.post('https://api.telegram.org/...', { text: message });
}
if (channel === 'fcm') {
await this.firebase.send({ body: message });
}
}
}
| القناة | الاستخدام | الفائدة |
|---|---|---|
| Telegram | رسائل مباشرة عبر Bot | سريع وسهل للمطور والمستخدم |
| Slack | إشعار لفريق العمل | مفيد للشركات والفرق |
| FCM | تنبيه على الهاتف | أفضل تجربة فورية للمستخدم |
8) الدفع والفلوس
بما أن هذا مشروع SaaS، فهو يحتاج نظام اشتراكات واضح. المستخدم يدفع، والنظام يفعّل صلاحياته تلقائياً. هذا الجزء يربط المنتج بنموذج الربح.
@Post('callback')
async handlePayment(@Body() data: any) {
const isValid = this.verifySignature(data);
if (isValid && data.success) {
await this.users.activateSubscription(data.userId);
}
}
- مزود الدفع يرسل Webhook عند نجاح العملية.
- السيرفر يتحقق من التوقيع الرقمي.
- إذا كانت العملية صحيحة، يفعّل الاشتراك مباشرة.
9) الأمان والاشتراكات
الأمان هنا ليس فقط تسجيل دخول. بل أيضاً حماية الميزات من الاستخدام غير المصرح به، والتحكم في حدود الخطة المجانية مقابل المدفوعة.
SubscriptionGuard — الحارس
async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const userId = request.headers['x-user-id'];
const user = await this.userRepo.findOne(userId);
if (user.tier === 'pro') return true;
if (user.requestCount < 10) {
user.requestCount++;
await this.userRepo.save(user);
return true;
}
throw new HttpException('انتهت النسخة التجريبية!', 402);
}
- يفحص هوية المستخدم.
- يجلب خطته الحالية من قاعدة البيانات.
- يسمح أو يمنع الوصول حسب السياسة المقررة.
10) التعامل مع الأخطاء
الأخطاء ستحدث دائماً: انقطاع من Meta، توكن منتهي، مشكلة قاعدة بيانات، أو خطأ برمجي. النظام الجيد لا ينهار بصمت، بل يعيد رسالة مفهومة ومنظمة.
catch(exception: unknown, host: ArgumentsHost) {
const status = exception.getStatus();
const message = exception.getResponse();
this.response.status(status).json({
code: status,
timestamp: new Date().toISOString(),
error: 'حدثت مشكلة في الطلب',
detail: message
});
}
11) الجداول والبيانات
لا يوجد مشروع Back-end حقيقي من دون طبقة بيانات واضحة. الكيانات أو الـ Entities هي الصياغة التي نستخدمها لوصف الجداول داخل قاعدة البيانات.
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ unique: true })
email: string;
@Column({ default: 'free' })
subscriptionTier: string;
@Column({ default: 0 })
requestCount: number;
@CreateDateColumn()
createdAt: Date;
}
لماذا هذا مهم؟
- يوضح ما الذي نخزنه لكل مستخدم.
- يسهل إنشاء الجداول تلقائياً عبر ORM.
- يجعل العلاقة بين الكود والبيانات واضحة وقابلة للصيانة.
12) قاموس المصطلحات
| المصطلح | المعنى المبسط |
|---|---|
| API | النافذة التي يتواصل عبرها النظام مع العالم الخارجي |
| JSON | صيغة نصية منظمة لتبادل البيانات |
| Endpoint | رابط أو مسار لميزة محددة داخل الـ API |
| Database | الخزنة التي نحفظ فيها المستخدمين والاشتراكات والقواعد |
| Environment Variables | إعدادات وأسرار تحفظ خارج الكود داخل ملف .env |
| Dependency Injection | طريقة NestJS الذكية لتمرير الخدمات بدون تعقيد يدوي |
13) ملخص رحلة البيانات (Data Journey)
هذا القسم يلخص ما يحدث تقنياً منذ لحظة ضغط المستخدم على الزر وحتى ظهور النتيجة أمامه.
14) تفاصيل إضافية مهمة لفهم المشروع بعمق
أ) ما الذي يجعل هذا المشروع احترافيًا؟
- الفصل الواضح بين الطبقات: Controller / Service / Entity / Guard.
- إمكانية التوسعة: إضافة TikTok أو Google Ads لاحقاً دون تخريب المعمارية.
- وجود أتمتة وتنبيهات، وليس مجرد عرض للبيانات.
- إدارة اشتراكات فعلية، ما يجعله SaaS قابلاً للبيع.
ب) ما الذي يمكنك تحسينه لاحقاً؟
تحسينات هندسية
- إضافة Redis للـ Cache وتقليل استدعاءات Meta.
- استخدام Queue jobs للمهام الثقيلة.
- إضافة Logger احترافي مثل Winston أو Pino.
- كتابة Unit Tests وIntegration Tests.
تحسينات منتجية
- خطط اشتراك متعددة مع Limits واضحة.
- واجهة Dashboard متقدمة ورسوم بيانية أكثر عمقاً.
- تقارير PDF أو أسبوعية للمستخدمين.
- دعم أكثر من شبكة إعلانية.
ج) لماذا هذا المشروع ممتاز للتعلّم؟
لأنه لا يعلمك فقط كيف تكتب Controller أو Service، بل يعلمك كيف تفكر كنظام: كيف تُدخل البيانات، كيف تنظفها، كيف تحللها، كيف تحميها، كيف تربطها بالدفع، وكيف تجعلها تنتج قيمة تجارية فعلية.
15) الخلاصة العملية
هذا المشروع مثال ممتاز على Backend حقيقي مبني بعقلية منتج SaaS، وليس مجرد تدريب أكاديمي. إذا فهمت هذا المستند جيداً، فأنت لم تتعلم NestJS فقط، بل بدأت تفهم فعلياً كيف تُبنى الأنظمة التي تجمع البيانات، تحللها، تتخذ القرار، وتحول كل ذلك إلى خدمة قابلة للبيع والاشتراك.
تم تنسيق هذه النسخة كصفحة HTML تعليمية أنيقة، باتجاه من اليمين إلى اليسار، وبأقسام واضحة وأمثلة كود داخل صناديق منظمة لسهولة القراءة والمراجعة.