كتبنا الكود البرمجي لتطبيق "مرتكز" من الصفر!

الحمد لله الذي علَّم بالقلم، علَّم الإنسان ما لم يعلم، والصلاة والسلام على خير معلِّم الناس الخير سيدنا محمد - صلَّى الله عليه وسلم … أما بعد ،،
عندما بدأنا العمل على منصة "مرتكز"، كان الهدف واضحًا: إطلاق نسخة أولية بسيطة بأقل قدر ممكن من الخصائص الأساسية لقياس مدى تفاعل المستخدمين مع الفكرة.
وبفضل الله، جاءت النتائج مشجعة بشكل كبير، مما دفعنا لاتخاذ خطوة مهمة وهي إعادة كتابة الكود البرمجي بشكل منظّم وواضح، ليكون قابلًا للتوسع وإضافة ميزات جديدة بسهولة وثقة.
في هذا المقال، أشارك معك أبرز التحسينات التقنية التي قمنا بتطبيقها، إلى جانب تجربتنا الشخصية وأهم الدوافع وراء هذه التغييرات، آملين أن يكون ذلك مرجعًا نافعًا أو مصدر إلهام لك خلال عملك على مشاريعك الخاصة.
تنظيم المنطق عبر Services
إن فصل المنطق في ملفات خدمات مستقلة (Services) هو نمط شائع يُستخدم في تطوير التطبيقات الحديثة، ويُعرف باسم Service Layer Pattern.
هذا النمط يهدف إلى "فصل مسؤوليات التطبيق" بحيث لا تبقى العمليات المعقدة أو المتكررة مدمجة داخل مكونات الواجهة أو المعالجات (Controllers)، بل تُوضع في وحدات خارجية قابلة لإعادة الاستخدام والاختبار.
كمثال للتوضيح، قد تكون هناك عمليات متكررة مثل التحقق من جلسة المستخدم، أو تحميل البيانات من قاعدة البيانات، أو معالجة المدخلات.
بدلاً من كتابة هذه العمليات مباشرة في كل صفحة أو نقطة نهاية (API route)، يتم نقلها إلى ملفات مستقلة داخل مجلد services/ مثل الكود التالي:
// user.service.ts
export const getUserById = async (id: string) => {
return await UserModel.findById(id).lean()
}
تطبيق هذا الأسلوب جعل كود المشروع أكثر وضوحًا وقابلية للتوسع.
فعند إضافة ميزات جديدة أو تعديل سلوك معين، لم نعد بحاجة للبحث داخل ملفات متعددة، بل أصبحت معظم المهام المركزية منظمة داخل خدمات، مما وفّر علينا الوقت وقلل من فرص الوقوع في أخطاء ناتجة عن التكرار أو التداخل.
الاعتماد الكامل على Typescript
إن JavaScript تسمح بمرونة كبيرة في التعامل مع القيم والأنواع، لكنها لا تُنبهك عند تمرير متغير خاطئ (مثل إرسال نص بدل رقم).
أما Typescript، فيفرض عليك تحديد نوع كل متغير أو كائن أو دالة، ويُظهر خطأ في حال التناقض.
بالتالي عند الاعتماد الكامل على Typescript في المشروع، فإننا نضيف طبقة تحقق صارمة للأنواع (Static Type Checking)، وهي ميزة رئيسية تساعد على كشف الأخطاء مبكرًا أثناء كتابة الكود، قبل حتى أن يُشغّل التطبيق.
إن تبني Typescript بالكامل في "مرتكز" قد ساعدنا على التحكم الأفضل في تدفق البيانات بين الواجهة الأمامية والخلفية، والتأكد من صحة القيم المستخدمة في كل خطوة.
هذا الأمر مكننا من إجراء تحسينات عميقة دون القلق من إتلاف شيء في مكان آخر، وقلل من زمن تتبع المشكلات في مرحلة التطوير.
وضوح واستقرار باستخدام Enums
في كثير من الحالات نحتاج إلى تمثيل مجموعة محددة من الخيارات، مثل أدوار المستخدمين (admin, user) أو حالة المشروع (pending, approved, rejected).
وعند استخدام النصوص مباشرة، يمكن أن تحدث أخطاء بسبب اختلاف بسيط في الكتابة أو الاستخدام.
والحل هو استخدام Enums في Typescript، فهو وسيلة لتعريف مجموعة من القيم الثابتة والمتوقعة بشكل واضح ومقروء، مما يعزز من وضوح الكود ويقلل من الأخطاء الناتجة عن القيم النصية العشوائية أو المتكررة.
كمثال توضيحي لتطبيق ذلك علي أدوار المستخدمين:
// roles.enum.ts
export enum UserRole {
ADMIN = 'admin',
USER = 'user',
}
ثم نستخدمه داخل الكود بالشكل التالي:
function checkPermission(role: UserRole) {
if (role === UserRole.ADMIN) {
// allow access
}
}
بهذا الشكل، إذا تم بالخطأ تمرير قيمة غير معرّفة داخل الـ enum، سينبّهك Typescript بوجود خطأ قبل تشغيل التطبيق.
في "مرتكز"، استخدمنا Enums لتعريف أدوار المستخدمين، وأنواع التفاعل مع المشاريع، وحالات النشر وغيرها.
هذا وفّر وضوحًا كبيرًا أثناء التطوير، وقلّل من احتمالات الخطأ عند إرسال البيانات، كما جعل الكود أكثر مرونة في التوسع مستقبلًا دون الحاجة لمراجعة كل العناصر التي تستخدم هذه القيم.
استخدام Interfaces لتعزيز هيكلة البيانات
إن استخدام Interfaces في Typescript يعني تحديد شكل وهيكل البيانات بشكل واضح ومسبق.
فهي بمثابة "عقد أو ميثاق" يحدد ما يجب أن تحتويه الكائنات من خصائص، وما هي أنواع هذه الخصائص.
هذا يُساعد المطور على معرفة ما هو متوقع من البيانات، ويمنع تمرير كائنات غير مكتملة أو تحتوي على خصائص غير صحيحة.
لنقل أنك تتعامل مع كائن "مستخدم"، وتحتاج إلى التأكد أن كل جزء من التطبيق يعرف بالضبط ما هي خصائص هذا الكائن.
// user.interface.ts
export interface IUser {
_id: string
firstName: string
lastName: string
email: string
role: UserRole
}
الآن عند استخدام هذا interface في أي مكان داخل المشروع، فإن Typescript سيتحقق تلقائيًا من التزام الكائنات بهذا الشكل، وإذا نسيت خاصية أو كتبت نوعًا غير صحيح، سيظهر لك خطأ أثناء التطوير.
وفي "مرتكز"، ساعدنا استخدام الـ Interfaces في توحيد هيكل البيانات.
فعلى سبيل المثال، كل ما يتعلق بالمستخدمين أو المشاريع صار له شكل معروف وثابت، مما سهّل كتابة الكود، وتسريع اكتشاف الأخطاء، وأتاح لنا التركيز على منطق التطبيق بدلًا من تصحيح البيانات.
إدارة الأخطاء بشكل مركزي
في التطبيقات التقليدية، قد تضطر إلى استخدام try/catch في كل وظيفة أو معالجة كل خطأ بطريقة مختلفة، مما يسبب فوضى وصعوبة في التتبع.
لكن مع الإدارة المركزية، يتم تعريف middle ware أو interceptor مسؤول عن استقبال الأخطاء من مختلف أنحاء المشروع، والتصرف بناءً على نوعها.
إن إدارة الأخطاء بشكل مركزي تعني أن يكون هناك مكان واحد مخصص للتعامل مع جميع الأخطاء التي قد تحدث أثناء تشغيل التطبيق، سواء في الواجهة الأمامية أو على مستوى الخادم (Back-end).
بدلاً من تكرار نفس الكود في كل وظيفة أو صفحة للتعامل مع الخطأ، يتم توجيه الخطأ إلى نقطة مركزية تقوم بتسجيله، تحليله، وإظهار رسالة مناسبة للمستخدم.
وفي "مرتكز"، ساعدنا تطبيق إدارة الأخطاء مركزيًا على تحسين الاستجابة للأعطال بشكل واضح ومنظم.
ولم يعد هناك حاجة لتكرار التعامل مع الأخطاء في كل صفحة، بل أصبحت جميع أنواع الأخطاء تُجمع وتُحلل في مكان واحد، مما وفّر وقتًا كبيرًا في التطوير، وساعدني على تقديم تجربة أكثر احترافية واستقرارًا للمستخدم النهائي.
إعداد قائمة اختبار بسيطة Smoke Tests
تُعد اختبارات Smoke Tests وسيلة بسيطة وفعّالة لضمان أن الوظائف الأساسية للتطبيق ما تزال تعمل بشكل سليم بعد كل تحديث أو تعديل.
لا تهدف هذه الاختبارات إلى تغطية كل التفاصيل، بل تركز على النقاط الحيوية مثل تحميل الصفحة الرئيسية، تسجيل الدخول، أو عرض المحتوى، مما يساعد على اكتشاف الأعطال المبكرة ويوفّر الكثير من الوقت والجهد في المراحل المتقدمة.
في منصة "مرتكز"، اعتمدنا على قائمة تحقق بسيطة نقوم بمراجعتها بعد كل عملية نشر.
ورغم بساطتها، كانت كافية لمنحنا الثقة في أن التعديلات لم تؤثر سلبًا على التجربة الأساسية للمستخدم.
ربما لاحقًا - بإذن الله - نعمل على بناء نظام اختبارات تلقائي أكثر تطورًا، لكن في الوقت الحالي، هذا الأسلوب البسيط يؤدّي الغرض بكفاءة ويمنحنا أرضية مطمئنة لمواصلة التطوير بثبات.
الخاتمة
رغم أن هذه التغييرات التقنية قد لا تكون ظاهرة بوضوح لمستخدمي منصة "مرتكز"، إلا أنها كانت ضرورية لوضع أساس متين يساعدنا علي التوسع بثقة، وتقديم تجربة أكثر استقرارًا وقابلية للتطوير في المستقبل.
إن "مرتكز" ليس مجرد مشروع تقني، بل رحلة نعمل عليها بشغف، نتعلم منها ونتطور من خلالها، ونسعى أن تكون إضافة حقيقية للمحتوى العربي والمجتمع التقني في منطقتنا.
وفقنا الله وإياك إلى ما يحبه ويرضاه ،،