Rejoice Software logo

Laravel · · قراءة 4 دقائق

Laravel متعدد المستأجرين: قاعدة بيانات واحدة أم عدة قواعد في الإنتاج

جرّبنا الخيارين. إليك نقاط الفشل والمفاضلات ومسارات الترحيل التي تمنّينا لو عرفناها من اليوم الأول.

طريقتان لفصل العملاء

في التطبيق متعدد المستأجرين يتشارك جميع العملاء (المستأجرين) الكود نفسه، لكن يجب ألا يرى أيٌّ منهم إلا بياناته. في Laravel هناك طريقتان رئيسيتان لتحقيق ذلك.

في نموذج قاعدة البيانات الواحدة، يحتوي كل جدول لبيانات العملاء على عمود tenant_id، ويُقيَّد كل استعلام بالمستأجر الحالي. وفي نموذج قواعد البيانات المتعددة، يحصل كل مستأجر على قاعدة بيانات (أو مخطط) خاصة، ويبدّل التطبيق الاتصال بمجرد معرفة المستأجر النشط. كلاهما يعمل جيدًا في الإنتاج، لكنهما يفشلان بطرق مختلفة.

قاعدة بيانات واحدة: نقاط القوة والمخاطر

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

الخطر الأساسي هو تسرب البيانات. استعلام واحد ينسى تقييد المستأجر — استعلام خام أو تقرير أو مهمة تعمل خارج الطلب — قد يعيد بيانات عميل آخر. ومن المخاطر الأخرى الجيران المزعجون (مستأجر كبير يبطئ الجميع) والاستعادة: إرجاع بيانات مستأجر واحد من نسخة احتياطية مشتركة أمر مرهق.

قواعد بيانات متعددة: نقاط القوة والمخاطر

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

التكلفة تشغيلية. يُنفَّذ كل ترحيل مرة لكل مستأجر، فتطول عمليات النشر، وقد يترك فشلٌ في المنتصف المستأجرين على إصدارات مخطط مختلفة. تصبح حدود الاتصالات قيدًا حقيقيًا، وتحتاج التحليلات عبر المستأجرين إلى خط إضافي نحو مستودع بيانات.

تطبيق قاعدة البيانات الواحدة بشكل صحيح في Laravel

  • حدّد المستأجر مرة واحدة ومبكرًا — في وسيط (middleware) من النطاق أو النطاق الفرعي أو المستخدم المسجّل — واحفظه في مكان واحد.
  • طبّق نطاقًا عامًا (global scope) واضبط tenant_id تلقائيًا عند إنشاء النماذج عبر trait مشترك.
  • ابدأ الفهارس المركبة بـ tenant_id وأدرجه في قيود التفرّد (مثل tenant_id مع email).
  • مرّر معرّف المستأجر إلى كل مهمة في الطابور واستعده قبل تنفيذها.
  • أضف المستأجر كبادئة لمفاتيح التخزين المؤقت ومسارات تخزين الملفات.
  • اكتب اختبارات تنشئ مستأجرَين وتتأكد أن أحدهما لا يستطيع أبدًا قراءة سجلات الآخر.
trait BelongsToTenant
{
    protected static function bootBelongsToTenant(): void
    {
        static::addGlobalScope('tenant', fn ($query) => $query->where('tenant_id', tenant()->id));

        static::creating(fn ($model) => $model->tenant_id ??= tenant()->id);
    }
}

تطبيق قواعد البيانات المتعددة بشكل صحيح

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

نفّذ ترحيلات المستأجرين بأمر يمر على جميع المستأجرين ويسجل التقدم ويستطيع الاستئناف بعد الفشل. راقب حد الاتصالات في خادم قاعدة البيانات، واستخدم مجمّع اتصالات إذا كان لديك عمال كثيرون. حزم مثل stancl/tenancy وspatie/laravel-multitenancy تتولى كثيرًا من ذلك وتدعم النموذجين.

النموذج الهجين

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

كيف تقرر

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

هل تريد أن نلقي نظرة على موقعك؟

شغّل فاحص المواقع المجاني للحصول على تقرير فوري، أو أنشئ تذكرة وسيرد مهندس خلال يوم عمل واحد.

#Laravel #SaaS #Multi-tenancy

لنتحدث

لنبنِ شيئاً استثنائياً.

مكالمة استراتيجية مجانية ٣٠ دقيقة. بدون عرض مبيعات ولا التزام — مجرد خطة.

ابدأ مشروعاً