من التحليل إلى تصميم النظام

التحليل إلى وحدات ومكونات

18 دقيقة الدرس 3 من 10

التحليل إلى وحدات ومكونات

بمجرد وضع الهيكل المعماري، يواجه المحلل وفريق التصميم سؤالاً جوهرياً: أين بالضبط ترسم الحدود؟ يُعدّ التحليل إلى أجزاء — أي فن تقسيم النظام إلى وحدات أصغر وأكثر قابلية للإدارة — المحدد الرئيسي لسهولة البناء والاختبار والصيانة على مر السنين. تحكم هذا القرار قوتان: التماسك (مدى انتماء العناصر داخل الوحدة الواحدة لبعضها) والاقتران (مدى الارتباط الوثيق بين وحدتين مختلفتين).

التماسك: مدى تلاحم الوحدة الداخلي

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

  • العشوائي — تُجمَّع العناصر بشكل اعتباطي دون رابط منطقي (مثل فئة "أدوات مساعدة" تحتوي على توليد ملفات PDF وإرسال بريد إلكتروني وتحويل عملات). تجنبه كلياً.
  • المنطقي — تؤدي العناصر عمليات متشابهة لكنها تُختار بناءً على راية تحكم (مثل دالة واحدة تتعامل مع create وupdate وdelete عبر معامل). أفضل من العشوائي لكنه لا يزال هشاً.
  • الزمني — تُنفَّذ العناصر في الوقت ذاته (كجمع كل مهام بدء التشغيل في وحدة واحدة). مقبول فقط لإجراءات التهيئة.
  • الإجرائي — تتبع العناصر تسلسلاً من الخطوات (مثل validateInput → formatData → saveRecord في وحدة واحدة). شائع لكنه يخلط المسؤوليات.
  • التواصلي — تعمل العناصر على مجموعة البيانات ذاتها (كل ما يتعلق بكيان Order). جيد: الوحدات المحورية للبيانات متماسكة بطبيعتها.
  • التسلسلي — يُغذّي خرج عنصر ما مدخل العنصر التالي مباشرةً (أسلوب خط الأنابيب). تماسك ممتاز.
  • الوظيفي — يُسهم كل عنصر في غرض واحد محدد بوضوح (مثل وحدة مهمتها الوحيدة حساب تكلفة الشحن). هذا هو الهدف المنشود.
اختبار عملي: هل يمكنك وصف الوحدة بجملة قصيرة واحدة دون استخدام كلمة "و"؟ إذا كان الجواب نعم، فالتماسك على الأرجح مرتفع. "تتولى تسجيل العملاء" — جيد. "تتولى تسجيل العملاء وتوليد الفواتير وإرسال رسائل البريد الإلكتروني" — قسّمها.

الاقتران: مدى الترابط بين وحدتين

يقيس الاقتران درجة الاعتمادية المتبادلة بين الوحدات. الاقتران الشديد يعني أن أي تغيير في وحدة ما سيفرض تغييرات في وحدة أخرى — ظاهرة "تأثير التموج" التي تجعل الأنظمة هشة. وللاقتران أيضاً طيف من الأسوأ إلى الأفضل:

  • اقتران المحتوى — تقرأ وحدة ما الحالة الداخلية لوحدة أخرى أو تعدّلها مباشرةً. كارثي ولا يُقبل قط.
  • الاقتران العام — تتشارك وحدات متعددة متغيراً عاماً. هش؛ تجنبه في الكود الإنتاجي.
  • اقتران التحكم — تمرر وحدة ما راية تتحكم في منطق وحدة أخرى. إشارة إلى ضرورة التقسيم.
  • الاقتران بالطوابع — تتشارك الوحدات بنية بيانات مركّبة لكنها تستخدم جزءاً منها فقط (إرسال كائن Customer كامل في حين أن البريد الإلكتروني فقط هو المطلوب).
  • الاقتران بالبيانات — تتواصل الوحدات فقط عبر معاملات بسيطة محددة بوضوح. هذا هو الهدف: كل وحدة لا تعرف شيئاً عن الآلية الداخلية للأخرى.
مبدأ التصميم: اسعَ دائماً إلى تماسك مرتفع واقتران منخفض. يتعزز هذان الهدفان ببعضهما — فالوحدة ذات الغرض الواحد الواضح تمتلك بطبيعتها أسباباً أقل للاعتماد على وحدات أخرى.

رسم حدود المكونات عملياً

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

  • PatientRegistration — إنشاء ملفات المرضى وإدارتها.
  • AppointmentScheduler — التحقق من التوافر، الحجز، إعادة الجدولة، الإلغاء.
  • BillingEngine — حساب الرسوم، إصدار الفواتير، معالجة المدفوعات.
  • NotificationService — إرسال تأكيدات وتذكيرات عبر الرسائل القصيرة والبريد الإلكتروني.

تكشف كل مكوّن عن واجهة ضيقة وصريحة للمكونات الأخرى. لا يرسل AppointmentScheduler الرسائل مباشرةً — بل يستدعي NotificationService.sendConfirmation(appointmentId) ويترك لذلك المكوّن تحديد القناة والقالب والتوصيل. هذا هو الاقتران بالبيانات في أحسن تجلياته.

Clinic Booking System — Component Boundary Diagram Clinic Booking System — Component Decomposition PatientRegistration createProfile() getPatientId() AppointmentScheduler checkAvailability() bookAppointment() BillingEngine calculateFee() issueInvoice() NotificationService sendConfirmation() sendReminder() Patient & Schedule Database (shared data store) patientId apptId apptId, fee جميع الاستدعاءات بين المكونات تمرر معرّفات بسيطة فقط (اقتران بالبيانات). لا مكوّن يصل إلى التفاصيل الداخلية لمكوّن آخر.
الشكل 1 — أربعة مكونات متماسكة ذات واجهات مقترنة بالبيانات في نظام حجز العيادة.

الاقتران والتماسك في مراجعة تصميم حقيقية

تخيّل متجراً إلكترونياً. تضمّنت المسودة الأولى لمطور البرمجيات وحدة OrderManager واحدة تتولى التحقق من صحة السلة، وحجز المخزون، وشحن البطاقة، وإرسال رسالة تأكيد، وتسجيل أحداث التحليل. كل المسؤوليات مجمّعة معاً. تظهر مشكلة الاقتران فوراً: إذا غيّر مزود الدفع واجهته البرمجية، وجب على المطور فتح OrderManager مخاطراً بإحداث خطأ في منطق البريد الإلكتروني غير ذي الصلة. يرصد المحلل هذا باستخدام اختبار الجملة الواحدة ويوصي بتقسيم الوحدة. يتحوّل التصميم النهائي إلى أربعة مكونات: CartValidator، وInventoryReserver، وPaymentProcessor، وOrderNotifier، تتواصل جميعها فقط عبر معرّف الطلب orderId.

High Cohesion vs Low Cohesion — Before and After Cohesion Comparison — Online Store Order Flow BEFORE (Low Cohesion) OrderManager validateCart() reserveStock() chargeCard() sendConfirmation() logAnalytics() 5 unrelated responsibilities One change breaks everything AFTER (High Cohesion) CartValidator validate() InventoryReserver reserve() PaymentProcessor charge() OrderNotifier sendConfirmation() Each module does one thing Changes are isolated & safe
الشكل 2 — إعادة هيكلة وحدة ضخمة منخفضة التماسك إلى أربعة مكونات عالية التماسك.

تحديد الواجهات بين المكونات

رسم الحدود وحده لا معنى له دون تحديد ما يعبرها. يجب أن يقترن كل حد بين مكوّنين بـعقد واجهة صريح يصف ما يقبله المكوّن وما يُعيده. في هذه المرحلة من التحليل لا يحتاج العقد أن يكون كوداً — يكفي وصف منظّم:

Component: AppointmentScheduler Operation: bookAppointment(patientId: Integer, slotId: Integer): AppointmentId Pre-conditions: slotId exists and is marked Available; patientId is a registered patient Post-conditions: Slot status set to Booked; Appointment record created; returns new appointmentId Errors: SlotNotAvailableException, PatientNotFoundException

هذه المواصفة محايدة تقنياً — تنطبق سواء كان التطبيق خدمة Java أو دالة Python أو نقطة REST. يكتبها المحلل ويلتزم بها المطور. هذا الفصل بين ماذا وكيف هو جوهر التفكير القائم على المكونات.

خطأ شائع — قاعدة البيانات المشتركة كنقطة اقتران خفية: مكوّنان يُفترض أنهما مستقلان لكنهما يقرآن ويكتبان في جداول قاعدة البيانات ذاتها دون واجهة صريحة، هما في الحقيقة مترابطان ترابطاً وثيقاً من خلال مخطط قاعدة البيانات. أي تعديل في المخطط يصبح حدثاً يطال النظام بأكمله. كلما أمكن، يجب أن تمتلك كل مكوّن بياناتها الخاصة وتكشف عنها فقط عبر واجهتها. حين تكون المخزن المشترك أمراً لا مفر منه (كما في مثال العيادة)، وثّقه بوضوح وعامِل تعديلات المخطط باعتبارها عقوداً مشتركة بين المكونات.

من مخطط الفئات إلى حدود المكونات

أنت تعرف بالفعل كيف ترسم مخطط الفئات. يستخدم تحليل المكونات ذلك المخطط مدخلاً. اجمع الفئات التي تتعاون بكثافة (عدد تفاعل عالٍ على مخطط الفئات) في المكوّن ذاته. ينبغي أن تتفاعل الفئات التي تتقاطع عبر حدود المكونات المستقبلية فقط عبر ارتباطات محددة بوضوح، ويُفضّل أن تكون بمضاعفة محدودة وبإتجاهية واضحة. في نموذج مجال شركة لوجستية مثلاً، قد يظهر أن Route وStop وVehicle مترابطة بشكل وثيق — تنتمي إلى مكوّن RouteManagement. أما Invoice وPaymentRecord فينتميان إلى مكوّن Billing منفصل. يحتاج مخطط الطرق فقط أن يعرف التكلفة النهائية، لا الطريقة التي يحسبها بها مكوّن الفوترة.

المخرج في هذه المرحلة: مخطط مكونات (UML أو صناديق وأسهم غير رسمية) يُظهر كل مكوّن صندوقاً مسمّى، والواجهات التي يوفرها (بتدوين المصاصة الصغيرة أو سهم مسمّى)، والواجهات التي يطلبها من الآخرين. يصبح هذا المخطط قسماً رئيسياً في وثيقة مواصفات التصميم التي ستكتبها في الدرس السابع.

التماسك والاقتران ليسا مُثُلاً مجردة — بل قرارات تصميمية لها تكاليف مباشرة. تكلف الأنظمة ذات التماسك المنخفض والاقتران العالي عادةً ضعفاً إلى خمسة أضعاف تكلفة الصيانة مقارنةً بالبدائل المحللة جيداً. دورك كمحلل هو إظهار تلك القرارات بصورة صريحة وقابلة للتتبع قبل كتابة سطر كود واحد.