طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 241 · تحليل وتصميم برمجيات الويب
الدرس 13 · الوحدة 6: تحديد هيكلية البرمجيات والمعمارية

معمارية تطبيقات الويب: الطبقات و MVC

كيف تقسم تطبيق الويب إلى طبقات ومسؤوليات واضحة يسهل بناؤها وصيانتها؟ ⏱ نحو 50 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تشرح الطبقات الثلاث ولماذا لا يتصل المتصفح بقاعدة البيانات.
  • تميّز مسؤوليات النموذج والعرض والمتحكم.
  • تقرأ تطبيق MVC متعدد الملفات وتكمل متحكمًا فيه.
  • تقابل MVC في Laravel و Django، وتحلل بنية تطبيق موجود.

١العميل والخادم والطبقات الثلاث

هيكلية البرمجيات تعني كيف نقسم النظام إلى أجزاء، وما مسؤولية كل جزء، وكيف تتواصل. أشهر تقسيم لتطبيقات الويب الطبقات الثلاث: عرض في المتصفح، ومنطق عمل في الخادم، وبيانات في قاعدة البيانات. لا تتخطى طبقة جارتها: المتصفح لا يتصل بقاعدة البيانات مباشرة أبدًا.

طبقة البيانات

طبقة منطق العمل

طبقة العرض

طلب HTTPS

HTML أو JSON

استعلام SQL

صفوف

المتصفح أو تطبيق الجوال

الخادم: القواعد والصلاحيات

قاعدة البيانات

الطبقات الثلاث: كل طبقة تتصل بجارتها فقط، والصلاحيات تُفرض في الطبقة الوسطى.

٢نمط MVC

داخل الخادم نحتاج تنظيمًا أيضًا. نمط MVC (Model-View-Controller) يفصل الكود إلى ثلاث مسؤوليات، وهو النمط الذي يطلب مشروع الخطة بناء تطبيق به:

الجزءمسؤوليتهلا يفعل
النموذج Modelالبيانات وقواعد العمل: الحفظ، والقراءة، والحساب (seatsLeft)لا يعرف شيئًا عن HTML أو الطلبات
العرض Viewتحويل البيانات إلى ما يراه المستخدم (HTML)لا يقرأ من قاعدة البيانات ولا يتخذ قرارات
المتحكم Controllerيستقبل الطلب، ويسأل النموذج، ويختار العرض والردلا يكتب SQL ولا HTML بنفسه
diagram.mmd
sequenceDiagram  actor U as المستخدم  participant R as المسارات Routes  participant C as المتحكم Controller  participant M as النموذج Model  participant V as العرض View  U->>R: GET /courses/1  R->>C: show(id = 1)  C->>M: findById(1)  M-->>C: كائن الدورة  C->>V: detail(course)  V-->>C: صفحة HTML  C-->>U: 200 + الصفحة
العرض Viewالنموذج Modelالمتحكم Controllerالمسارات RoutesالمستخدمGET /courses/1show(id = 1)findById(1)كائن الدورةdetail(course)صفحة HTML200 + الصفحة

تتبّع الطلب: المسار يوجّه إلى المتحكم، والمتحكم يسأل النموذج، ثم يعطي البيانات للعرض، ثم يرد. كل جزء لا يعرف إلا جاره.

٣MVC يعمل: خادم دورات

هذا تطبيق صغير مقسّم بنمط MVC في أربعة ملفات. افتح كل ملف، ولاحظ أن لكل منها مسؤولية واحدة، ثم انظر الطلبات الثلاثة:

const express = require('express');const app = express();const ctrl = require('./controllers/courseController'); app.get('/courses', ctrl.list);app.get('/courses/:id', ctrl.show); app.listen(3000);

الطلب الثالث لدورة غير موجودة: النموذج أعاد لا شيء، فاختار المتحكم عرض «غير موجودة» مع الرمز 404. لو أردنا تغيير شكل الصفحة نعدّل views فقط، ولو انتقلت البيانات إلى MySQL نعدّل models فقط.

٤MVC في Laravel و Django

الأطر الكبيرة تفرض هذا التقسيم بأسماء مجلدات ثابتة، فيعرف أي مطور أين يجد كل شيء. هذا الكود نفسه في Laravel:

// routes/web.phpRoute::get('/courses/{id}', [CourseController::class, 'show']); // app/Http/Controllers/CourseController.phppublic function show($id) {    $course = Course::findOrFail($id);          // Model    return view('courses.show', [                // View        'course' => $course,        'left'   => $course->seatsLeft(),    ]);}
المسؤوليةExpress (درسنا)LaravelDjango
المساراتserver.js / routesroutes/web.phpurls.py
المتحكمcontrollers/app/Http/Controllersviews.py (يسميه Django «view»)
النموذجmodels/app/Models (Eloquent)models.py
العرضviews/resources/views (Blade)templates/ (Django يسمي النمط MTV)

٥دراسة حالة: تحليل بنية تطبيق موجود

تطلب الخطة «تحليل تطبيق ويب موجود لفهم بنيته وتحديد نقاط الضعف». عند فتح مشروع لم تكتبه، اسأل هذه الأسئلة:

السؤالعلامة ضعف إن وجدت
أين تُكتب استعلامات قاعدة البيانات؟SQL داخل ملفات العرض أو المتحكمات ← لا يوجد نموذج حقيقي
ما حجم المتحكمات؟متحكم بمئات الأسطر فيه حسابات وقواعد ← «متحكم سمين»؛ انقل القواعد إلى النموذج
هل في العرض قرارات؟شروط صلاحيات أو حسابات داخل HTML ← تتكرر وتُنسى
أين تُفحص الصلاحيات؟في الواجهة فقط ← ثغرة؛ يجب أن تكون في الخادم
هل يتكرر الكود نفسه؟نفس الاستعلام في عشرة أماكن ← تعديل واحد يحتاج عشرة

توقّع وتحقّق

أين يُحسب عدد المقاعد المتاحة في MVC؟

قاعدة عمل على البيانات.

ما مسؤولية المتحكم؟

في Django، ملف views.py يقابل في MVC:

Django يسمي النمط MTV.

وجدت استعلامات SQL داخل ملفات HTML في تطبيق تحلله. ما الاستنتاج؟

الخلاصة

تحدٍّ برمجي: أكمل المتحكم

متوسط

تطبيق «دورات» مقسّم بنمط MVC، لكن دالة show في المتحكم لم تُكتب بعد. أكملها دون أن تكسر فصل المسؤوليات.

المطلوب:
  • GET /courses/1 يعرض اسم الدورة والمقاعد المتاحة بالرمز 200.
  • دورة غير موجودة ← 404 بعرض «غير موجودة».
  • المتحكم يستخدم Course.findById و view.detail؛ ولا يكتب HTML ولا بيانات بنفسه.
👀 اعرض الاستجابات المطلوبة
✓ التحقق الآلي: يشغّل المختبر خادمك من جديد ويرسل إليه هذه الطلبات بالترتيب ثم يفحص الردود
  1. GET /courses/1
  2. GET /courses/2
  3. GET /courses/9
  • الخادم يعمل ويرد على كل الطلبات بلا أخطاء غير معالَجة
  • الدورة 1 ← 200
  • تظهر «تصميم الويب» والمقاعد المتاحة 2
  • الدورة 2 مقاعدها 0
  • دورة غير موجودة ← 404
  • … بعرض «غير موجودة»
  • المتحكم يستخدم Course.findById
  • المتحكم يستخدم view.detail
  • لا HTML داخل المتحكم

💡 تلميح 1
المعرّف في الرابط نص؛ حوّله: Number(req.params.id).
💡 تلميح 2
انظر views/courseView.js: فيه detail(c, left) و notFound() جاهزتان.
💡 تلميح 3
المقاعد المتاحة من النموذج: Course.seatsLeft(c).

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

const express = require('express');const app = express();const ctrl = require('./controllers/courseController'); app.get('/courses', ctrl.list);app.get('/courses/:id', ctrl.show); app.listen(3000);
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس