تصل كل شركة نامية إلى اللحظة التي يقاومها فيها CRM. مسار المبيعات لا يطابق المراحل التي يستخدمها الفريق فعلاً، وفريق التسليم يريد حقولاً لا يقدّمها المورّد، والمالية تصدّر إلى جدول بيانات على أي حال، وفاتورة المقعد تواصل الارتفاع مقابل ميزات لا يفتحها أحد. وصلنا إلى تلك اللحظة بأنفسنا، وبنينا Steppe Dev Box ليحل محله، ونستخدم الأساس نفسه حين يطلب العملاء نظام CRM أو مكتباً خلفياً أو أدوات عمليات. يشرح هذا المقال المعمارية والمفاضلات بصراحة، بما في ذلك متى لا ينبغي أن تبنوا نظامكم الخاص.
متى يكون بناء CRM فكرة خاطئة
نبدأ بالجزء غير المريح. يكون CRM المخصص استثماراً سيئاً في الحالات التالية:
- تكون عمليتكم معيارية (عميل محتمل → مؤهَّل → عرض → فوز) ويكفيها SaaS بتهيئة خفيفة.
- تحتاجون منظومة تكاملات واسعة منذ اليوم الأول — تسلسل البريد، والاتصال الهاتفي، وأتمتة التسويق.
- لن يملك أحد في الفريق المنتج بعد الإطلاق. الأدوات الداخلية بلا مالك تتآكل بسرعة.
ويكون استثماراً جيداً حين تكون العملية هي الميزة التنافسية، أو حين يجب أن تبقى البيانات داخل بنيتكم، أو حين تحتاج عدة فرق إلى عروض مختلفة للكيانات نفسها، أو حين تتجاوز فاتورة SaaS بالفعل ما سيكلفه بناء مركّز على مدى سنتين أو ثلاث. بالنسبة إلينا كان السببان الثاني والثالث: احتاجت المبيعات والتسليم والمالية جميعاً إلى رؤية المشاريع والأشخاص والأموال، كل من زاوية مختلفة، وأردنا مصدر حقيقة واحداً.
الفكرة الأساسية: كل شيء وحدة
القرار التصميمي الذي جعل النظام قابلاً للصيانة هو غياب أي مفهوم مثبّت في الكود عن «عميل محتمل» أو «صفقة». هناك نظام وحدات، والوحدة حزمة تصرّح بما يلي:
- مخطط كيان — حقول بأنواع وتحقق وتلميحات عرض؛
- عروض — لوحة kanban، وجدول، وصفحة تفاصيل، كل منها مهيّأ لا مكتوب كوداً؛
- علاقات بوحدات أخرى (ينتمي المشروع إلى عميل؛ وتنتمي المهمة إلى مشروع)؛
- صلاحيات — من يستطيع القراءة والإنشاء والتعديل والحذف، وصولاً إلى مستوى الحقل؛
- خطافات اختيارية — دوال على الخادم تعمل عند الإنشاء أو التحديث أو تغيّر المرحلة.
مسار المبيعات، والمشاريع، والعملاء، والفواتير، والمرشحون، وحتى الأصول الداخلية: كلها وحدات. إضافة وحدة جديدة تعني كتابة تعريف للمخطط، وخطافاً إن لزم. تعرض النواة العروض، وتفرض الصلاحيات، وتحفظ البيانات، وتبث التغييرات. عملياً، تستغرق الوحدة الجديدة نحو يوم من مهندس واحد، وهذا المقياس الذي يهمنا أكثر من سواه.
تحت الغطاء تُقابَل كل وحدة بنموذج Prisma على PostgreSQL. فكّرنا في مخزن ديناميكي بالكامل بلا مخطط، ورفضناه: الجداول المنمّطة، والترحيلات، والمفاتيح الأجنبية الحقيقية تستحق الكلفة الصغيرة لتوليد ترحيل حين تتغيّر وحدة. ولا تُستخدم أعمدة JSON المرنة إلا للسمات الديناميكية حقاً.
فوري افتراضياً
حين يستخدم CRM عدة أشخاص في الوقت نفسه، يظهر نمط إخفاق محدد: يسحب شخصان البطاقة نفسها، فيخسر أحدهما، ولا يلاحظ أحد حتى يتصل العميل. قررنا مبكراً أن يُبث كل تعديل، وأن يعكسه فوراً كل عرض مفتوح.
الآلية بسيطة عمداً:
- تمر كل عمليات الكتابة عبر الخادم (معالجات المسارات وإجراءات الخادم في Next.js)، وهو يتحقق وفق مخطط الوحدة والصلاحيات، ويكتب في PostgreSQL ضمن معاملة، ويعيد السجل المعياري.
- بعد الالتزام، ينشر الخادم حدث تغيّر —
{ module, id, type: "created" | "updated" | "deleted", version }— إلى قناة. - توزّع بوابة WebSocket الأحداث على العملاء المتصلين، بعد تصفيتها وفق ما يُسمح لكل عميل برؤيته.
- يحتفظ العميل بذاكرة تخزين مؤقت موحَّدة لكل وحدة. عند وصول حدث، إما يرقّع السجل في مكانه، أو يعيد جلبه إذا دلّ رقم الإصدار على أنه فاته شيء.
يعتمد السحب والإفلات على تحديثات تفاؤلية مع تراجع: تتحرك البطاقة فوراً، ويؤكد الخادم أو يرفض، وعند الرفض تعود البطاقة إلى موضعها مع رسالة. تُحَل التعارضات بأرقام الإصدار — تُرفض الكتابة القديمة ويُحدَّث العميل — وهذا بسيط، وقد كفى لفريق بحجمنا. التحرير التعاوني الحقيقي لحقول النص الطويلة يحتاج إلى CRDT؛ ولم نحتج إليه.
الواجهة: سريعة للاستخدام اليومي
تُستخدم الأدوات الداخلية ساعات كل يوم، لذا سرعة التفاعل أهم من الزخرفة البصرية. الأنماط التي أثبتت جدواها:
- مسارات kanban بالسحب والإفلات عرضاً أساسياً لكل ما له مراحل، مع عرض الجدول نفسه للعمليات المجمّعة.
- تحرير في السطر على الجداول وصفحات التفاصيل — انقروا خلية، وغيّروها، وانتقلوا إلى التالية بمفتاح Tab — مع تحقق مأخوذ من مخطط الوحدة.
- تنقل بلوحة المفاتيح ولوحة الأوامر للتنقل بين السجلات وتنفيذ الإجراءات.
- مرشحات وعروض محفوظة لكل مستخدم، فيصل كل دور إلى شريحة البيانات التي يحتاجها.
- عرض من الخادم للتحميل الأول، ثم تنقل على العميل ببيانات مخزَّنة مؤقتاً، فيبقى التطبيق قابلاً للاستخدام حتى على اتصال بطيء.
نستخدم Next.js وTypeScript عبر المكدس، مع Tailwind CSS للتنسيق؛ والأنواع المشتركة بين مخطط Prisma والخادم والعميل سبب كبير في أن نظام الوحدات آمن للتوسيع بسرعة.
صلاحيات تصل إلى مستوى الحقل
نادراً ما تنفصل الأدوار في CRM انفصالاً واضحاً. ينبغي لمندوب المبيعات أن يرى قيم الصفقات لا الرواتب؛ وينبغي للمتعاقد أن يرى مهامه لا ميزانية العميل. نفّذنا الصلاحيات على ثلاثة مستويات — الوحدة، والسجل، والحقل — تُقيَّم على الخادم عند كل قراءة وكتابة. تُولَّد العروض من مجموعة الحقول المسموح بها، فلا يرى المستخدم عموداً لا يستطيع قراءته، وتطبّق بوابة WebSocket القواعد نفسها قبل البث، فلا يُرسَل حقل ممنوع عبر الاتصال.
هذا عمل أكثر من تقسيم بسيط بين مدير ومستخدم. وهو أيضاً الميزة التي أتاحت لنا فتح النظام للشركاء والمتعاقدين من دون بناء بوابة منفصلة.
كم يكلف التشغيل
الأرقام التي يسأل عنها الناس:
- البنية التحتية: نسخة PostgreSQL مُدارة، وعملية Node.js صغيرة لبوابة WebSocket، وتطبيق Next.js على Vercel. لشركة بحجمنا، الفاتورة الشهرية مقاربة لبضع مقاعد SaaS.
- الصيانة: بضع ساعات في الشهر لتحديثات التبعيات وتغييرات المخطط الصغيرة. الوحدات الجديدة هي العمل المستمر الأهم، وتقودها احتياجات العمل لا الصيانة الدورية.
- البناء الأولي: نظام بنواة الوحدات، والمزامنة الفورية، وثلاث وحدات أو أربع، والصلاحيات، مشروع من 6–10 أسابيع لفريق صغير. قارنوا ذلك بكلفة SaaS على مدى ثلاث سنوات بحسب عدد مقاعدكم قبل أن تقرروا.
إعادة استخدام الأساس مع العملاء
لأن نظام الوحدات عام، أصبح Steppe Dev Box نقطة انطلاق لمشاريع العملاء لا منتجاً لمرة واحدة: مكاتب خلفية للأسواق، وبوابات شركاء، ولوحات عمليات. تبقى النواة — الوحدات، والعروض، والصلاحيات، والمزامنة الفورية — وتُفصَّل الوحدات وفق عملية العميل. وهذه أيضاً نصيحتنا الصريحة إن كنتم تقيّمون CRM مخصصاً: لا تبنوا CRM، بل ابنوا نظام وحدات، ثم صِفوا CRM الخاص بكم داخله.