تخطَّ إلى المحتوى
العودة إلى المدونة

هندسة البرمجيات

وقت القراءة
11 دقيقة قراءة
تاريخ النشر
آخر تحديث

مقال من برمج تك

تطبيق Clean Architecture وDDD عملياً في Laravel

هندسة برمج تكالفريق الهندسيآخر مراجعة للمحتوى: ٢٠ تموز ٢٠٢٦
تطبيق Clean Architecture وDDD عملياً في Laravel

يتناول هذا المقال «تطبيق Clean Architecture وDDD عملياً في Laravel» ضمن محور «Clean Architecture وDDD في Laravel | منهج تطبيقي»، بلغة تشغيلية يمكن لفريق العمل تطبيقها مباشرة.

تطبيق العمارة النظيفة (Clean Architecture) والتصميم الموجّه بالمجال (DDD) في مشروع Laravel لا يُقاس بعدد المجلدات أو بوجود كلمة Domain في المسار. تظهر القيمة عندما يستطيع الفريق قراءة قاعدة العمل واختبارها وتغيير واجهة أو مزود خارجي من دون إعادة كتابة القرار التجاري. لا يستبعد هذا النهج Laravel من المشروع؛ بل يضع أدوات الإطار في طبقة التكامل والعرض، ويحافظ على وضوح قواعد العمل. وفي المشروع البسيط قد يكون التصميم الأقل طبقات هو الأوضح.

ابدأ من سبب التغيير

قبل نقل الملفات، راقب أين يتكرر الألم. هل تتغير قواعد التسعير في عدة Controllers؟ هل يصعب اختبار إصدار فاتورة من دون قاعدة وHTTP؟ هل يستحيل تبديل مزود لأن أنواعه منتشرة في كل مكان؟ المعمارية تجيب عن ضغوط تغيير حقيقية. إعادة هيكلة نظام مستقر إلى نموذج نظري بلا مشكلة محددة قد تزيد التكلفة ولا تحسن النتيجة.

اكتب قائمة بالقرارات التي تتغير لأسباب مختلفة: قاعدة العمل تتغير بطلب صاحب المجال، وواجهة الويب تتغير بتجربة المستخدم، والمزود يتغير بعقد خارجي، والتخزين يتغير بأسباب تشغيلية. الحدود الجيدة تمنع سبباً واحداً من سحب البقية معه.

ابنِ لغة مشتركة مع أصحاب المجال

في DDD تبدأ النمذجة من اللغة. إذا كان صاحب العمل يقول «زيارة» و«مطالبة» و«اعتماد»، فلا تسمِّ كل شيء Record وStatus. الاسم الدقيق يوضح السلوك ويكشف الاختلافات. قد تعني كلمة «إلغاء» في قسم حذف حجز، وفي المالية إنشاء قيد مقابل؛ جمعهما تحت دالة عامة يخفي قرارين.

أنشئ قاموساً صغيراً للمصطلحات الملتبسة، واستخدمه في المحادثة والكود والاختبارات. عندما يتغير الفهم، صحح الاسم. اللغة المشتركة لا تعني ترجمة كل كلمة إلى Class؛ تعني أن الكود المهم يروي العملية نفسها التي يشرحها الخبير.

اكتشف الحدود قبل إنشاء الوحدات

Bounded Context هو حد لمعنى النموذج، لا مجلد تقني. قد تكون «الفوترة» و«الجدولة» سياقين لأن معنى العميل والحالة والقواعد مختلف. ارسم الأحداث والمعلومات التي تعبر بينهما. لا تجعل Model واحداً يحمل كل حقول المؤسسة لأن الجداول مترابطة.

ابدأ بحدود داخل تطبيق واحد عندما يكفي ذلك. Modular monolith يمنح فصل المعاني من دون تعقيد الشبكة والنشر الموزع. الخدمات المصغرة قرار تشغيلي أيضاً؛ لا تستخدمها لعلاج أسماء غير واضحة داخل قاعدة واحدة.

اجعل حالة الاستخدام وحدة التغيير

حالة الاستخدام تحمل نية مثل RescheduleAppointment أو IssueInvoice، وتنسق التحقق التجاري والمعاملة والأحداث. Controller يوثق المدخلات على مستوى النقل، يستدعي الحالة، ثم يحول النتيجة إلى استجابة. Command أو Job يمكنه استدعاء الحالة نفسها من دون تكرار القاعدة.

لا تجمع وظائف متباعدة في خدمة عامة باسم Helpers أو Manager. أعطِ كل خدمة مدخلاً ونتيجة ومسؤولية مفهومة. وميّز التحقق من صيغة البيانات، مثل إلزامية الحقل، عن قاعدة المجال مثل «لا يمكن اعتماد فاتورة ملغاة». الأول يجري عند استقبال الطلب، والثانية تبقى مع النموذج الذي يحميها.

استخدم كائنات القيمة حين تحمي المعنى

قيمة المال ليست رقماً فقط؛ تحمل عملة وقواعد تقريب. فترة الموعد لها بداية ونهاية وعلاقة صحيحة. معرف المستأجر لا ينبغي الخلط بينه وبين معرف المستخدم. Value Object مفيد عندما يمنع حالة غير صالحة أو يجمع سلوكاً يتكرر.

لا تحول كل string إلى كائن لمجرد النقاء. أنشئ النوع عندما يزيل فئة أخطاء أو يوضح عقداً. اجعله غير قابل للتغيير قدر الإمكان، واختبر مساواته وتسلسله عند عبور الحواف.

ضع المعاملات حول ثابت المجال

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

عند وجود أثر خارجي، لا تدع معاملة قاعدة البيانات تتظاهر بأنها تتحكم في API بعيد. استخدم نمطاً مثل outbox أو حدثاً مسجلاً بعد التغيير، ثم عالج الإرسال بشكل idempotent. صمم التعويض والفشل؛ المعاملة الموزعة السحرية ليست افتراضاً آمناً.

اعزل المزوّد خلف عقد يملكه التطبيق

يجب أن تعبّر الواجهة عن حاجة المجال، مثل CollectPayment أو SubmitInvoice، لا أن تنسخ حزمة المزود البرمجية (SDK). ينفذ المهايئ (Adapter) هذا العقد ويترجم الحالات والأخطاء، فتبقى أنواع الطرف الخارجي داخل طبقة التكامل ويصبح اختبار القاعدة بمحاكي صغير ممكناً.

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

دع Eloquent يؤدي دوره المناسب

يمكن استخدام Eloquent عند البنية أو حتى داخل وحدة بسيطة إذا بقيت القواعد واضحة. المشكلة ليست Active Record بذاته؛ المشكلة أن يصبح Model ضخماً يجمع التحقق والتكامل والتنسيق والعرض. استخدم casts وrelationships وscopes بعناية، وانقل حالة الاستخدام المتشابكة إلى مكان يحمل اسماً واختباراً.

Repository لا يجب أن يعيد بناء Eloquent كله. إذا كان المطلوب استعلامات تطبيقية محددة أو عزل تخزين مجال معقد، أنشئ عقداً صغيراً. أما find, save, delete العام فوق كل Model فقد يضيف طبقة بلا معنى.

صمّم الأخطاء كجزء من العقد

فرق بين خطأ مجال متوقع، وفشل مزود قابل لإعادة المحاولة، وعيب برمجي. حالة «الموعد لم يعد متاحاً» نتيجة يمكن للواجهة عرض بديل لها. Timeout عند المزود يحتاج سياسة ومحاولة أو انتظاراً. Null غير متوقع يحتاج مراقبة وتحقيقاً. جمعها في Exception عامة يضعف السلوك.

تجنب تسريب رسالة SDK أو SQL للمستخدم. ترجم الفشل عند الحد، واحتفظ بالسياق الفني في السجل وفق سياسة الخصوصية. يجب أن تستطيع الاختبارات تأكيد النتيجة التجارية لا نص الخطأ الداخلي.

اختبر الهرم الذي تملكه

اختبر Value Objects وقواعد المجال وحالات الاستخدام سريعاً من دون HTTP حيث يمكن. استخدم Feature tests للتوجيه والمصادقة والتفويض وقاعدة البيانات. واكتب contract tests لكل Adapter مهم مقابل محاكاة أو بيئة مزود معتمدة. لا تعِد اختبار Laravel نفسه، لكن اختبر تهيئتك وحدودك.

ابنِ fixtures بلغة المجال، لا arrays غامضة. مثال الاختبار يجب أن يشرح لماذا رفضت العملية. عند اكتشاف عيب، أضف اختباراً في أقرب طبقة تمنع تكراره.

قِس التعقيد واحذف ما لا يخدم

راجع وقت الوصول إلى الكود وعدد القفزات لفهم حالة بسيطة. إذا كانت إضافة حقل تمر عبر ست طبقات لا تحمي أي قاعدة، فقد أفرط التصميم. وإذا كانت كل القواعد في Controller واحد، فالنقص واضح أيضاً. استخدم ADR قصيراً لتوثيق قرار كبير وموعد مراجعته.

لا تحتاج شاشة CRUD إدارية إلى Aggregate وDomain Event وRepository إن لم توجد قاعدة. ابدأ مباشراً، واستخرج الحد عندما تظهر لغة أو تغير أو خطر. البساطة المتعمدة ليست ضد DDD؛ هي تطبيق لمبدأ أن النموذج يتبع المجال.

أعد الهيكلة على شرائح

لا توقف المنتج لإعادة كتابة كاملة. اختر مساراً مؤلماً، غطّ سلوكه الحالي باختبارات، استخرج حالة استخدام، ثم اعزل مزوداً أو قاعدة واحدة. استخدم Strangler pattern داخل التطبيق إذا لزم، وقس أثر التغيير على زمن التسليم والأخطاء والفهم.

يمكن أن يساعد منهج بناء الأنظمة في برمج تك في ربط الاكتشاف بالتسليم، بينما تعرض خدمة الاستشارات المعمارية نطاقاً لمراجعة نظام قائم من دون افتراض إعادة كتابة.

الخلاصة: احمِ القرار الذي يتغير

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

أسئلة شائعة

لا. معناها حماية قواعد العمل من تفاصيل الإطار والمزوّدين بأقل تعقيد يحقق ذلك.

المصادر

#Laravel #المعمارية

سياسة التحرير في برمج تك

اقرأ أيضاً

مقالات ذات صلة

  1. 01

    هندسة البرمجيات / 12 دقيقة قراءة

    Flutter وLaravel: خلفية واحدة لتطبيقات iOS وAndroid

    Flutter وLaravel: خلفية واحدة لتطبيقات
    غلاف: Flutter وLaravel: خلفية واحدة لتطبيقات
  2. 02

    هندسة البرمجيات / 12 دقيقة قراءة

    أنماط PWA تعمل دون إنترنت للأسواق ذات الشبكة المتغيرة

    أنماط PWA تعمل دون إنترنت
  3. 03

    هندسة البرمجيات / 12 دقيقة قراءة

    بناء Design System يبدأ بـ RTL للمنتجات العربية الثنائية

    بناء Design System يبدأ بـ

هل تبني نظاماً خاصاً لعملك؟

بعد قراءة «تطبيق Clean Architecture وDDD»، أرسل لنا وصفاً للمشروع والمستخدمين والتكاملات المطلوبة، وسنرد بخطوة عملية خلال يوم عمل.