كل المقالات
معمارية

تصميم معمارية SaaS متعددة المستأجرين: عزل البيانات والاختيار الصحيح

ثلاثة أنماط لعزل المستأجرين — قاعدة مشتركة مع RLS، مخطط لكل مستأجر، قاعدة لكل مستأجر — ومتى تختار كلًّا منها، مع مثال Postgres RLS.

PB
PhiBit
فاي · phi-bit.com

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

الأنماط الثلاثة لعزل المستأجرين

تتوزّع معظم تصاميم SaaS على طيف بين العزل المنطقي الكامل (مشاركة كل شيء) والعزل المادي الكامل (فصل كل شيء):

  • قاعدة بيانات مشتركة + Row-Level Security (RLS): جميع المستأجرين في نفس الجداول، ويميّزهم عمود tenant_id، ويُفرض العزل على مستوى الصف داخل قاعدة البيانات نفسها. الأرخص والأسهل تشغيلًا، والأنسب للبدايات و SaaS واسع النطاق بعدد مستأجرين كبير.
  • مخطط لكل مستأجر (Schema-per-tenant): قاعدة بيانات واحدة لكنها مقسّمة إلى schema منفصل لكل مستأجر. عزل منطقي أوضح وأسهل في النسخ الاحتياطي الانتقائي، لكنه يصبح ثقيلًا على الـ migrations عند تجاوز مئات المستأجرين.
  • قاعدة بيانات لكل مستأجر (DB-per-tenant): عزل مادي كامل، الأعلى أمانًا والأنسب لمتطلبات إقامة البيانات (data residency) والعملاء المؤسسيين، لكنه الأغلى تشغيليًا والأصعب توسّعًا أفقيًا.

المشكلات الجوهرية: الجار المزعج وإقامة البيانات

في النمط المشترك، يظهر تحدّي الجار المزعج (noisy neighbor): مستأجر كثيف الاستخدام يلتهم موارد قاعدة البيانات فيتأثّر الباقون. تُعالَج بحصص للموارد، وفهرسة جيّدة على tenant_id، وأحيانًا فصل المستأجرين الكبار إلى قواعد خاصة بهم. أمّا إقامة البيانات فهي إلزام قانوني في بعض الدول والقطاعات بأن تبقى بيانات العميل ضمن نطاق جغرافي محدّد — وهنا يصبح فصل القواعد جغرافيًا (أو مخطط/قاعدة لكل منطقة) ضرورة لا خيارًا.

مثال: فرض RLS في Postgres

في النمط المشترك، الخطأ الأمني الأشيع هو الاعتماد على شرط WHERE tenant_id في كود التطبيق فقط — فأي استعلام يُنسى فيه الشرط يسرّب بيانات مستأجر إلى آخر. الحل الصحيح هو فرض العزل داخل قاعدة البيانات عبر Row-Level Security، بحيث يستحيل تجاوزه من طبقة التطبيق:

-- Enable row-level security on the tenant-scoped table
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
ALTER TABLE invoices FORCE ROW LEVEL SECURITY;

-- Policy: a row is only visible when its tenant_id
-- matches the tenant set for the current session
CREATE POLICY tenant_isolation ON invoices
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

-- The application sets this per request, inside the
-- transaction, after authenticating the tenant:
--   SET LOCAL app.tenant_id = '...uuid...';
-- Now every query is automatically scoped to that tenant,
-- even if the WHERE clause forgets it.

كيف تختار؟

  1. ابدأ بالقاعدة المشتركة + RLS ما لم يوجد سبب قوي للعكس — فهي الأقل كلفة والأسرع للوصول إلى السوق.
  2. انتقل إلى مخطط/قاعدة لكل مستأجر عندما يفرض عميل مؤسسي أو منظّم متطلبات عزل مادي أو إقامة بيانات صريحة.
  3. اعتمد نموذجًا هجينًا واقعيًا: الأغلبية على قاعدة مشتركة، والعملاء الكبار أو المنظَّمون على قواعد مخصّصة. هذا ما نفضّله في PhiBit لأنه يوازن الكلفة مع متطلبات المؤسسات.

تبني منتج SaaS وتتردّد في اختيار نموذج العزل المناسب لحالتك؟ راسلنا على [email protected] ويسعدنا مناقشة الخيارات بناءً على متطلباتك التنظيمية وحجمك المتوقّع.

جاهز لتُحوّل فكرتك إلى منتج؟

احصل على عرض سعر تفصيلي خلال 24 ساعة. استشارة أولى مجانية بدون التزام.

PhiBit Ltd